14 Commits

7 changed files with 481 additions and 146 deletions

View File

@@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<AssemblyVersion>0.0.0.4</AssemblyVersion> <AssemblyVersion>0.0.0.9</AssemblyVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using Jellyfin.Plugin.MediaCleaner.Configuration;
using Jellyfin.Plugin.MediaCleaner.Models;
using Jellyfin.Plugin.MediaCleaner.ScheduledTasks;
using MediaBrowser.Controller.Entities;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.MediaCleaner.Helpers;
public class LoggingHelper
{
private readonly ILogger _logger;
public LoggingHelper(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
private static PluginConfiguration Configuration =>
Plugin.Instance!.Configuration;
public void StartLogging()
{
if (Configuration.DebugMode)
{
_logger.LogInformation("--DEBUG MODE ACTIVE--");
}
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Starting stale media scan...");
}
public void EndLogging()
{
_logger.LogInformation("Ending stale media scan...");
_logger.LogInformation("-------------------------------------------------");
}
public void PrintDebugEndOfScanningForSeries(BaseItem item)
{
if (Configuration.DebugMode)
{
_logger.LogInformation("End of scanning for series: {Series}", item);
_logger.LogInformation("-------------------------------------------------");
}
}
public void StartScanningSeriesItems()
{
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Starting scan of series items.");
_logger.LogInformation("-------------------------------------------------");
}
public void StartScanningMoviesItems()
{
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Starting scan of movies items.");
_logger.LogInformation("-------------------------------------------------");
}
public void PrintItemsInformation(IReadOnlyCollection<BaseItem> items)
{
ArgumentNullException.ThrowIfNull(items);
_logger.LogInformation("Total items: {ItemCount}", items.Count);
_logger.LogInformation("Stale items found: {AllItems}", items);
}
public void PrintStaleMoviesInformation(IReadOnlyCollection<BaseItem> staleMovies)
{
ArgumentNullException.ThrowIfNull(staleMovies);
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Stale Movies found: {StaleMovies}", staleMovies.Count);
if (staleMovies.Count > 0 && Configuration.DebugMode)
{
foreach (var movieInfo in staleMovies)
{
_logger.LogInformation("Movie Info: ID: {Id} | Movie Name: {MovieName}", [movieInfo.Id, movieInfo.Name]);
}
}
}
public void PrintStaleEpisodesInformation(Func<IReadOnlyCollection<BaseItem>, List<SeriesInfo>> findSeriesInfoFromEpisodes, IReadOnlyCollection<BaseItem> staleEpisodes)
{
ArgumentNullException.ThrowIfNull(staleEpisodes);
ArgumentNullException.ThrowIfNull(findSeriesInfoFromEpisodes);
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Stale Episodes found: {StaleEpisodes}", staleEpisodes.Count);
if (staleEpisodes.Count > 0 && Configuration.DebugMode)
{
if (findSeriesInfoFromEpisodes == null)
{
throw new ArgumentNullException(nameof(findSeriesInfoFromEpisodes), "The method to find series information cannot be null.");
}
List<SeriesInfo> seriesInfoList = findSeriesInfoFromEpisodes(staleEpisodes);
foreach (var seriesInfo in seriesInfoList)
{
_logger.LogInformation("Series Info: ID: {Id} | Series Name: {SeriesName} | Stale Seasons: {Seasons}", [seriesInfo.Id, seriesInfo.SeriesName, string.Join(", ", seriesInfo.Seasons)]);
}
}
_logger.LogInformation("-------------------------------------------------");
}
public void PrintDebugDataForSeries(BaseItem item)
{
ArgumentNullException.ThrowIfNull(item);
if (Configuration.DebugMode)
{
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Debug data for series: {SeriesName}", item.Name);
_logger.LogInformation("-------------------------------------------------");
}
}
public void PrintDebugSeasonInfo()
{
if (Configuration.DebugMode)
{
_logger.LogInformation("Season debug information:");
}
}
public void PrintDebugSeasonCreatedOutsideCutoff()
{
if(Configuration.DebugMode)
{
_logger.LogInformation("All episodes were created outside of media cutoff, season is possibly stale.");
}
}
public void PrintDebugEpisodesWithUserData(IReadOnlyCollection<BaseItem> episodesWithUserData)
{
if(Configuration.DebugMode){
_logger.LogInformation("Episodes with user data: {EpisodesWithUserData}", episodesWithUserData);
_logger.LogInformation("-------------------------------------------------");
}
}
public void PrintDebugNoUserDataAndOutsideCutoffEpisodeInfo(IReadOnlyCollection<BaseItem> episodes)
{
ArgumentNullException.ThrowIfNull(episodes);
if(Configuration.DebugMode){
_logger.LogInformation("No user data, and creation date is outside of media cutoff, Season is stale.");
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Episode creation dates:");
_logger.LogInformation("-------------------------------------------------");
foreach(BaseItem episode in episodes)
{
_logger.LogInformation("Episode: {EpisodeName} | Date Created: {EpisodeDateCreated}", [episode.Name, episode.DateCreated]);
}
_logger.LogInformation("-------------------------------------------------");
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Linq;
using System.Threading;
using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Plugin.MediaCleaner.Configuration;
using MediaBrowser.Controller.Entities;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.MediaCleaner.Helpers;
public class MovieHelper
{
private readonly ILogger _logger;
public MovieHelper(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
private static PluginConfiguration Configuration =>
Plugin.Instance!.Configuration;
public bool IsMovieStale(BaseItem movie)
{
if (Configuration.DebugMode)
{
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("Start of scanning for movie: {Movie}", movie);
_logger.LogInformation("-------------------------------------------------");
}
bool movieIsStale = false;
bool createdOutsideCutoff = movie.DateCreated < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff);
bool hasUserData = movie.UserData.Where(data => data.LastPlayedDate != null).ToList().Count > 0;
if (hasUserData)
{
var mostRecentUserData = movie.UserData.OrderByDescending(data => data.LastPlayedDate).First(data => data.LastPlayedDate != null);
if (Configuration.DebugMode){
_logger.LogInformation("Most recent user data: {Movie}", movie);
foreach (var property in typeof(UserData).GetProperties())
{
_logger.LogInformation("{PropertyName}: {PropertyValue}", property.Name, property.GetValue(mostRecentUserData));
}
_logger.LogInformation("-------------------------------------------------");
}
if (mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff))
{
if (Configuration.DebugMode)
{
_logger.LogInformation("Most recent user data last played date is outside of cutoff. Adding {Movie} to stale movies.", movie);
}
movieIsStale = true;
}
}
else if (createdOutsideCutoff)
{
if (Configuration.DebugMode)
{
_logger.LogInformation("Movie has no user data and was created outside of cutoff: {DateCreated}. Adding {Movie} to stale movies.", [movie.DateCreated, movie]);
}
movieIsStale = true;
}
if (Configuration.DebugMode)
{
_logger.LogInformation("-------------------------------------------------");
_logger.LogInformation("End of scanning for movie: {Movie}", movie);
_logger.LogInformation("-------------------------------------------------");
}
return movieIsStale;
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Database.Implementations.Entities.Libraries;
using Jellyfin.Plugin.MediaCleaner.Configuration;
using MediaBrowser.Controller.Entities;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.MediaCleaner.Helpers;
public class SeriesHelper
{
private readonly ILogger _logger;
private readonly LoggingHelper _loggingHelper;
public SeriesHelper(ILogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_loggingHelper = new LoggingHelper(logger);
}
private static PluginConfiguration Configuration =>
Plugin.Instance!.Configuration;
public bool IsSeasonUserDataStale(IReadOnlyList<BaseItem> episodes)
{
bool seasonIsStale = false;
List<BaseItem> staleEpisodes = [];
var episodesWithUserData = episodes.Where(episode => episode.UserData.Where(data => data.LastPlayedDate != null).ToList().Count > 0).ToList();
_loggingHelper.PrintDebugEpisodesWithUserData(episodesWithUserData);
foreach (var episode in episodesWithUserData)
{
bool episodeIsStale = false;
var mostRecentUserData = episode.UserData.OrderByDescending(data => data.LastPlayedDate).First(data => data.LastPlayedDate != null);
var staleLastPlayedDate = mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff);
var staleCreationDate = episode.DateCreated < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff);
if(Configuration.DebugMode){
_logger.LogInformation("User data for episode: {Episode}", episode);
_logger.LogInformation("-------------------------------------------------");
foreach (var property in typeof(UserData).GetProperties())
{
_logger.LogInformation("{PropertyName}: {PropertyValue}", property.Name, property.GetValue(mostRecentUserData));
}
_logger.LogInformation("-------------------------------------------------");
}
if (staleLastPlayedDate && staleCreationDate)
{
episodeIsStale = true;
if(Configuration.DebugMode){
_logger.LogInformation("Most recent user data has a last played date of: {LastPlayedDate}.", [mostRecentUserData.LastPlayedDate]);
_logger.LogInformation("And episode created {DateCreated}.", episode.DateCreated);
}
}
if (episodeIsStale)
{
staleEpisodes.Add(episode);
if(Configuration.DebugMode){
_logger.LogInformation("Episode is stale.");
}
}
}
if(staleEpisodes.Count == episodes.Count)
{
seasonIsStale = true;
_logger.LogInformation("Stale episodes count matches all episode count. Season is stale.");
_logger.LogInformation("-------------------------------------------------");
}
return seasonIsStale;
}
}

View File

@@ -0,0 +1,70 @@
#pragma warning disable RS0030 // Do not use banned APIs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Database.Implementations;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.MediaCleaner.ScheduledTasks;
/// <summary>
/// Task to clean up any detached userdata from the database.
/// </summary>
public class CleanupUserDataTask : IScheduledTask
{
private readonly ILocalizationManager _localization;
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
private readonly ILogger<CleanupUserDataTask> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CleanupUserDataTask"/> class.
/// </summary>
/// <param name="localization">The localisation Provider.</param>
/// <param name="dbProvider">The DB context factory.</param>
/// <param name="logger">A logger.</param>
public CleanupUserDataTask(ILocalizationManager localization, IDbContextFactory<JellyfinDbContext> dbProvider, ILogger<CleanupUserDataTask> logger)
{
_localization = localization;
_dbProvider = dbProvider;
_logger = logger;
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("CleanupUserDataTask");
public Guid PlaceholderId => new("00000000-0000-0000-0000-000000000001");
/// <inheritdoc />
public string Description => _localization.GetLocalizedString("CleanupUserDataTaskDescription");
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("Media");
/// <inheritdoc />
public string Key => nameof(CleanupUserDataTask);
/// <inheritdoc/>
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using (dbContext.ConfigureAwait(false))
{
var detachedUserData = dbContext.UserData.Where(e => e.ItemId == PlaceholderId);
_logger.LogInformation("Deleting {DetachedUserDataCount} detached UserData entries.", detachedUserData.Count());
await detachedUserData.ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false);
}
}
/// <inheritdoc/>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
yield break;
}
}

View File

@@ -5,6 +5,7 @@ using System.ComponentModel;
using System.Data.Common; using System.Data.Common;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -12,6 +13,7 @@ using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations.Entities; using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Database.Implementations.Entities.Libraries; using Jellyfin.Database.Implementations.Entities.Libraries;
using Jellyfin.Plugin.MediaCleaner.Configuration; using Jellyfin.Plugin.MediaCleaner.Configuration;
using Jellyfin.Plugin.MediaCleaner.Helpers;
using Jellyfin.Plugin.MediaCleaner.Models; using Jellyfin.Plugin.MediaCleaner.Models;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@@ -26,20 +28,23 @@ namespace Jellyfin.Plugin.MediaCleaner.ScheduledTasks;
public sealed class StaleMediaTask : IScheduledTask public sealed class StaleMediaTask : IScheduledTask
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly LoggingHelper _loggingHelper;
private readonly MovieHelper _movieHelper;
private readonly SeriesHelper _seriesHelper;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StaleMediaTask"/> class. /// Initializes a new instance of the <see cref="StaleMediaTask"/> class.
/// </summary> /// </summary>
/// <param name="logger">Logger.</param> /// <param name="logger">Logger for StaleMediaTask.</param>
/// <param name="userManager">User manager.</param> /// <param name="libraryManager">Accesses jellyfin's library manager for media.</param>
/// <param name="libraryManager">.</param> public StaleMediaTask(ILogger<StaleMediaTask> logger, ILibraryManager libraryManager)
public StaleMediaTask(ILogger<StaleMediaTask> logger, IUserManager userManager, ILibraryManager libraryManager)
{ {
_logger = logger; _logger = logger;
_userManager = userManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_loggingHelper = new LoggingHelper(_logger);
_movieHelper = new MovieHelper(_logger);
_seriesHelper = new SeriesHelper(_logger);
} }
private static PluginConfiguration Configuration => private static PluginConfiguration Configuration =>
@@ -55,44 +60,31 @@ public sealed class StaleMediaTask : IScheduledTask
Task IScheduledTask.ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) Task IScheduledTask.ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{ {
_loggingHelper.StartLogging();
var query = new InternalItemsQuery var query = new InternalItemsQuery
{ {
IncludeItemTypes = [BaseItemKind.Movie, BaseItemKind.Series], IncludeItemTypes = [BaseItemKind.Movie, BaseItemKind.Series],
Recursive = true Recursive = true
}; };
List<BaseItem> allItems = [.. _libraryManager.GetItemsResult(query).Items]; List<BaseItem> allItems = [.. _libraryManager.GetItemsResult(query).Items];
if (Configuration.DebugMode) _loggingHelper.PrintItemsInformation(allItems);
{
_logger.LogInformation("Total items found: {AllItems}", allItems);
}
List<BaseItem> series = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Series)]; List<BaseItem> series = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Series)];
List<BaseItem> movies = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Movie)]; List<BaseItem> movies = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Movie)];
_loggingHelper.StartScanningSeriesItems();
List<BaseItem> staleEpisodes = [.. series.SelectMany(GetStaleEpisodes)]; List<BaseItem> staleEpisodes = [.. series.SelectMany(GetStaleEpisodes)];
_loggingHelper.StartScanningMoviesItems();
List<BaseItem> staleMovies = [.. GetStaleMovies(movies)]; List<BaseItem> staleMovies = [.. GetStaleMovies(movies)];
_logger.LogInformation("Stale Movies found: {StaleMovies}", staleMovies.Count); _loggingHelper.PrintStaleMoviesInformation(staleMovies);
if (staleMovies.Count > 0) _loggingHelper.PrintStaleEpisodesInformation(FindSeriesInfoFromEpisodes, staleEpisodes);
{
_logger.LogInformation("Movies: {Names}", string.Join(", ", staleMovies.Select(movie => movie.Name)));
}
_logger.LogInformation("Stale Episodes found: {StaleEpisodes}", staleEpisodes.Count); _loggingHelper.EndLogging();
if (staleEpisodes.Count > 0)
{
// Firstly figure out the seasons, and then the Series to find the name.
List<SeriesInfo> seriesInfoList = FindSeriesInfoFromEpisodes(staleEpisodes);
foreach (var seriesInfo in seriesInfoList)
{
if (Configuration.DebugMode)
{
_logger.LogInformation("Series Info: ID: {Id} | Series Name: {SeriesName} | Stale Seasons: {Seasons}", [seriesInfo.Id, seriesInfo.SeriesName, string.Join(", ", seriesInfo.Seasons)]);
}
}
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -101,46 +93,64 @@ public sealed class StaleMediaTask : IScheduledTask
{ {
List<BaseItem> staleMovies = []; List<BaseItem> staleMovies = [];
foreach (var movie in movies) staleMovies.AddRange(movies.Where(_movieHelper.IsMovieStale));
{
bool movieIsStale = movie.DateCreated < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff);
bool movieHasUserData = movie.UserData.Count > 0;
if (movieHasUserData)
{
if (Configuration.DebugMode){
_logger.LogInformation("Movie has user data: {Movie}", movie);
_logger.LogInformation("-------------------------------------------------");
}
var mostRecentUserData = movie.UserData.OrderByDescending(data => data.LastPlayedDate).First();
if (Configuration.DebugMode){
_logger.LogInformation("Most recent user data: {Movie}", movie);
foreach (var property in typeof(UserData).GetProperties())
{
_logger.LogInformation("{PropertyName}: {PropertyValue}", property.Name, property.GetValue(mostRecentUserData));
}
_logger.LogInformation("-------------------------------------------------");
}
if (mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff))
{
_logger.LogInformation("Most recent user data last played date is outside of cutoff. Adding to stale movies.");
staleMovies.Add(movie);
}
}
else if (movieIsStale)
{
_logger.LogInformation("Movie has no user data and was created outside of cutoff: {DateCreated}", movie.DateCreated);
staleMovies.Add(movie);
}
}
return staleMovies; return staleMovies;
} }
private List<SeriesInfo> FindSeriesInfoFromEpisodes(List<BaseItem> episodes)
private List<BaseItem> GetStaleEpisodes(BaseItem item)
{
List<BaseItem> staleEpisodes = [];
// Gets each season in a show
var seasons = _libraryManager.GetItemList(new InternalItemsQuery
{
ParentId = item.Id,
Recursive = false
});
_loggingHelper.PrintDebugDataForSeries(item);
foreach (var season in seasons)
{
// Gets each episode, to access user data.
var episodes = _libraryManager.GetItemList(new InternalItemsQuery
{
ParentId = season.Id,
Recursive = false
});
bool seasonCreatedOutsideCutoff = episodes.All(episode => episode.DateCreated < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff));
_loggingHelper.PrintDebugSeasonInfo();
if (seasonCreatedOutsideCutoff)
{
_loggingHelper.PrintDebugSeasonCreatedOutsideCutoff();
}
bool seasonHasUserData = episodes.Any(episode => episode.UserData.Count > 0);
bool seasonIsStale = (seasonHasUserData && _seriesHelper.IsSeasonUserDataStale(episodes)) || seasonCreatedOutsideCutoff;
bool noUserDataAndOutsideCutoff = !seasonHasUserData && seasonCreatedOutsideCutoff;
if (seasonIsStale)
{
if (noUserDataAndOutsideCutoff)
{
_loggingHelper.PrintDebugNoUserDataAndOutsideCutoffEpisodeInfo(episodes);
}
staleEpisodes.AddRange(episodes);
}
}
_loggingHelper.PrintDebugEndOfScanningForSeries(item);
return staleEpisodes;
}
private List<SeriesInfo> FindSeriesInfoFromEpisodes(IReadOnlyCollection<BaseItem> episodes)
{ {
Guid[] seasonIds = [.. episodes.Select(episode => episode.ParentId).Distinct()]; Guid[] seasonIds = [.. episodes.Select(episode => episode.ParentId).Distinct()];
@@ -156,7 +166,6 @@ public sealed class StaleMediaTask : IScheduledTask
ItemIds = seriesIds ItemIds = seriesIds
}).ToList(); }).ToList();
// Series Id, Series Name and Stale Seasons
List<string> seriesNames = [.. series.Select(series => series.Name).Distinct()]; List<string> seriesNames = [.. series.Select(series => series.Name).Distinct()];
List<SeriesInfo> seriesInfoList = []; List<SeriesInfo> seriesInfoList = [];
@@ -174,83 +183,6 @@ public sealed class StaleMediaTask : IScheduledTask
return seriesInfoList; return seriesInfoList;
} }
private List<BaseItem> GetStaleEpisodes(BaseItem item)
{
List<BaseItem> staleEpisodes = [];
// Gets each season in a show
var seasons = _libraryManager.GetItemList(new InternalItemsQuery
{
ParentId = item.Id,
Recursive = false
});
foreach (var season in seasons)
{
// Gets each episode, to access user data.
var episodes = _libraryManager.GetItemList(new InternalItemsQuery
{
ParentId = season.Id,
Recursive = false
});
bool seasonHasUserData = episodes.Any(episode => episode.UserData.Count > 0);
if (seasonHasUserData && Configuration.DebugMode)
{
_logger.LogInformation("Season has user data for episodes: {Episodes}", episodes);
_logger.LogInformation("-------------------------------------------------");
}
bool seasonIsStale = episodes.All(episode => episode.DateCreated < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff));
if (seasonIsStale && Configuration.DebugMode)
{
_logger.LogInformation("All episodes are outside media cutoff.");
_logger.LogInformation("-------------------------------------------------");
}
if (seasonHasUserData)
{
var episodesWithUserData = episodes.Where(episode => episode.UserData.Count > 0).ToList();
if(Configuration.DebugMode){
_logger.LogInformation("Episodes with user data: {EpisodesWithUserData}", episodesWithUserData);
_logger.LogInformation("-------------------------------------------------");
}
foreach (var episode in episodesWithUserData)
{
var mostRecentUserData = episode.UserData.OrderByDescending(data => data.LastPlayedDate).First();
if(Configuration.DebugMode){
foreach (var property in typeof(UserData).GetProperties())
{
_logger.LogInformation("{PropertyName}: {PropertyValue}", property.Name, property.GetValue(mostRecentUserData));
}
_logger.LogInformation("-------------------------------------------------");
}
if (mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff))
{
if(Configuration.DebugMode){
_logger.LogInformation("Most Recent User Data Last Played Date is: {LastPlayedDate}. All Episodes are stale.", mostRecentUserData.LastPlayedDate);
_logger.LogInformation("-------------------------------------------------");
}
staleEpisodes.AddRange(episodes);
break;
}
}
}
// Check for episodes that have gone unwatched for stale media cutoff
else if (seasonIsStale)
{
if(Configuration.DebugMode){
_logger.LogInformation("No user data, adding all episodes as it is outside of cutoff.");
_logger.LogInformation("-------------------------------------------------");
}
staleEpisodes.AddRange(episodes);
}
}
return staleEpisodes;
}
IEnumerable<TaskTriggerInfo> IScheduledTask.GetDefaultTriggers() IEnumerable<TaskTriggerInfo> IScheduledTask.GetDefaultTriggers()
{ {
// Run this task every 24 hours // Run this task every 24 hours

View File

@@ -1,9 +1,9 @@
The idea behind this plugin is to have an easy way to run a task to find all movies and shows in your media collection that users haven't viewed in a number of cutoff days. The idea behind this plugin is to have an easy way to run a task to find all movies and shows in your media collection that users haven't viewed in a number of cutoff days.
At the time of writing, the plugin is only capable of logging movies and shows that are stale (Unwatched for 90 days) by running a scheduled task. You will need to view your logs to know the number of stale files. At the time of writing, the plugin is only capable of logging movies and shows that are stale (Unwatched for a user set number of days) by running a scheduled task. You will need to view your logs to know the number of stale files and the names of said files.
Planned features: Planned features:
- Better logging to show more than just the count. - Better logging to show more than just the count.
- A page that shows what media is currently flagged for removal. And a button to confirm removal. - A page that shows what media is currently flagged for removal. And a button to confirm removal.
- Integration with sonarr and radarr apis to delete your media. - Integration with sonarr and radarr apis to delete your media.
- Whitelist for shows to ignore. (Seasonal shows) - Whitelist for shows to ignore. (Seasonal shows)