Delete Cache (#192)
Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com>
This commit is contained in:
parent
afe6534ef2
commit
c9f87c58cf
@ -593,9 +593,14 @@
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<button id="btnEraseSeasonTimestamps" type="button" style="display:none;">
|
<div id="eraseSeasonContainer" style="display: none;">
|
||||||
Erase all timestamps for this season
|
<button id="btnEraseSeasonTimestamps" type="button">
|
||||||
</button>
|
Erase all timestamps for this season
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<input type="checkbox" id="eraseSeasonCacheCheckbox" style="margin-left: 10px;">
|
||||||
|
<label for="eraseSeasonCacheCheckbox" style="margin-left: 5px;">Erase cached fingerprint files</label>
|
||||||
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<button id="btnEraseIntroTimestamps">
|
<button id="btnEraseIntroTimestamps">
|
||||||
@ -606,6 +611,15 @@
|
|||||||
<button id="btnEraseCreditTimestamps">
|
<button id="btnEraseCreditTimestamps">
|
||||||
Erase all end credits timestamps (globally)
|
Erase all end credits timestamps (globally)
|
||||||
</button>
|
</button>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<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>
|
</details>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@ -630,6 +644,7 @@
|
|||||||
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 = [
|
||||||
@ -682,6 +697,7 @@
|
|||||||
var txtOffset = document.querySelector("input#offset");
|
var txtOffset = document.querySelector("input#offset");
|
||||||
var txtSuggested = document.querySelector("span#suggestedShifts");
|
var txtSuggested = document.querySelector("span#suggestedShifts");
|
||||||
var btnSeasonEraseTimestamps = document.querySelector("button#btnEraseSeasonTimestamps");
|
var btnSeasonEraseTimestamps = document.querySelector("button#btnEraseSeasonTimestamps");
|
||||||
|
var eraseSeasonContainer = document.getElementById("eraseSeasonContainer");
|
||||||
var timestampError = document.querySelector("textarea#timestampError");
|
var timestampError = document.querySelector("textarea#timestampError");
|
||||||
var timestampEditor = document.querySelector("#timestampEditor");
|
var timestampEditor = document.querySelector("#timestampEditor");
|
||||||
var btnUpdateTimestamps = document.querySelector("button#btnUpdateTimestamps");
|
var btnUpdateTimestamps = document.querySelector("button#btnUpdateTimestamps");
|
||||||
@ -791,7 +807,7 @@
|
|||||||
// show changed, populate seasons
|
// show changed, populate seasons
|
||||||
async function showChanged() {
|
async function showChanged() {
|
||||||
clearSelect(selectSeason);
|
clearSelect(selectSeason);
|
||||||
btnSeasonEraseTimestamps.style.display = "none";
|
eraseSeasonContainer.style.display = "none";
|
||||||
clearSelect(selectEpisode1);
|
clearSelect(selectEpisode1);
|
||||||
clearSelect(selectEpisode2);
|
clearSelect(selectEpisode2);
|
||||||
|
|
||||||
@ -810,7 +826,7 @@
|
|||||||
|
|
||||||
clearSelect(selectEpisode1);
|
clearSelect(selectEpisode1);
|
||||||
clearSelect(selectEpisode2);
|
clearSelect(selectEpisode2);
|
||||||
btnSeasonEraseTimestamps.style.display = "unset";
|
eraseSeasonContainer.style.display = "unset";
|
||||||
|
|
||||||
let i = 1;
|
let i = 1;
|
||||||
for (let episode of episodes) {
|
for (let episode of episodes) {
|
||||||
@ -1028,6 +1044,7 @@
|
|||||||
const body = "Are you sure you want to erase all previously discovered " +
|
const body = "Are you sure you want to erase all previously discovered " +
|
||||||
mode.toLocaleLowerCase() +
|
mode.toLocaleLowerCase() +
|
||||||
" timestamps?";
|
" timestamps?";
|
||||||
|
const eraseCacheChecked = document.getElementById("eraseModeCacheCheckbox").checked;
|
||||||
|
|
||||||
Dashboard.confirm(
|
Dashboard.confirm(
|
||||||
body,
|
body,
|
||||||
@ -1037,12 +1054,31 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchWithAuth("Intros/EraseTimestamps?mode=" + mode, "POST", null);
|
fetchWithAuth("Intros/EraseTimestamps?mode=" + mode + "&eraseCache=" + eraseCacheChecked, "POST", null);
|
||||||
|
|
||||||
Dashboard.alert(mode + " timestamps erased");
|
Dashboard.alert(mode + " timestamps erased");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
@ -1100,6 +1136,10 @@
|
|||||||
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?",
|
||||||
@ -1111,11 +1151,13 @@
|
|||||||
|
|
||||||
const show = selectShow.value;
|
const show = selectShow.value;
|
||||||
const season = selectSeason.value;
|
const season = selectSeason.value;
|
||||||
|
const eraseCacheChecked = document.getElementById("eraseSeasonCacheCheckbox").checked;
|
||||||
|
|
||||||
const url = "Intros/Show/" + encodeURIComponent(show) + "/" + encodeURIComponent(season);
|
const url = "Intros/Show/" + encodeURIComponent(show) + "/" + encodeURIComponent(season);
|
||||||
fetchWithAuth(url, "DELETE", null);
|
fetchWithAuth(url + "?eraseCache=" + eraseCacheChecked, "DELETE", null);
|
||||||
|
|
||||||
Dashboard.alert("Erased timestamps for " + season + " of " + show);
|
Dashboard.alert("Erased timestamps for " + season + " of " + show);
|
||||||
|
document.getElementById("eraseSeasonCacheCheckbox").checked = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -114,11 +114,12 @@ public class SkipIntroController : ControllerBase
|
|||||||
/// Erases all previously discovered introduction timestamps.
|
/// Erases all previously discovered introduction timestamps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mode">Mode.</param>
|
/// <param name="mode">Mode.</param>
|
||||||
|
/// <param name="eraseCache">Erase cache.</param>
|
||||||
/// <response code="204">Operation successful.</response>
|
/// <response code="204">Operation successful.</response>
|
||||||
/// <returns>No content.</returns>
|
/// <returns>No content.</returns>
|
||||||
[Authorize(Policy = Policies.RequiresElevation)]
|
[Authorize(Policy = Policies.RequiresElevation)]
|
||||||
[HttpPost("Intros/EraseTimestamps")]
|
[HttpPost("Intros/EraseTimestamps")]
|
||||||
public ActionResult ResetIntroTimestamps([FromQuery] AnalysisMode mode)
|
public ActionResult ResetIntroTimestamps([FromQuery] AnalysisMode mode, [FromQuery] bool eraseCache = false)
|
||||||
{
|
{
|
||||||
if (mode == AnalysisMode.Introduction)
|
if (mode == AnalysisMode.Introduction)
|
||||||
{
|
{
|
||||||
@ -129,10 +130,28 @@ public class SkipIntroController : ControllerBase
|
|||||||
Plugin.Instance!.Credits.Clear();
|
Plugin.Instance!.Credits.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eraseCache)
|
||||||
|
{
|
||||||
|
FFmpegWrapper.DeleteCacheFiles(mode);
|
||||||
|
}
|
||||||
|
|
||||||
Plugin.Instance!.SaveTimestamps(mode);
|
Plugin.Instance!.SaveTimestamps(mode);
|
||||||
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>
|
||||||
|
@ -125,11 +125,12 @@ public class VisualizationController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="series">Show name.</param>
|
/// <param name="series">Show name.</param>
|
||||||
/// <param name="season">Season name.</param>
|
/// <param name="season">Season name.</param>
|
||||||
|
/// <param name="eraseCache">Erase cache.</param>
|
||||||
/// <response code="204">Season timestamps erased.</response>
|
/// <response code="204">Season timestamps erased.</response>
|
||||||
/// <response code="404">Unable to find season in provided series.</response>
|
/// <response code="404">Unable to find season in provided series.</response>
|
||||||
/// <returns>No content.</returns>
|
/// <returns>No content.</returns>
|
||||||
[HttpDelete("Show/{Series}/{Season}")]
|
[HttpDelete("Show/{Series}/{Season}")]
|
||||||
public ActionResult EraseSeason([FromRoute] string series, [FromRoute] string season)
|
public ActionResult EraseSeason([FromRoute] string series, [FromRoute] string season, [FromQuery] bool eraseCache = false)
|
||||||
{
|
{
|
||||||
if (!LookupSeasonByName(series, season, out var episodes))
|
if (!LookupSeasonByName(series, season, out var episodes))
|
||||||
{
|
{
|
||||||
@ -142,6 +143,10 @@ public class VisualizationController : ControllerBase
|
|||||||
{
|
{
|
||||||
Plugin.Instance!.Intros.TryRemove(e.EpisodeId, out _);
|
Plugin.Instance!.Intros.TryRemove(e.EpisodeId, out _);
|
||||||
Plugin.Instance!.Credits.TryRemove(e.EpisodeId, out _);
|
Plugin.Instance!.Credits.TryRemove(e.EpisodeId, out _);
|
||||||
|
if (eraseCache)
|
||||||
|
{
|
||||||
|
FFmpegWrapper.DeleteEpisodeCache(e.EpisodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin.Instance!.SaveTimestamps(AnalysisMode.Introduction);
|
Plugin.Instance!.SaveTimestamps(AnalysisMode.Introduction);
|
||||||
|
@ -4,6 +4,7 @@ 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;
|
||||||
@ -607,20 +608,72 @@ public static class FFmpegWrapper
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove a cached episode fingerprint from disk.
|
/// Remove a cached episode fingerprint from disk.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="episodeId">Episode to remove from cache.</param>
|
/// <param name="id">Episode to remove from cache.</param>
|
||||||
/// <param name="mode">Analysis mode.</param>
|
public static void DeleteEpisodeCache(Guid id)
|
||||||
public static void DeleteEpisodeCache(string episodeId, AnalysisMode mode)
|
|
||||||
{
|
{
|
||||||
var cachePath = Path.Join(
|
var cachePath = Path.Join(
|
||||||
Plugin.Instance!.FingerprintCachePath,
|
Plugin.Instance!.FingerprintCachePath,
|
||||||
episodeId);
|
id.ToString("N"));
|
||||||
|
|
||||||
if (mode == AnalysisMode.Credits)
|
// File.Delete(cachePath);
|
||||||
|
// File.Delete(cachePath + "-intro-silence-v1");
|
||||||
|
// File.Delete(cachePath + "-credits");
|
||||||
|
|
||||||
|
var filePattern = Path.GetFileName(cachePath) + "*";
|
||||||
|
foreach (var filePath in Directory.EnumerateFiles(Plugin.Instance!.FingerprintCachePath, filePattern))
|
||||||
{
|
{
|
||||||
cachePath += "-credits";
|
File.Delete(filePath);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File.Delete(cachePath);
|
/// <summary>
|
||||||
|
/// Remove cached fingerprints from disk by mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mode">Analysis mode.</param>
|
||||||
|
public static void DeleteCacheFiles(AnalysisMode mode)
|
||||||
|
{
|
||||||
|
foreach (var filePath in Directory.EnumerateFiles(Plugin.Instance!.FingerprintCachePath))
|
||||||
|
{
|
||||||
|
var shouldDelete = (mode == AnalysisMode.Introduction)
|
||||||
|
? !filePath.Contains("credit", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !filePath.Contains("blackframes", StringComparison.OrdinalIgnoreCase)
|
||||||
|
: filePath.Contains("credit", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| filePath.Contains("blackframes", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (shouldDelete)
|
||||||
|
{
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user