mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
faster cleanup of missing episodes
This commit is contained in:
parent
54e04dd027
commit
69622a74a8
@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="fileInfo">The file information.</param>
|
/// <param name="fileInfo">The file information.</param>
|
||||||
/// <param name="parent">The parent.</param>
|
/// <param name="parent">The parent.</param>
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem ResolvePath(FileSystemMetadata fileInfo,
|
BaseItem ResolvePath(FileSystemMetadata fileInfo,
|
||||||
Folder parent = null);
|
Folder parent = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -36,9 +36,9 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="parent">The parent.</param>
|
/// <param name="parent">The parent.</param>
|
||||||
/// <param name="collectionType">Type of the collection.</param>
|
/// <param name="collectionType">Type of the collection.</param>
|
||||||
/// <returns>List{``0}.</returns>
|
/// <returns>List{``0}.</returns>
|
||||||
IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
|
IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
|
||||||
IDirectoryService directoryService,
|
IDirectoryService directoryService,
|
||||||
Folder parent, string
|
Folder parent, string
|
||||||
collectionType = null);
|
collectionType = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -60,7 +60,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem FindByPath(string path);
|
BaseItem FindByPath(string path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the artist.
|
/// Gets the artist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -156,7 +156,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="id">The identifier.</param>
|
/// <param name="id">The identifier.</param>
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem GetMemoryItemById(Guid id);
|
BaseItem GetMemoryItemById(Guid id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the intros.
|
/// Gets the intros.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -243,6 +243,8 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem RetrieveItem(Guid id);
|
BaseItem RetrieveItem(Guid id);
|
||||||
|
|
||||||
|
bool IsScanRunning { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when [item added].
|
/// Occurs when [item added].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -290,7 +292,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string GetConfiguredContentType(string path);
|
string GetConfiguredContentType(string path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Normalizes the root path list.
|
/// Normalizes the root path list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -332,8 +334,8 @@ namespace MediaBrowser.Controller.Library
|
|||||||
Task<UserView> GetNamedView(User user,
|
Task<UserView> GetNamedView(User user,
|
||||||
string name,
|
string name,
|
||||||
string parentId,
|
string parentId,
|
||||||
string viewType,
|
string viewType,
|
||||||
string sortName,
|
string sortName,
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -346,8 +348,8 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task<UserView>.</returns>
|
/// <returns>Task<UserView>.</returns>
|
||||||
Task<UserView> GetNamedView(User user,
|
Task<UserView> GetNamedView(User user,
|
||||||
string name,
|
string name,
|
||||||
string viewType,
|
string viewType,
|
||||||
string sortName,
|
string sortName,
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
@ -393,7 +395,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
string viewType,
|
string viewType,
|
||||||
string sortName,
|
string sortName,
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is video file] [the specified path].
|
/// Determines whether [is video file] [the specified path].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -477,14 +479,14 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>List<PersonInfo>.</returns>
|
/// <returns>List<PersonInfo>.</returns>
|
||||||
List<PersonInfo> GetPeople(InternalPeopleQuery query);
|
List<PersonInfo> GetPeople(InternalPeopleQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the people items.
|
/// Gets the people items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>List<Person>.</returns>
|
/// <returns>List<Person>.</returns>
|
||||||
List<Person> GetPeopleItems(InternalPeopleQuery query);
|
List<Person> GetPeopleItems(InternalPeopleQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all people names.
|
/// Gets all people names.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -559,7 +561,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>QueryResult<BaseItem>.</returns>
|
/// <returns>QueryResult<BaseItem>.</returns>
|
||||||
QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query);
|
QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ignores the file.
|
/// Ignores the file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -27,6 +27,8 @@ namespace MediaBrowser.Providers.TV
|
|||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||||
|
private static readonly SemaphoreSlim _resourceLock = new SemaphoreSlim(1, 1);
|
||||||
|
public static bool IsRunning = false;
|
||||||
|
|
||||||
public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem)
|
public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
@ -37,13 +39,16 @@ namespace MediaBrowser.Providers.TV
|
|||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Run(IEnumerable<IGrouping<string, Series>> series, CancellationToken cancellationToken)
|
public async Task Run(List<IGrouping<string, Series>> series, bool addNewItems, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
await _resourceLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
IsRunning = true;
|
||||||
|
|
||||||
foreach (var seriesGroup in series)
|
foreach (var seriesGroup in series)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Run(seriesGroup, cancellationToken).ConfigureAwait(false);
|
await Run(seriesGroup, addNewItems, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@ -58,9 +63,12 @@ namespace MediaBrowser.Providers.TV
|
|||||||
_logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key);
|
_logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IsRunning = false;
|
||||||
|
_resourceLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Run(IGrouping<string, Series> group, CancellationToken cancellationToken)
|
private async Task Run(IGrouping<string, Series> group, bool addNewItems, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var tvdbId = group.Key;
|
var tvdbId = group.Key;
|
||||||
|
|
||||||
@ -110,7 +118,7 @@ namespace MediaBrowser.Providers.TV
|
|||||||
|
|
||||||
var hasNewEpisodes = false;
|
var hasNewEpisodes = false;
|
||||||
|
|
||||||
if (_config.Configuration.EnableInternetProviders)
|
if (_config.Configuration.EnableInternetProviders && addNewItems)
|
||||||
{
|
{
|
||||||
var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase));
|
var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
@ -427,7 +435,7 @@ namespace MediaBrowser.Providers.TV
|
|||||||
|
|
||||||
await season.AddChild(episode, cancellationToken).ConfigureAwait(false);
|
await season.AddChild(episode, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await episode.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
|
await episode.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
|
||||||
{
|
{
|
||||||
}, cancellationToken).ConfigureAwait(false);
|
}, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommonIO;
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.ScheduledTasks;
|
||||||
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Plugins;
|
||||||
|
using MediaBrowser.Model.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.TV
|
namespace MediaBrowser.Providers.TV
|
||||||
{
|
{
|
||||||
@ -46,14 +50,17 @@ namespace MediaBrowser.Providers.TV
|
|||||||
|
|
||||||
private async Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
|
private async Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var seriesList = _libraryManager.RootFolder
|
var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
|
||||||
.GetRecursiveChildren(i => i is Series)
|
{
|
||||||
.Cast<Series>()
|
IncludeItemTypes = new[] { typeof(Series).Name },
|
||||||
.ToList();
|
Recursive = true
|
||||||
|
|
||||||
|
}).Cast<Series>().ToList();
|
||||||
|
|
||||||
var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
|
var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
|
||||||
|
|
||||||
await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem).Run(seriesGroups, cancellationToken).ConfigureAwait(false);
|
await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem)
|
||||||
|
.Run(seriesGroups, true, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var numComplete = 0;
|
var numComplete = 0;
|
||||||
|
|
||||||
@ -82,7 +89,7 @@ namespace MediaBrowser.Providers.TV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList)
|
internal static IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList)
|
||||||
{
|
{
|
||||||
var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList());
|
var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList());
|
||||||
|
|
||||||
@ -102,7 +109,7 @@ namespace MediaBrowser.Providers.TV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results)
|
private static void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results)
|
||||||
{
|
{
|
||||||
results.Add(series);
|
results.Add(series);
|
||||||
visited.Add(series);
|
visited.Add(series);
|
||||||
@ -118,7 +125,7 @@ namespace MediaBrowser.Providers.TV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShareProviderId(Series a, Series b)
|
private static bool ShareProviderId(Series a, Series b)
|
||||||
{
|
{
|
||||||
return a.ProviderIds.Any(id =>
|
return a.ProviderIds.Any(id =>
|
||||||
{
|
{
|
||||||
@ -137,4 +144,108 @@ namespace MediaBrowser.Providers.TV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CleanMissingEpisodesEntryPoint : IServerEntryPoint
|
||||||
|
{
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IServerConfigurationManager _config;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly ILocalizationManager _localization;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
private readonly object _libraryChangedSyncLock = new object();
|
||||||
|
private const int LibraryUpdateDuration = 180000;
|
||||||
|
private readonly ITaskManager _taskManager;
|
||||||
|
|
||||||
|
public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager)
|
||||||
|
{
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
_config = config;
|
||||||
|
_logger = logger;
|
||||||
|
_localization = localization;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
_taskManager = taskManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Timer LibraryUpdateTimer { get; set; }
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
_libraryManager.ItemAdded += _libraryManager_ItemAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
|
||||||
|
{
|
||||||
|
if (!FilterItem(e.Item))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_libraryChangedSyncLock)
|
||||||
|
{
|
||||||
|
if (LibraryUpdateTimer == null)
|
||||||
|
{
|
||||||
|
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void LibraryUpdateTimerCallback(object state)
|
||||||
|
{
|
||||||
|
if (MissingEpisodeProvider.IsRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_libraryManager.IsScanRunning)
|
||||||
|
{
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
|
||||||
|
{
|
||||||
|
IncludeItemTypes = new[] { typeof(Series).Name },
|
||||||
|
Recursive = true
|
||||||
|
|
||||||
|
}).Cast<Series>().ToList();
|
||||||
|
|
||||||
|
var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
|
||||||
|
|
||||||
|
await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem)
|
||||||
|
.Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FilterItem(BaseItem item)
|
||||||
|
{
|
||||||
|
return item is Episode && item.LocationType != LocationType.Virtual;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||||
|
protected virtual void Dispose(bool dispose)
|
||||||
|
{
|
||||||
|
if (dispose)
|
||||||
|
{
|
||||||
|
if (LibraryUpdateTimer != null)
|
||||||
|
{
|
||||||
|
LibraryUpdateTimer.Dispose();
|
||||||
|
LibraryUpdateTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_libraryManager.ItemAdded -= _libraryManager_ItemAdded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
|
private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
|
||||||
private readonly Func<IProviderManager> _providerManagerFactory;
|
private readonly Func<IProviderManager> _providerManagerFactory;
|
||||||
private readonly Func<IUserViewManager> _userviewManager;
|
private readonly Func<IUserViewManager> _userviewManager;
|
||||||
|
public bool IsScanRunning { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _library items cache
|
/// The _library items cache
|
||||||
@ -1102,6 +1103,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
|
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
IsScanRunning = true;
|
||||||
_libraryMonitorFactory().Stop();
|
_libraryMonitorFactory().Stop();
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -1111,6 +1113,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_libraryMonitorFactory().Start();
|
_libraryMonitorFactory().Start();
|
||||||
|
IsScanRunning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user