Clean Cache Task to Remove Unused Files (#195)
This commit is contained in:
parent
a9cdaf66b0
commit
ddecb15a51
@ -615,11 +615,6 @@
|
|||||||
|
|
||||||
<input type="checkbox" id="eraseModeCacheCheckbox" style="margin-left: 10px;">
|
<input type="checkbox" id="eraseModeCacheCheckbox" style="margin-left: 10px;">
|
||||||
<label for="eraseModeCacheCheckbox" style="margin-left: 5px;">Erase cached fingerprint files</label>
|
<label for="eraseModeCacheCheckbox" style="margin-left: 5px;">Erase cached fingerprint files</label>
|
||||||
<br />
|
|
||||||
|
|
||||||
<button id="btnCleanCache">
|
|
||||||
Clean cache from unused fingerprints
|
|
||||||
</button>
|
|
||||||
</details>
|
</details>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@ -644,7 +639,6 @@
|
|||||||
var support = document.querySelector("details#support");
|
var support = document.querySelector("details#support");
|
||||||
var btnEraseIntroTimestamps = document.querySelector("button#btnEraseIntroTimestamps");
|
var btnEraseIntroTimestamps = document.querySelector("button#btnEraseIntroTimestamps");
|
||||||
var btnEraseCreditTimestamps = document.querySelector("button#btnEraseCreditTimestamps");
|
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).
|
// all plugin configuration fields that can be get or set with .value (i.e. strings or numbers).
|
||||||
var configurationFields = [
|
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')
|
document.querySelector('#TemplateConfigPage')
|
||||||
.addEventListener('pageshow', function () {
|
.addEventListener('pageshow', function () {
|
||||||
Dashboard.showLoadingMsg();
|
Dashboard.showLoadingMsg();
|
||||||
@ -1136,10 +1111,6 @@
|
|||||||
eraseTimestamps("Credits");
|
eraseTimestamps("Credits");
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
btnCleanCache.addEventListener("click", (e) => {
|
|
||||||
cleanCache();
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
btnSeasonEraseTimestamps.addEventListener("click", () => {
|
btnSeasonEraseTimestamps.addEventListener("click", () => {
|
||||||
Dashboard.confirm(
|
Dashboard.confirm(
|
||||||
"Are you sure you want to erase all timestamps for this season?",
|
"Are you sure you want to erase all timestamps for this season?",
|
||||||
|
@ -139,19 +139,6 @@ public class SkipIntroController : ControllerBase
|
|||||||
return NoContent();
|
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>
|
/// <summary>
|
||||||
/// Get all introductions or credits. Only used by the end to end testing script.
|
/// Get all introductions or credits. Only used by the end to end testing script.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -4,7 +4,6 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.Extensions.Logging;
|
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>
|
/// <summary>
|
||||||
/// Determines the path an episode should be cached at.
|
/// Determines the path an episode should be cached at.
|
||||||
/// This function was created before the unified caching mechanism was introduced (in v0.1.7).
|
/// This function was created before the unified caching mechanism was introduced (in v0.1.7).
|
||||||
|
@ -374,6 +374,24 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
|||||||
SaveTimestamps(mode);
|
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>
|
/// <summary>
|
||||||
/// Inject the skip button script into the web interface.
|
/// Inject the skip button script into the web interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -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>();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user