Clean Cache Task to Remove Unused Files (#195)

This commit is contained in:
rlauuzo 2024-06-15 10:57:20 +02:00 committed by GitHub
parent a9cdaf66b0
commit ddecb15a51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 135 additions and 72 deletions

View File

@ -615,11 +615,6 @@
<input type="checkbox" id="eraseModeCacheCheckbox" style="margin-left: 10px;">
<label for="eraseModeCacheCheckbox" style="margin-left: 5px;">Erase cached fingerprint files</label>
<br />
<button id="btnCleanCache">
Clean cache from unused fingerprints
</button>
</details>
</fieldset>
</div>
@ -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?",

View File

@ -139,19 +139,6 @@ public class SkipIntroController : ControllerBase
return NoContent();
}
/// <summary>
/// Erases all previously cached introduction fingerprints.
/// </summary>
/// <response code="204">Operation successful.</response>
/// <returns>No content.</returns>
[Authorize(Policy = "RequiresElevation")]
[HttpPost("Intros/CleanCache")]
public ActionResult CleanIntroCache()
{
FFmpegWrapper.CleanCacheFiles();
return NoContent();
}
/// <summary>
/// Get all introductions or credits. Only used by the end to end testing script.
/// </summary>

View File

@ -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
}
}
/// <summary>
/// Remove a cached episode fingerprint from disk.
/// </summary>
public static void CleanCacheFiles()
{
// Get valid episode IDs from the dictionaries
HashSet<Guid> validEpisodeIds = new HashSet<Guid>(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);
}
}
}
}
/// <summary>
/// Determines the path an episode should be cached at.
/// This function was created before the unified caching mechanism was introduced (in v0.1.7).

View File

@ -374,6 +374,24 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
SaveTimestamps(mode);
}
internal void CleanTimestamps(HashSet<Guid> validEpisodeIds)
{
var allKeys = new HashSet<Guid>(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);
}
/// <summary>
/// Inject the skip button script into the web interface.
/// </summary>

View File

@ -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;
/// <summary>
/// Analyze all television episodes for introduction sequences.
/// </summary>
public class CleanCacheTask : IScheduledTask
{
private readonly ILogger<CleanCacheTask> _logger;
private readonly ILoggerFactory _loggerFactory;
private readonly ILibraryManager _libraryManager;
/// <summary>
/// Initializes a new instance of the <see cref="CleanCacheTask"/> class.
/// </summary>
/// <param name="loggerFactory">Logger factory.</param>
/// <param name="libraryManager">Library manager.</param>
/// <param name="logger">Logger.</param>
public CleanCacheTask(
ILogger<CleanCacheTask> logger,
ILoggerFactory loggerFactory,
ILibraryManager libraryManager)
{
_logger = logger;
_loggerFactory = loggerFactory;
_libraryManager = libraryManager;
}
/// <summary>
/// Gets the task name.
/// </summary>
public string Name => "Clean Intro Skipper Cache";
/// <summary>
/// Gets the task category.
/// </summary>
public string Category => "Intro Skipper";
/// <summary>
/// Gets the task description.
/// </summary>
public string Description => "Clear Intro Skipper cache of unused files.";
/// <summary>
/// Gets the task key.
/// </summary>
public string Key => "CPBIntroSkipperCleanCache";
/// <summary>
/// Cleans the Intro Skipper cache by removing files that are no longer associated with episodes in the library.
/// </summary>
/// <param name="progress">Task progress.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Task.</returns>
public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
if (_libraryManager is null)
{
throw new InvalidOperationException("Library manager was null");
}
var queueManager = new QueueManager(
_loggerFactory.CreateLogger<QueueManager>(),
_libraryManager);
// Retrieve media items and get valid episode IDs
var queue = queueManager.GetMediaItems();
var validEpisodeIds = new HashSet<Guid>(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<Guid>()
.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;
}
/// <summary>
/// Get task triggers.
/// </summary>
/// <returns>Task triggers.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return Array.Empty<TaskTriggerInfo>();
}
}