From ddecb15a51d9ba42532083e354d7a2d34e503f2c Mon Sep 17 00:00:00 2001 From: rlauuzo <46294892+rlauuzo@users.noreply.github.com> Date: Sat, 15 Jun 2024 10:57:20 +0200 Subject: [PATCH] Clean Cache Task to Remove Unused Files (#195) --- .../Configuration/configPage.html | 29 ----- .../Controllers/SkipIntroController.cs | 13 -- .../FFmpegWrapper.cs | 30 ----- .../Plugin.cs | 18 +++ .../ScheduledTasks/CleanCacheTask.cs | 117 ++++++++++++++++++ 5 files changed, 135 insertions(+), 72 deletions(-) create mode 100644 ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/CleanCacheTask.cs diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html index 8e03f1a..b898079 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html @@ -615,11 +615,6 @@ -
- - @@ -644,7 +639,6 @@ var support = document.querySelector("details#support"); var btnEraseIntroTimestamps = document.querySelector("button#btnEraseIntroTimestamps"); var btnEraseCreditTimestamps = document.querySelector("button#btnEraseCreditTimestamps"); - var btnCleanCache = document.querySelector("button#btnCleanCache"); // all plugin configuration fields that can be get or set with .value (i.e. strings or numbers). var configurationFields = [ @@ -1060,25 +1054,6 @@ }); } - // erase all intro/credits cache - function cleanCache() { - const title = "Confirm Cache erasure"; - const body = "Are you sure you want to erase all unused fingerprints?"; - - Dashboard.confirm( - body, - title, - (result) => { - if (!result) { - return; - } - - fetchWithAuth("Intros/CleanCache", "POST", null); - - Dashboard.alert("Cache cleaned"); - }); - } - document.querySelector('#TemplateConfigPage') .addEventListener('pageshow', function () { Dashboard.showLoadingMsg(); @@ -1136,10 +1111,6 @@ eraseTimestamps("Credits"); e.preventDefault(); }); - btnCleanCache.addEventListener("click", (e) => { - cleanCache(); - e.preventDefault(); - }); btnSeasonEraseTimestamps.addEventListener("click", () => { Dashboard.confirm( "Are you sure you want to erase all timestamps for this season?", diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs index a88e551..9e60310 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs @@ -139,19 +139,6 @@ public class SkipIntroController : ControllerBase return NoContent(); } - /// - /// Erases all previously cached introduction fingerprints. - /// - /// Operation successful. - /// No content. - [Authorize(Policy = "RequiresElevation")] - [HttpPost("Intros/CleanCache")] - public ActionResult CleanIntroCache() - { - FFmpegWrapper.CleanCacheFiles(); - return NoContent(); - } - /// /// Get all introductions or credits. Only used by the end to end testing script. /// diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs index 4926d49..dac33f2 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Linq; using System.Text; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; @@ -641,35 +640,6 @@ public static class FFmpegWrapper } } - /// - /// Remove a cached episode fingerprint from disk. - /// - public static void CleanCacheFiles() - { - // Get valid episode IDs from the dictionaries - HashSet validEpisodeIds = new HashSet(Plugin.Instance!.Intros.Keys.Concat(Plugin.Instance!.Credits.Keys)); // Or use GetMediaItems instead? - - // Delete invalid cache files - foreach (string filePath in Directory.EnumerateFiles(Plugin.Instance!.FingerprintCachePath)) - { - string fileName = Path.GetFileNameWithoutExtension(filePath); - - int dashIndex = fileName.IndexOf('-', StringComparison.Ordinal); // Find the index of the first '-' character - if (dashIndex > 0) - { - fileName = fileName.Substring(0, dashIndex); - } - - if (Guid.TryParse(fileName, out Guid episodeId)) - { - if (!validEpisodeIds.Contains(episodeId)) - { - DeleteEpisodeCache(episodeId); - } - } - } - } - /// /// Determines the path an episode should be cached at. /// This function was created before the unified caching mechanism was introduced (in v0.1.7). diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs index cef2d2e..f2dcdf5 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs @@ -374,6 +374,24 @@ public class Plugin : BasePlugin, IHasWebPages SaveTimestamps(mode); } + internal void CleanTimestamps(HashSet validEpisodeIds) + { + var allKeys = new HashSet(Instance!.Intros.Keys); + allKeys.UnionWith(Instance!.Credits.Keys); + + foreach (var key in allKeys) + { + if (!validEpisodeIds.Contains(key)) + { + Instance!.Intros.TryRemove(key, out _); + Instance!.Credits.TryRemove(key, out _); + } + } + + SaveTimestamps(AnalysisMode.Introduction); + SaveTimestamps(AnalysisMode.Credits); + } + /// /// Inject the skip button script into the web interface. /// diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/CleanCacheTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/CleanCacheTask.cs new file mode 100644 index 0000000..e58e226 --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/CleanCacheTask.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Tasks; +using Microsoft.Extensions.Logging; + +namespace ConfusedPolarBear.Plugin.IntroSkipper; + +/// +/// Analyze all television episodes for introduction sequences. +/// +public class CleanCacheTask : IScheduledTask +{ + private readonly ILogger _logger; + + private readonly ILoggerFactory _loggerFactory; + + private readonly ILibraryManager _libraryManager; + + /// + /// Initializes a new instance of the class. + /// + /// Logger factory. + /// Library manager. + /// Logger. + public CleanCacheTask( + ILogger logger, + ILoggerFactory loggerFactory, + ILibraryManager libraryManager) + { + _logger = logger; + _loggerFactory = loggerFactory; + _libraryManager = libraryManager; + } + + /// + /// Gets the task name. + /// + public string Name => "Clean Intro Skipper Cache"; + + /// + /// Gets the task category. + /// + public string Category => "Intro Skipper"; + + /// + /// Gets the task description. + /// + public string Description => "Clear Intro Skipper cache of unused files."; + + /// + /// Gets the task key. + /// + public string Key => "CPBIntroSkipperCleanCache"; + + /// + /// Cleans the Intro Skipper cache by removing files that are no longer associated with episodes in the library. + /// + /// Task progress. + /// Cancellation token. + /// Task. + public Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) + { + if (_libraryManager is null) + { + throw new InvalidOperationException("Library manager was null"); + } + + var queueManager = new QueueManager( + _loggerFactory.CreateLogger(), + _libraryManager); + + // Retrieve media items and get valid episode IDs + var queue = queueManager.GetMediaItems(); + var validEpisodeIds = new HashSet(queue.Values.SelectMany(episodes => episodes.Select(e => e.EpisodeId))); + + Plugin.Instance!.CleanTimestamps(validEpisodeIds); + + // Identify invalid episode IDs + var invalidEpisodeIds = Directory.EnumerateFiles(Plugin.Instance!.FingerprintCachePath) + .Select(filePath => + { + var fileName = Path.GetFileNameWithoutExtension(filePath); + var episodeIdStr = fileName.Split('-')[0]; + if (Guid.TryParse(episodeIdStr, out Guid episodeId)) + { + return validEpisodeIds.Contains(episodeId) ? (Guid?)null : episodeId; + } + + return null; + }) + .OfType() + .ToHashSet(); + + // Delete cache files for invalid episode IDs + foreach (var episodeId in invalidEpisodeIds) + { + _logger.LogDebug("Deleting cache files for episode ID: {EpisodeId}", episodeId); + FFmpegWrapper.DeleteEpisodeCache(episodeId); + } + + return Task.CompletedTask; + } + + /// + /// Get task triggers. + /// + /// Task triggers. + public IEnumerable GetDefaultTriggers() + { + return Array.Empty(); + } +}