jellyfin/Emby.Server.Implementations/TV/SeriesPostScanTask.cs

242 lines
8.4 KiB
C#
Raw Normal View History

2016-10-27 07:58:33 +00:00
using System;
2014-12-23 03:58:14 +00:00
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
2016-10-27 07:58:33 +00:00
using MediaBrowser.Controller.Configuration;
2016-04-26 03:39:21 +00:00
using MediaBrowser.Controller.Entities;
2016-10-27 07:58:33 +00:00
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
2016-04-26 03:39:21 +00:00
using MediaBrowser.Controller.Plugins;
2016-10-27 07:58:33 +00:00
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
2016-10-24 02:45:23 +00:00
using MediaBrowser.Model.Globalization;
2016-10-27 07:58:33 +00:00
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
2016-10-23 19:47:34 +00:00
using MediaBrowser.Model.Tasks;
2016-11-03 22:53:02 +00:00
using MediaBrowser.Model.Threading;
2016-10-27 19:03:23 +00:00
using MediaBrowser.Model.Xml;
2016-10-27 07:58:33 +00:00
using MediaBrowser.Providers.TV;
2016-11-03 22:53:02 +00:00
namespace Emby.Server.Implementations.TV
{
class SeriesGroup : List<Series>, IGrouping<string, Series>
{
public string Key { get; set; }
}
class SeriesPostScanTask : ILibraryPostScanTask, IHasOrder
{
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
2014-12-23 03:58:14 +00:00
private readonly ILocalizationManager _localization;
2015-09-13 23:07:54 +00:00
private readonly IFileSystem _fileSystem;
2016-10-27 19:03:23 +00:00
private readonly IXmlReaderSettingsFactory _xmlSettings;
2016-10-27 19:03:23 +00:00
public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
{
_libraryManager = libraryManager;
_logger = logger;
_config = config;
2014-12-23 03:58:14 +00:00
_localization = localization;
2015-09-13 23:07:54 +00:00
_fileSystem = fileSystem;
2016-10-27 19:03:23 +00:00
_xmlSettings = xmlSettings;
}
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return RunInternal(progress, cancellationToken);
}
2016-10-08 05:57:38 +00:00
private Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
{
2016-04-26 03:39:21 +00:00
var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
{
IncludeItemTypes = new[] { typeof(Series).Name },
2016-05-06 06:02:10 +00:00
Recursive = true,
GroupByPresentationUniqueKey = false
2016-04-26 03:39:21 +00:00
}).Cast<Series>().ToList();
var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
2013-12-08 22:07:57 +00:00
2016-10-27 19:03:23 +00:00
return new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings).Run(seriesGroups, true, cancellationToken);
}
2016-04-26 03:39:21 +00:00
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 visited = new HashSet<Series>();
foreach (var series in seriesList)
{
if (!visited.Contains(series))
{
var group = new SeriesGroup();
FindAllLinked(series, visited, links, group);
group.Key = group.Select(s => s.GetProviderId(MetadataProviders.Tvdb)).FirstOrDefault(id => !string.IsNullOrEmpty(id));
yield return group;
}
}
}
2016-04-26 03:39:21 +00:00
private static void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results)
{
results.Add(series);
visited.Add(series);
var links = linksMap[series];
foreach (var s in links)
{
if (!visited.Contains(s))
{
FindAllLinked(s, visited, linksMap, results);
}
}
}
2016-04-26 03:39:21 +00:00
private static bool ShareProviderId(Series a, Series b)
{
return a.ProviderIds.Any(id =>
{
string value;
return b.ProviderIds.TryGetValue(id.Key, out value) && id.Value == value;
});
}
public int Order
{
get
{
// Run after tvdb update task
return 1;
}
}
}
2016-04-26 03:39:21 +00:00
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;
2016-10-27 19:03:23 +00:00
private readonly IXmlReaderSettingsFactory _xmlSettings;
2016-11-03 22:53:02 +00:00
private readonly ITimerFactory _timerFactory;
2016-04-26 03:39:21 +00:00
2016-11-03 22:53:02 +00:00
public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager, IXmlReaderSettingsFactory xmlSettings, ITimerFactory timerFactory)
2016-04-26 03:39:21 +00:00
{
_libraryManager = libraryManager;
_config = config;
_logger = logger;
_localization = localization;
_fileSystem = fileSystem;
_taskManager = taskManager;
2016-10-27 19:03:23 +00:00
_xmlSettings = xmlSettings;
2016-11-03 22:53:02 +00:00
_timerFactory = timerFactory;
2016-04-26 03:39:21 +00:00
}
2016-11-03 22:53:02 +00:00
private ITimer LibraryUpdateTimer { get; set; }
2016-04-26 03:39:21 +00:00
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)
{
2016-11-03 22:53:02 +00:00
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite);
2016-04-26 03:39:21 +00:00
}
else
{
LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
}
}
}
private async void LibraryUpdateTimerCallback(object state)
{
try
2016-04-26 03:39:21 +00:00
{
if (MissingEpisodeProvider.IsRunning)
{
return;
}
2016-04-26 03:39:21 +00:00
if (_libraryManager.IsScanRunning)
{
return;
}
2016-04-26 03:39:21 +00:00
var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true,
GroupByPresentationUniqueKey = false
2016-04-26 03:39:21 +00:00
}).Cast<Series>().ToList();
2016-04-26 03:39:21 +00:00
var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
2016-04-26 03:39:21 +00:00
2016-10-27 19:03:23 +00:00
await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings)
.Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error in SeriesPostScanTask", ex);
}
2016-04-26 03:39:21 +00:00
}
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;
}
}
}
}