add ignore list (#281)

* add block list

* better block list system.
todo:
-change the name blocklist.
-fixing small bugs.
-maybe moving from Dictionary to List<BlockListItem>.

* - moving from ConcurrentDictionary to List<BlackListItem>, for better xml file.
- changing block to black.
- small fixes.

todo:
- maybe changing the blacklist naming.

* moving to ignorelist.
moving the blacklisting to Manage Fingerprints.
changing the object BlackListItem.

todo:
- moving to the naming "ignorelist", instead of "blacklist".
- adding "save for series" button.
- improving the ui of the blacklist section".
- fixing some more bugs.
- changing the "Manage Fingerprints" to "Manage Timestamps & Fingerprints".

* adding the option to apply ignorelist changes into a series.
moving to ignorelist naming.
changing "Manage Fingerprints" to "Manage Timestamps & Fingerprints".
improving the ui of the ignorelist editor

* small fixes

* fix some bugs. improving the ignore feature

* fix some stuff

* Refactor CSS styles for ignore list checkboxes

* small fixes

* small changes

* small changes

* big changes

* small fixes

* Refactor IgnoreListItem to use SeasonId instead of Id

* Refactor IgnoreListItem to use SeasonId instead of Id.

changes to the ExecuteAsync function and to its documentation
This commit is contained in:
theMasterpc 2024-09-20 14:18:04 +03:00 committed by GitHub
parent 06da138a17
commit d867ede882
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 437 additions and 45 deletions

View File

@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using ConfusedPolarBear.Plugin.IntroSkipper.Data; using ConfusedPolarBear.Plugin.IntroSkipper.Data;
using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Plugins;

View File

@ -537,7 +537,7 @@
</details> </details>
<details id="visualizer"> <details id="visualizer">
<summary>Manage Fingerprints</summary> <summary>Manage Timestamps & Fingerprints</summary>
<br /> <br />
<label class="inputLabel" for="troubleshooterShow">Select TV Series to manage</label> <label class="inputLabel" for="troubleshooterShow">Select TV Series to manage</label>
@ -546,6 +546,35 @@
<select is="emby-select" id="troubleshooterSeason" class="emby-select-withcolor emby-select"></select> <select is="emby-select" id="troubleshooterSeason" class="emby-select-withcolor emby-select"></select>
<br /> <br />
<div id="ignorelistSection" style="display: none;">
<h3 style="margin:0;">Ignore list editor</h3>
<p style="margin:0;">
Add or remove items from the ignore list. Items on the ignore list will not be analyzed.<br />
You can apply the changes for the entire series or just the selected season.
</p>
<br />
<div id="ignoreListCheckboxContainer">
<label for="ignorelistIntro" style="margin-right: 1.5em; display: inline-block;">
<span>Ignore intros</span>
<input type="checkbox" id="ignorelistIntro">
</label>
<label for="ignorelistCredits" style="margin-right: 1.5em; display: inline-block;">
<span>Ignore credits</span>
<input type="checkbox" id="ignorelistCredits">
</label>
</div>
<br />
<button id="saveIgnoreListSeries" class="raised button-submit block emby-button">
Apply to series
</button>
<button id="saveIgnoreListSeason" class="raised button-submit block emby-button" style="display: none;">
Apply to season
</button>
</div>
<br />
<label class="inputLabel" for="troubleshooterEpisode1">Select the first episode</label> <label class="inputLabel" for="troubleshooterEpisode1">Select the first episode</label>
<select is="emby-select" id="troubleshooterEpisode1" class="emby-select-withcolor emby-select"></select> <select is="emby-select" id="troubleshooterEpisode1" class="emby-select-withcolor emby-select"></select>
<label class="inputLabel" for="troubleshooterEpisode1">Select the second episode</label> <label class="inputLabel" for="troubleshooterEpisode1">Select the second episode</label>
@ -705,6 +734,8 @@
// seasons grouped by show // seasons grouped by show
var shows = {}; var shows = {};
var IgnoreListSeasonId;
// settings elements // settings elements
var visualizer = document.querySelector("details#visualizer"); var visualizer = document.querySelector("details#visualizer");
var support = document.querySelector("details#support"); var support = document.querySelector("details#support");
@ -759,6 +790,11 @@
] ]
// visualizer elements // visualizer elements
var ignorelistSection = document.querySelector("div#ignorelistSection");
var ignorelistIntro = ignorelistSection.querySelector("input#ignorelistIntro");
var ignorelistCredits = ignorelistSection.querySelector("input#ignorelistCredits");
var saveIgnoreListSeasonButton = ignorelistSection.querySelector("button#saveIgnoreListSeason");
var saveIgnoreListSeriesButton = ignorelistSection.querySelector("button#saveIgnoreListSeries");
var canvas = document.querySelector("canvas#troubleshooter"); var canvas = document.querySelector("canvas#troubleshooter");
var selectShow = document.querySelector("select#troubleshooterShow"); var selectShow = document.querySelector("select#troubleshooterShow");
var selectSeason = document.querySelector("select#troubleshooterSeason"); var selectSeason = document.querySelector("select#troubleshooterSeason");
@ -965,6 +1001,15 @@
clearSelect(selectEpisode1); clearSelect(selectEpisode1);
clearSelect(selectEpisode2); clearSelect(selectEpisode2);
// show the ignore list editor.
Dashboard.showLoadingMsg();
const IgnoreList = await getJson("Intros/IgnoreList/" + encodeURI(selectShow.value));
ignorelistIntro.checked = IgnoreList.IgnoreIntro;
ignorelistCredits.checked = IgnoreList.IgnoreCredits;
ignorelistSection.style.display = "unset";
saveIgnoreListSeasonButton.style.display = "none";
Dashboard.hideLoadingMsg();
// add all seasons from this show to the season select // add all seasons from this show to the season select
for (var season of shows[selectShow.value]) { for (var season of shows[selectShow.value]) {
addItem(selectSeason, season, season); addItem(selectSeason, season, season);
@ -975,20 +1020,30 @@
// season changed, reload all episodes // season changed, reload all episodes
async function seasonChanged() { async function seasonChanged() {
const url = "Intros/Show/" + encodeURI(selectShow.value) + "/" + selectSeason.value; const seasonData = encodeURI(selectShow.value) + "/" + encodeURI(selectSeason.value);
const episodes = await getJson(url);
Dashboard.showLoadingMsg();
// show the ignore list editor.
saveIgnoreListSeasonButton.style.display = "block";
const IgnoreList = await getJson("Intros/IgnoreList/" + seasonData);
ignorelistIntro.checked = IgnoreList.IgnoreIntro;
ignorelistCredits.checked = IgnoreList.IgnoreCredits;
IgnoreListSeasonId = IgnoreList.SeasonId;
// show the erase season button
eraseSeasonContainer.style.display = "unset";
clearSelect(selectEpisode1); clearSelect(selectEpisode1);
clearSelect(selectEpisode2); clearSelect(selectEpisode2);
eraseSeasonContainer.style.display = "unset";
let i = 1; let i = 1;
const episodes = await getJson("Intros/Show/" + seasonData);
for (let episode of episodes) { for (let episode of episodes) {
const strI = i.toLocaleString("en", { minimumIntegerDigits: 2, maximumFractionDigits: 0 }); const strI = i.toLocaleString("en", { minimumIntegerDigits: 2, maximumFractionDigits: 0 });
addItem(selectEpisode1, strI + ": " + episode.Name, episode.Id); addItem(selectEpisode1, strI + ": " + episode.Name, episode.Id);
addItem(selectEpisode2, strI + ": " + episode.Name, episode.Id); addItem(selectEpisode2, strI + ": " + episode.Name, episode.Id);
i++; i++;
} }
Dashboard.hideLoadingMsg();
setTimeout(() => { setTimeout(() => {
selectEpisode1.selectedIndex = 0; selectEpisode1.selectedIndex = 0;
@ -1292,6 +1347,35 @@
} }
); );
}); });
saveIgnoreListSeasonButton.addEventListener("click", () => {
Dashboard.showLoadingMsg();
var url ="Intros/IgnoreList/UpdateSeason";
const newRhs = {
IgnoreIntro: ignorelistIntro.checked,
IgnoreCredits: ignorelistCredits.checked,
SeasonId: IgnoreListSeasonId,
};
fetchWithAuth(url, "POST", JSON.stringify(newRhs));
Dashboard.alert("Ignore list updated for " + selectSeason.value + " of " + selectShow.value);
Dashboard.hideLoadingMsg();
});
saveIgnoreListSeriesButton.addEventListener("click", () => {
Dashboard.showLoadingMsg();
var url ="Intros/IgnoreList/UpdateSeries" + "/" + encodeURIComponent(selectShow.value);
const newRhs = {
IgnoreIntro: ignorelistIntro.checked,
IgnoreCredits: ignorelistCredits.checked,
};
fetchWithAuth(url, "POST", JSON.stringify(newRhs));
Dashboard.alert("Ignore list updated for " + selectShow.value);
Dashboard.hideLoadingMsg();
});
btnUpdateTimestamps.addEventListener("click", () => { btnUpdateTimestamps.addEventListener("click", () => {
const lhsId = selectEpisode1.options[selectEpisode1.selectedIndex].value; const lhsId = selectEpisode1.options[selectEpisode1.selectedIndex].value;
const newLhs = { const newLhs = {

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Net.Mime; using System.Net.Mime;
using ConfusedPolarBear.Plugin.IntroSkipper.Data; using ConfusedPolarBear.Plugin.IntroSkipper.Data;
using MediaBrowser.Common.Api; using MediaBrowser.Common.Api;
@ -72,6 +73,48 @@ public class VisualizationController : ControllerBase
return showSeasons; return showSeasons;
} }
/// <summary>
/// Returns the ignore list for the provided season.
/// </summary>
/// <param name="series">Show name.</param>
/// <param name="season">Season name.</param>
/// <returns>List of episode titles.</returns>
[HttpGet("IgnoreList/{Series}/{Season}")]
public ActionResult<IgnoreListItem> GetIgnoreListSeason([FromRoute] string series, [FromRoute] string season)
{
if (!LookupSeasonIdByName(series, season, out var seasonId))
{
return NotFound();
}
if (!Plugin.Instance!.IgnoreList.TryGetValue(seasonId, out _))
{
return new IgnoreListItem(seasonId);
}
return new IgnoreListItem(Plugin.Instance!.IgnoreList[seasonId]);
}
/// <summary>
/// Returns the ignore list for the provided series.
/// </summary>
/// <param name="series">Show name.</param>
/// <returns>List of episode titles.</returns>
[HttpGet("IgnoreList/{Series}")]
public ActionResult<IgnoreListItem> GetIgnoreListSeries([FromRoute] string series)
{
if (!LookupSeasonIdsByName(series, out var seasonIds))
{
return NotFound();
}
return new IgnoreListItem(Guid.Empty)
{
IgnoreIntro = seasonIds.All(seasonId => Plugin.Instance!.IsIgnored(seasonId, AnalysisMode.Introduction)),
IgnoreCredits = seasonIds.All(seasonId => Plugin.Instance!.IsIgnored(seasonId, AnalysisMode.Credits))
};
}
/// <summary> /// <summary>
/// Returns the names and unique identifiers of all episodes in the provided season. /// Returns the names and unique identifiers of all episodes in the provided season.
/// </summary> /// </summary>
@ -79,9 +122,7 @@ public class VisualizationController : ControllerBase
/// <param name="season">Season name.</param> /// <param name="season">Season name.</param>
/// <returns>List of episode titles.</returns> /// <returns>List of episode titles.</returns>
[HttpGet("Show/{Series}/{Season}")] [HttpGet("Show/{Series}/{Season}")]
public ActionResult<List<EpisodeVisualization>> GetSeasonEpisodes( public ActionResult<List<EpisodeVisualization>> GetSeasonEpisodes([FromRoute] string series, [FromRoute] string season)
[FromRoute] string series,
[FromRoute] string season)
{ {
var visualEpisodes = new List<EpisodeVisualization>(); var visualEpisodes = new List<EpisodeVisualization>();
@ -157,6 +198,61 @@ public class VisualizationController : ControllerBase
return NoContent(); return NoContent();
} }
/// <summary>
/// Updates the ignore list for the provided season.
/// </summary>
/// <param name="ignoreListItem">New ignore list items.</param>
/// <param name="save">Save the ignore list.</param>
/// <returns>No content.</returns>
[HttpPost("IgnoreList/UpdateSeason")]
public ActionResult UpdateIgnoreListSeason([FromBody] IgnoreListItem ignoreListItem, bool save = true)
{
if (!Plugin.Instance!.QueuedMediaItems.ContainsKey(ignoreListItem.SeasonId))
{
return NotFound();
}
if (ignoreListItem.IgnoreIntro || ignoreListItem.IgnoreCredits)
{
Plugin.Instance!.IgnoreList.AddOrUpdate(ignoreListItem.SeasonId, ignoreListItem, (_, _) => ignoreListItem);
}
else
{
Plugin.Instance!.IgnoreList.TryRemove(ignoreListItem.SeasonId, out _);
}
if (save)
{
Plugin.Instance!.SaveIgnoreList();
}
return NoContent();
}
/// <summary>
/// Updates the ignore list for the provided series.
/// </summary>
/// <param name="series">Series name.</param>
/// <param name="ignoreListItem">New ignore list items.</param>
/// <returns>No content.</returns>
[HttpPost("IgnoreList/UpdateSeries/{Series}")]
public ActionResult UpdateIgnoreListSeries([FromRoute] string series, [FromBody] IgnoreListItem ignoreListItem)
{
if (!LookupSeasonIdsByName(series, out var seasonIds))
{
return NotFound();
}
foreach (var seasonId in seasonIds)
{
UpdateIgnoreListSeason(new IgnoreListItem(ignoreListItem) { SeasonId = seasonId }, false);
}
Plugin.Instance!.SaveIgnoreList();
return NoContent();
}
/// <summary> /// <summary>
/// Updates the introduction timestamps for the provided episode. /// Updates the introduction timestamps for the provided episode.
/// </summary> /// </summary>
@ -212,4 +308,60 @@ public class VisualizationController : ControllerBase
episodes = []; episodes = [];
return false; return false;
} }
/// <summary>
/// Lookup a named season of a series and return its season id.
/// </summary>
/// <param name="series">Series name.</param>
/// <param name="season">Season name.</param>
/// <param name="seasonId">Season id.</param>
/// <returns>Boolean indicating if the requested season was found.</returns>
private bool LookupSeasonIdByName(string series, string season, out Guid seasonId)
{
foreach (var queuedEpisodes in Plugin.Instance!.QueuedMediaItems)
{
var first = queuedEpisodes.Value[0];
var firstSeasonName = GetSeasonName(first);
// Assert that the queued episode series and season are equal to what was requested
if (
!string.Equals(first.SeriesName, series, StringComparison.OrdinalIgnoreCase) ||
!string.Equals(firstSeasonName, season, StringComparison.OrdinalIgnoreCase))
{
continue;
}
seasonId = queuedEpisodes.Key;
return true;
}
seasonId = Guid.Empty;
return false;
}
/// <summary>
/// Lookup a named series and return all the season ids.
/// </summary>
/// <param name="series">Series name.</param>
/// <param name="seasons">Seasons.</param>
/// <returns>Boolean indicating if the requested series was found.</returns>
private bool LookupSeasonIdsByName(string series, out List<Guid> seasons)
{
seasons = new List<Guid>();
foreach (var queuedEpisodes in Plugin.Instance!.QueuedMediaItems)
{
var first = queuedEpisodes.Value[0];
// Assert that the queued episode series is equal to what was requested
if (!string.Equals(first.SeriesName, series, StringComparison.OrdinalIgnoreCase))
{
continue;
}
seasons.Add(queuedEpisodes.Key);
}
return seasons.Count > 0;
}
} }

View File

@ -0,0 +1,89 @@
using System;
using System.Runtime.Serialization;
namespace ConfusedPolarBear.Plugin.IntroSkipper.Data;
/// <summary>
/// Represents an item to ignore.
/// </summary>
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/ConfusedPolarBear.Plugin.IntroSkipper")]
public class IgnoreListItem
{
/// <summary>
/// Initializes a new instance of the <see cref="IgnoreListItem"/> class.
/// </summary>
public IgnoreListItem()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IgnoreListItem"/> class.
/// </summary>
/// <param name="seasonId">The season id.</param>
public IgnoreListItem(Guid seasonId)
{
SeasonId = seasonId;
}
/// <summary>
/// Initializes a new instance of the <see cref="IgnoreListItem"/> class.
/// </summary>
/// <param name="item">The item to copy.</param>
public IgnoreListItem(IgnoreListItem item)
{
SeasonId = item.SeasonId;
IgnoreIntro = item.IgnoreIntro;
IgnoreCredits = item.IgnoreCredits;
}
/// <summary>
/// Gets or sets the season id.
/// </summary>
[DataMember]
public Guid SeasonId { get; set; } = Guid.Empty;
/// <summary>
/// Gets or sets a value indicating whether to ignore the intro.
/// </summary>
[DataMember]
public bool IgnoreIntro { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether to ignore the credits.
/// </summary>
[DataMember]
public bool IgnoreCredits { get; set; } = false;
/// <summary>
/// Toggles the provided mode to the provided value.
/// </summary>
/// <param name="mode">Analysis mode.</param>
/// <param name="value">Value to set.</param>
public void Toggle(AnalysisMode mode, bool value)
{
switch (mode)
{
case AnalysisMode.Introduction:
IgnoreIntro = value;
break;
case AnalysisMode.Credits:
IgnoreCredits = value;
break;
}
}
/// <summary>
/// Checks if the provided mode is ignored.
/// </summary>
/// <param name="mode">Analysis mode.</param>
/// <returns>True if ignored, false otherwise.</returns>
public bool IsIgnored(AnalysisMode mode)
{
return mode switch
{
AnalysisMode.Introduction => IgnoreIntro,
AnalysisMode.Credits => IgnoreCredits,
_ => false,
};
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration; using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
using ConfusedPolarBear.Plugin.IntroSkipper.Data; using ConfusedPolarBear.Plugin.IntroSkipper.Data;
@ -30,6 +31,7 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
private readonly ILogger<Plugin> _logger; private readonly ILogger<Plugin> _logger;
private readonly string _introPath; private readonly string _introPath;
private readonly string _creditsPath; private readonly string _creditsPath;
private string _ignorelistPath;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Plugin"/> class. /// Initializes a new instance of the <see cref="Plugin"/> class.
@ -66,6 +68,7 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
FingerprintCachePath = Path.Join(introsDirectory, pluginCachePath); FingerprintCachePath = Path.Join(introsDirectory, pluginCachePath);
_introPath = Path.Join(applicationPaths.DataPath, pluginDirName, "intros.xml"); _introPath = Path.Join(applicationPaths.DataPath, pluginDirName, "intros.xml");
_creditsPath = Path.Join(applicationPaths.DataPath, pluginDirName, "credits.xml"); _creditsPath = Path.Join(applicationPaths.DataPath, pluginDirName, "credits.xml");
_ignorelistPath = Path.Join(applicationPaths.DataPath, pluginDirName, "ignorelist.xml");
var cacheRoot = applicationPaths.CachePath; var cacheRoot = applicationPaths.CachePath;
var oldIntrosDirectory = Path.Join(cacheRoot, pluginDirName); var oldIntrosDirectory = Path.Join(cacheRoot, pluginDirName);
@ -129,6 +132,15 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
_logger.LogWarning("Unable to load introduction timestamps: {Exception}", ex); _logger.LogWarning("Unable to load introduction timestamps: {Exception}", ex);
} }
try
{
LoadIgnoreList();
}
catch (Exception ex)
{
_logger.LogWarning("Unable to load ignore list: {Exception}", ex);
}
// Inject the skip intro button code into the web interface. // Inject the skip intro button code into the web interface.
try try
{ {
@ -164,6 +176,11 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
/// </summary> /// </summary>
public ConcurrentDictionary<Guid, EpisodeState> EpisodeStates { get; } = new(); public ConcurrentDictionary<Guid, EpisodeState> EpisodeStates { get; } = new();
/// <summary>
/// Gets the ignore list.
/// </summary>
public ConcurrentDictionary<Guid, IgnoreListItem> IgnoreList { get; } = new();
/// <summary> /// <summary>
/// Gets or sets the total number of episodes in the queue. /// Gets or sets the total number of episodes in the queue.
/// </summary> /// </summary>
@ -226,6 +243,53 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
} }
} }
/// <summary>
/// Save IgnoreList to disk.
/// </summary>
public void SaveIgnoreList()
{
var ignorelist = Instance!.IgnoreList.Values.ToList();
lock (_serializationLock)
{
try
{
XmlSerializationHelper.SerializeToXml(ignorelist, _ignorelistPath);
}
catch (Exception e)
{
_logger.LogError("SaveIgnoreList {Message}", e.Message);
}
}
}
/// <summary>
/// Check if an item is ignored.
/// </summary>
/// <param name="id">Item id.</param>
/// <param name="mode">Mode.</param>
/// <returns>True if ignored, false otherwise.</returns>
public bool IsIgnored(Guid id, AnalysisMode mode)
{
return Instance!.IgnoreList.TryGetValue(id, out var item) && item.IsIgnored(mode);
}
/// <summary>
/// Load IgnoreList from disk.
/// </summary>
public void LoadIgnoreList()
{
if (File.Exists(_ignorelistPath))
{
var ignorelist = XmlSerializationHelper.DeserializeFromXml<IgnoreListItem>(_ignorelistPath);
foreach (var item in ignorelist)
{
Instance!.IgnoreList.TryAdd(item.SeasonId, item);
}
}
}
/// <summary> /// <summary>
/// Restore previous analysis results from disk. /// Restore previous analysis results from disk.
/// </summary> /// </summary>
@ -234,7 +298,7 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
if (File.Exists(_introPath)) if (File.Exists(_introPath))
{ {
// Since dictionaries can't be easily serialized, analysis results are stored on disk as a list. // Since dictionaries can't be easily serialized, analysis results are stored on disk as a list.
var introList = XmlSerializationHelper.DeserializeFromXml(_introPath); var introList = XmlSerializationHelper.DeserializeFromXml<Segment>(_introPath);
foreach (var intro in introList) foreach (var intro in introList)
{ {
@ -244,7 +308,7 @@ public partial class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
if (File.Exists(_creditsPath)) if (File.Exists(_creditsPath))
{ {
var creditList = XmlSerializationHelper.DeserializeFromXml(_creditsPath); var creditList = XmlSerializationHelper.DeserializeFromXml<Segment>(_creditsPath);
foreach (var credit in creditList) foreach (var credit in creditList)
{ {

View File

@ -114,7 +114,7 @@ public class BaseItemAnalyzerTask
// of the current media items were deleted from Jellyfin since the task was started. // of the current media items were deleted from Jellyfin since the task was started.
var (episodes, requiredModes) = queueManager.VerifyQueue( var (episodes, requiredModes) = queueManager.VerifyQueue(
season.Value.AsReadOnly(), season.Value.AsReadOnly(),
_analysisModes); _analysisModes.Where(m => !Plugin.Instance!.IsIgnored(season.Key, m)).ToList().AsReadOnly());
var episodeCount = episodes.Count; var episodeCount = episodes.Count;

View File

@ -58,7 +58,9 @@ public class CleanCacheTask : IScheduledTask
public string Key => "CPBIntroSkipperCleanCache"; public string Key => "CPBIntroSkipperCleanCache";
/// <summary> /// <summary>
/// Cleans the Intro Skipper cache by removing files that are no longer associated with episodes in the library. /// Cleans the cache of unused files.
/// Clears the Segment cache by removing files that are no longer associated with episodes in the library.
/// Clears the IgnoreList cache by removing items that are no longer associated with seasons in the library.
/// </summary> /// </summary>
/// <param name="progress">Task progress.</param> /// <param name="progress">Task progress.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
@ -103,6 +105,30 @@ public class CleanCacheTask : IScheduledTask
FFmpegWrapper.DeleteEpisodeCache(episodeId); FFmpegWrapper.DeleteEpisodeCache(episodeId);
} }
// Clean up ignore list by removing items that are no longer exist..
var removedItems = false;
foreach (var ignoredItem in Plugin.Instance.IgnoreList.Values.ToList())
{
if (!Plugin.Instance.QueuedMediaItems.ContainsKey(ignoredItem.SeasonId))
{
removedItems = true;
Plugin.Instance.IgnoreList.TryRemove(ignoredItem.SeasonId, out _);
}
}
// Save ignore list if at least one item was removed.
if (removedItems)
{
try
{
Plugin.Instance!.SaveIgnoreList();
}
catch (Exception e)
{
_logger.LogError("Failed to save ignore list: {Error}", e.Message);
}
}
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Xml; using System.Xml;
using ConfusedPolarBear.Plugin.IntroSkipper.Data; using ConfusedPolarBear.Plugin.IntroSkipper.Data;
@ -22,40 +23,17 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper
public static void MigrateFromIntro(string filePath) public static void MigrateFromIntro(string filePath)
{ {
var intros = new List<Intro>(); List<Intro> intros = DeserializeFromXml<Intro>(filePath);
var segments = new List<Segment>();
try
{
// Create a FileStream to read the XML file
using FileStream fileStream = new FileStream(filePath, FileMode.Open);
// Create an XmlDictionaryReader to read the XML
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fileStream, new XmlDictionaryReaderQuotas());
// Create a DataContractSerializer for type T
DataContractSerializer serializer = new DataContractSerializer(typeof(List<Intro>));
// Deserialize the object from the XML
intros = serializer.ReadObject(reader) as List<Intro>;
// Close the reader
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine($"Error deserializing XML: {ex.Message}");
}
ArgumentNullException.ThrowIfNull(intros); ArgumentNullException.ThrowIfNull(intros);
intros.ForEach(delegate(Intro name)
{ var segments = intros.Select(name => new Segment(name)).ToList();
segments.Add(new Segment(name));
});
SerializeToXml(segments, filePath); SerializeToXml(segments, filePath);
} }
public static List<Segment> DeserializeFromXml(string filePath) public static List<T> DeserializeFromXml<T>(string filePath)
{ {
var result = new List<Segment>(); var result = new List<T>();
try try
{ {
// Create a FileStream to read the XML file // Create a FileStream to read the XML file
@ -63,11 +41,11 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper
// Create an XmlDictionaryReader to read the XML // Create an XmlDictionaryReader to read the XML
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fileStream, new XmlDictionaryReaderQuotas()); XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fileStream, new XmlDictionaryReaderQuotas());
// Create a DataContractSerializer for type T // Create a DataContractSerializer for type List<T>
DataContractSerializer serializer = new DataContractSerializer(typeof(List<Segment>)); DataContractSerializer serializer = new DataContractSerializer(typeof(List<T>));
// Deserialize the object from the XML // Deserialize the object from the XML
result = serializer.ReadObject(reader) as List<Segment>; result = serializer.ReadObject(reader) as List<T>;
// Close the reader // Close the reader
reader.Close(); reader.Close();