Resolve all remaining FIXMEs with credit detection
This commit is contained in:
parent
4bb978639c
commit
1966357e29
@ -60,7 +60,7 @@ public class Entrypoint : IServerEntryPoint
|
|||||||
// Enqueue all episodes at startup to ensure any FFmpeg errors appear as early as possible
|
// Enqueue all episodes at startup to ensure any FFmpeg errors appear as early as possible
|
||||||
_logger.LogInformation("Running startup enqueue");
|
_logger.LogInformation("Running startup enqueue");
|
||||||
var queueManager = new QueueManager(_loggerFactory.CreateLogger<QueueManager>(), _libraryManager);
|
var queueManager = new QueueManager(_loggerFactory.CreateLogger<QueueManager>(), _libraryManager);
|
||||||
queueManager.EnqueueAllEpisodes();
|
queueManager.GetMediaItems();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
@ -37,10 +38,10 @@ public class QueueManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Iterates through all libraries on the server and queues all episodes for analysis.
|
/// Gets all media items on the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Queued media items.</returns>
|
/// <returns>Queued media items.</returns>
|
||||||
public ReadOnlyDictionary<Guid, List<QueuedEpisode>> EnqueueAllEpisodes()
|
public ReadOnlyDictionary<Guid, List<QueuedEpisode>> GetMediaItems()
|
||||||
{
|
{
|
||||||
// Assert that ffmpeg with chromaprint is installed
|
// Assert that ffmpeg with chromaprint is installed
|
||||||
if (!FFmpegWrapper.CheckFFmpegVersion())
|
if (!FFmpegWrapper.CheckFFmpegVersion())
|
||||||
@ -220,4 +221,51 @@ public class QueueManager
|
|||||||
|
|
||||||
Plugin.Instance!.TotalQueued++;
|
Plugin.Instance!.TotalQueued++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify that a collection of queued media items still exist in Jellyfin and in storage.
|
||||||
|
/// This is done to ensure that we don't analyze items that were deleted between the call to GetMediaItems() and popping them from the queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="candidates">Queued media items.</param>
|
||||||
|
/// <param name="mode">Analysis mode.</param>
|
||||||
|
/// <returns>Media items that have been verified to exist in Jellyfin and in storage.</returns>
|
||||||
|
public (ReadOnlyCollection<QueuedEpisode> VerifiedItems, bool AnyUnanalyzed)
|
||||||
|
VerifyQueue(ReadOnlyCollection<QueuedEpisode> candidates, AnalysisMode mode)
|
||||||
|
{
|
||||||
|
var unanalyzed = false;
|
||||||
|
var verified = new List<QueuedEpisode>();
|
||||||
|
|
||||||
|
var timestamps = mode == AnalysisMode.Introduction ?
|
||||||
|
Plugin.Instance!.Intros :
|
||||||
|
Plugin.Instance!.Credits;
|
||||||
|
|
||||||
|
foreach (var candidate in candidates)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var path = Plugin.Instance!.GetItemPath(candidate.EpisodeId);
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
verified.Add(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timestamps.ContainsKey(candidate.EpisodeId))
|
||||||
|
{
|
||||||
|
unanalyzed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(
|
||||||
|
"Skipping {Mode} analysis of {Name} ({Id}): {Exception}",
|
||||||
|
mode,
|
||||||
|
candidate.Name,
|
||||||
|
candidate.EpisodeId,
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (verified.AsReadOnly(), unanalyzed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,6 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
||||||
|
|
||||||
#if !DEBUG
|
|
||||||
#error Fix all FIXMEs introduced during initial credit implementation before release
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Analyze all television episodes for credits.
|
/// Analyze all television episodes for credits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -84,7 +80,7 @@ public class DetectCreditsTask : IScheduledTask
|
|||||||
_loggerFactory.CreateLogger<QueueManager>(),
|
_loggerFactory.CreateLogger<QueueManager>(),
|
||||||
_libraryManager);
|
_libraryManager);
|
||||||
|
|
||||||
var queue = queueManager.EnqueueAllEpisodes();
|
var queue = queueManager.GetMediaItems();
|
||||||
|
|
||||||
if (queue.Count == 0)
|
if (queue.Count == 0)
|
||||||
{
|
{
|
||||||
@ -101,9 +97,11 @@ public class DetectCreditsTask : IScheduledTask
|
|||||||
// Analyze all episodes in the queue using the degrees of parallelism the user specified.
|
// Analyze all episodes in the queue using the degrees of parallelism the user specified.
|
||||||
Parallel.ForEach(queue, options, (season) =>
|
Parallel.ForEach(queue, options, (season) =>
|
||||||
{
|
{
|
||||||
// TODO: FIXME: use VerifyEpisodes
|
var (episodes, unanalyzed) = queueManager.VerifyQueue(
|
||||||
var episodes = season.Value.AsReadOnly();
|
season.Value.AsReadOnly(),
|
||||||
if (episodes.Count == 0)
|
AnalysisMode.Credits);
|
||||||
|
|
||||||
|
if (episodes.Count == 0 || unanalyzed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ public class DetectIntroductionsTask : IScheduledTask
|
|||||||
_loggerFactory.CreateLogger<QueueManager>(),
|
_loggerFactory.CreateLogger<QueueManager>(),
|
||||||
_libraryManager);
|
_libraryManager);
|
||||||
|
|
||||||
var queue = queueManager.EnqueueAllEpisodes();
|
var queue = queueManager.GetMediaItems();
|
||||||
|
|
||||||
if (queue.Count == 0)
|
if (queue.Count == 0)
|
||||||
{
|
{
|
||||||
@ -100,13 +100,15 @@ public class DetectIntroductionsTask : IScheduledTask
|
|||||||
MaxDegreeOfParallelism = Plugin.Instance!.Configuration.MaxParallelism
|
MaxDegreeOfParallelism = Plugin.Instance!.Configuration.MaxParallelism
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: if the queue is modified while the task is running, the task will fail.
|
|
||||||
// clone the queue before running the task to prevent this.
|
|
||||||
|
|
||||||
// Analyze all episodes in the queue using the degrees of parallelism the user specified.
|
// Analyze all episodes in the queue using the degrees of parallelism the user specified.
|
||||||
Parallel.ForEach(queue, options, (season) =>
|
Parallel.ForEach(queue, options, (season) =>
|
||||||
{
|
{
|
||||||
var (episodes, unanalyzed) = VerifyEpisodes(season.Value.AsReadOnly());
|
// Since the first run of the task can run for multiple hours, ensure that none
|
||||||
|
// of the current media items were deleted from Jellyfin since the task was started.
|
||||||
|
var (episodes, unanalyzed) = queueManager.VerifyQueue(
|
||||||
|
season.Value.AsReadOnly(),
|
||||||
|
AnalysisMode.Introduction);
|
||||||
|
|
||||||
if (episodes.Count == 0)
|
if (episodes.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -178,51 +180,6 @@ public class DetectIntroductionsTask : IScheduledTask
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verify that all episodes in a season exist in Jellyfin and as a file in storage.
|
|
||||||
/// TODO: FIXME: move to queue manager.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="candidates">QueuedEpisodes.</param>
|
|
||||||
/// <returns>Verified QueuedEpisodes and a flag indicating if any episode in this season has not been analyzed yet.</returns>
|
|
||||||
private (
|
|
||||||
ReadOnlyCollection<QueuedEpisode> VerifiedEpisodes,
|
|
||||||
bool AnyUnanalyzed)
|
|
||||||
VerifyEpisodes(ReadOnlyCollection<QueuedEpisode> candidates)
|
|
||||||
{
|
|
||||||
var unanalyzed = false;
|
|
||||||
var verified = new List<QueuedEpisode>();
|
|
||||||
|
|
||||||
foreach (var candidate in candidates)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Verify that the episode exists in Jellyfin and in storage
|
|
||||||
var path = Plugin.Instance!.GetItemPath(candidate.EpisodeId);
|
|
||||||
|
|
||||||
if (File.Exists(path))
|
|
||||||
{
|
|
||||||
verified.Add(candidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flag this season for analysis if the current episode hasn't been analyzed yet
|
|
||||||
if (!Plugin.Instance.Intros.ContainsKey(candidate.EpisodeId))
|
|
||||||
{
|
|
||||||
unanalyzed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogDebug(
|
|
||||||
"Skipping analysis of {Name} ({Id}): {Exception}",
|
|
||||||
candidate.Name,
|
|
||||||
candidate.EpisodeId,
|
|
||||||
ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (verified.AsReadOnly(), unanalyzed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fingerprints all episodes in the provided season and stores the timestamps of all introductions.
|
/// Fingerprints all episodes in the provided season and stores the timestamps of all introductions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user