support individual library refreshing

This commit is contained in:
Luke Pulverenti 2017-06-23 12:04:45 -04:00
parent 6ff89eab78
commit 1e5c3db9eb
44 changed files with 523 additions and 261 deletions

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events; using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -402,7 +403,7 @@ namespace Emby.Common.Implementations.ScheduledTasks
throw new InvalidOperationException("Cannot execute a Task that is already running"); throw new InvalidOperationException("Cannot execute a Task that is already running");
} }
var progress = new Progress<double>(); var progress = new SimpleProgress<double>();
CurrentCancellationTokenSource = new CancellationTokenSource(); CurrentCancellationTokenSource = new CancellationTokenSource();

View File

@ -5,7 +5,7 @@ using MediaBrowser.Model.Drawing;
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -104,7 +104,7 @@ namespace Emby.Drawing.ImageMagick
var tempPath = await httpClient.GetTempFile(new HttpRequestOptions var tempPath = await httpClient.GetTempFile(new HttpRequestOptions
{ {
Url = url, Url = url,
Progress = new Progress<double>() Progress = new SimpleProgress<double>()
}).ConfigureAwait(false); }).ConfigureAwait(false);

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using System.Reflection; using System.Reflection;
using MediaBrowser.Common.Progress;
namespace Emby.Drawing.Skia namespace Emby.Drawing.Skia
{ {
@ -99,7 +100,7 @@ namespace Emby.Drawing.Skia
var tempPath = await httpClient.GetTempFile(new HttpRequestOptions var tempPath = await httpClient.GetTempFile(new HttpRequestOptions
{ {
Url = url, Url = url,
Progress = new Progress<double>() Progress = new SimpleProgress<double>()
}).ConfigureAwait(false); }).ConfigureAwait(false);

View File

@ -23,7 +23,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
@ -980,7 +980,7 @@ namespace Emby.Server.Implementations.Channels
? null ? null
: _userManager.GetUserById(query.UserId); : _userManager.GetUserById(query.UserId);
var internalResult = await GetChannelItemsInternal(query, new Progress<double>(), cancellationToken).ConfigureAwait(false); var internalResult = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
var dtoOptions = new DtoOptions() var dtoOptions = new DtoOptions()
{ {

View File

@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.Channels
StartIndex = totalRetrieved, StartIndex = totalRetrieved,
FolderId = folderId FolderId = folderId
}, new Progress<double>(), cancellationToken); }, new SimpleProgress<double>(), cancellationToken);
folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N"))); folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N")));

View File

@ -4,6 +4,7 @@ using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.Channels namespace Emby.Server.Implementations.Channels
@ -42,7 +43,7 @@ namespace Emby.Server.Implementations.Channels
{ {
var manager = (ChannelManager)_channelManager; var manager = (ChannelManager)_channelManager;
await manager.RefreshChannels(new Progress<double>(), cancellationToken).ConfigureAwait(false); await manager.RefreshChannels(new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken) await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);

View File

@ -148,7 +148,6 @@
<Compile Include="Library\Validators\PeopleValidator.cs" /> <Compile Include="Library\Validators\PeopleValidator.cs" />
<Compile Include="Library\Validators\StudiosPostScanTask.cs" /> <Compile Include="Library\Validators\StudiosPostScanTask.cs" />
<Compile Include="Library\Validators\StudiosValidator.cs" /> <Compile Include="Library\Validators\StudiosValidator.cs" />
<Compile Include="Library\Validators\YearsPostScanTask.cs" />
<Compile Include="LiveTv\ChannelImageProvider.cs" /> <Compile Include="LiveTv\ChannelImageProvider.cs" />
<Compile Include="LiveTv\EmbyTV\DirectRecorder.cs" /> <Compile Include="LiveTv\EmbyTV\DirectRecorder.cs" />
<Compile Include="LiveTv\EmbyTV\EmbyTV.cs" /> <Compile Include="LiveTv\EmbyTV\EmbyTV.cs" />

View File

@ -6,9 +6,14 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Threading; using MediaBrowser.Model.Threading;
@ -49,13 +54,16 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary> /// </summary>
private const int LibraryUpdateDuration = 5000; private const int LibraryUpdateDuration = 5000;
public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, ITimerFactory timerFactory) private readonly IProviderManager _providerManager;
public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, ITimerFactory timerFactory, IProviderManager providerManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_sessionManager = sessionManager; _sessionManager = sessionManager;
_userManager = userManager; _userManager = userManager;
_logger = logger; _logger = logger;
_timerFactory = timerFactory; _timerFactory = timerFactory;
_providerManager = providerManager;
} }
public void Run() public void Run()
@ -64,6 +72,108 @@ namespace Emby.Server.Implementations.EntryPoints
_libraryManager.ItemUpdated += libraryManager_ItemUpdated; _libraryManager.ItemUpdated += libraryManager_ItemUpdated;
_libraryManager.ItemRemoved += libraryManager_ItemRemoved; _libraryManager.ItemRemoved += libraryManager_ItemRemoved;
_providerManager.RefreshCompleted += _providerManager_RefreshCompleted;
_providerManager.RefreshStarted += _providerManager_RefreshStarted;
_providerManager.RefreshProgress += _providerManager_RefreshProgress;
}
private Dictionary<Guid, DateTime> _lastProgressMessageTimes = new Dictionary<Guid, DateTime>();
private void _providerManager_RefreshProgress(object sender, GenericEventArgs<Tuple<BaseItem, double>> e)
{
var item = e.Argument.Item1;
if (!EnableRefreshMessage(item))
{
return;
}
var progress = e.Argument.Item2;
DateTime lastMessageSendTime;
if (_lastProgressMessageTimes.TryGetValue(item.Id, out lastMessageSendTime))
{
if (progress > 0 && progress < 100 && (DateTime.UtcNow - lastMessageSendTime).TotalMilliseconds < 1000)
{
return;
}
}
_lastProgressMessageTimes[item.Id] = DateTime.UtcNow;
var dict = new Dictionary<string, string>();
dict["ItemId"] = item.Id.ToString("N");
dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture);
try
{
_sessionManager.SendMessageToAdminSessions("RefreshProgress", dict, CancellationToken.None);
_logger.Info("Sending refresh progress {0} {1}", item.Id.ToString("N"), progress);
}
catch
{
}
var collectionFolders = _libraryManager.GetCollectionFolders(item).ToList();
foreach (var collectionFolder in collectionFolders)
{
var collectionFolderDict = new Dictionary<string, string>();
collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N");
collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture);
try
{
_sessionManager.SendMessageToAdminSessions("RefreshProgress", collectionFolderDict, CancellationToken.None);
}
catch
{
}
}
}
private void _providerManager_RefreshStarted(object sender, GenericEventArgs<BaseItem> e)
{
_providerManager_RefreshProgress(sender, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(e.Argument, 0)));
}
private void _providerManager_RefreshCompleted(object sender, GenericEventArgs<BaseItem> e)
{
_providerManager_RefreshProgress(sender, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(e.Argument, 100)));
}
private bool EnableRefreshMessage(BaseItem item)
{
var folder = item as Folder;
if (folder == null)
{
return false;
}
if (folder.IsRoot)
{
return false;
}
if (folder is AggregateFolder || folder is UserRootFolder)
{
return false;
}
if (folder is UserView || folder is Channel)
{
return false;
}
if (!folder.IsTopParent)
{
return false;
}
return true;
} }
/// <summary> /// <summary>
@ -218,8 +328,8 @@ namespace Emby.Server.Implementations.EntryPoints
try try
{ {
info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo,
foldersRemovedFrom, id); foldersRemovedFrom, id);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -6,7 +6,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events; using MediaBrowser.Common.Events;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
@ -170,7 +170,7 @@ namespace Emby.Server.Implementations.IO
// If the root folder changed, run the library task so the user can see it // If the root folder changed, run the library task so the user can see it
if (itemsToRefresh.Any(i => i is AggregateFolder)) if (itemsToRefresh.Any(i => i is AggregateFolder))
{ {
LibraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); LibraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
return; return;
} }

View File

@ -462,7 +462,7 @@ namespace Emby.Server.Implementations.Library
if (parent != null) if (parent != null)
{ {
await parent.ValidateChildren(new Progress<double>(), CancellationToken.None, new MetadataRefreshOptions(_fileSystem), false).ConfigureAwait(false); await parent.ValidateChildren(new SimpleProgress<double>(), CancellationToken.None, new MetadataRefreshOptions(_fileSystem), false).ConfigureAwait(false);
} }
} }
else if (parent != null) else if (parent != null)
@ -1113,13 +1113,13 @@ namespace Emby.Server.Implementations.Library
progress.Report(.5); progress.Report(.5);
// Start by just validating the children of the root, but go no further // Start by just validating the children of the root, but go no further
await RootFolder.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false); await RootFolder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false);
progress.Report(1); progress.Report(1);
await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
await GetUserRootFolder().ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); await GetUserRootFolder().ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false);
progress.Report(2); progress.Report(2);
// Quickly scan CollectionFolders for changes // Quickly scan CollectionFolders for changes
@ -1204,25 +1204,24 @@ namespace Emby.Server.Implementations.Library
/// Gets the default view. /// Gets the default view.
/// </summary> /// </summary>
/// <returns>IEnumerable{VirtualFolderInfo}.</returns> /// <returns>IEnumerable{VirtualFolderInfo}.</returns>
public IEnumerable<VirtualFolderInfo> GetVirtualFolders() public List<VirtualFolderInfo> GetVirtualFolders()
{ {
return GetView(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath); return GetVirtualFolders(false);
} }
/// <summary> public List<VirtualFolderInfo> GetVirtualFolders(bool includeRefreshState)
/// Gets the view.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>IEnumerable{VirtualFolderInfo}.</returns>
private IEnumerable<VirtualFolderInfo> GetView(string path)
{ {
var topLibraryFolders = GetUserRootFolder().Children.ToList(); var topLibraryFolders = GetUserRootFolder().Children.ToList();
return _fileSystem.GetDirectoryPaths(path) var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null;
.Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders));
return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath)
.Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue))
.OrderBy(i => i.Name)
.ToList();
} }
private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders) private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders, Dictionary<Guid, Guid> refreshQueue)
{ {
var info = new VirtualFolderInfo var info = new VirtualFolderInfo
{ {
@ -1248,6 +1247,13 @@ namespace Emby.Server.Implementations.Library
{ {
info.ItemId = libraryFolder.Id.ToString("N"); info.ItemId = libraryFolder.Id.ToString("N");
info.LibraryOptions = GetLibraryOptions(libraryFolder); info.LibraryOptions = GetLibraryOptions(libraryFolder);
if (refreshQueue != null)
{
info.RefreshProgress = libraryFolder.GetRefreshProgress();
info.RefreshStatus = info.RefreshProgress.HasValue ? "Active" : refreshQueue.ContainsKey(libraryFolder.Id) ? "Queued" : "Idle";
}
} }
return info; return info;
@ -2947,7 +2953,7 @@ namespace Emby.Server.Implementations.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (refreshLibrary) if (refreshLibrary)
{ {
ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
} }
else else
{ {
@ -3075,7 +3081,7 @@ namespace Emby.Server.Implementations.Library
private void SyncLibraryOptionsToLocations(string virtualFolderPath, LibraryOptions options) private void SyncLibraryOptionsToLocations(string virtualFolderPath, LibraryOptions options)
{ {
var topLibraryFolders = GetUserRootFolder().Children.ToList(); var topLibraryFolders = GetUserRootFolder().Children.ToList();
var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders); var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders, null);
if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length) if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length)
{ {
@ -3125,7 +3131,7 @@ namespace Emby.Server.Implementations.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (refreshLibrary) if (refreshLibrary)
{ {
ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
} }
else else
{ {

View File

@ -55,10 +55,6 @@ namespace Emby.Server.Implementations.Library.Validators
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{ {
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(pct => progress.Report(pct * .15));
var people = _libraryManager.GetPeople(new InternalPeopleQuery()); var people = _libraryManager.GetPeople(new InternalPeopleQuery());
var dict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); var dict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

View File

@ -1,57 +0,0 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Emby.Server.Implementations.Library.Validators
{
public class YearsPostScanTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
public YearsPostScanTask(ILibraryManager libraryManager, ILogger logger)
{
_libraryManager = libraryManager;
_logger = logger;
}
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var yearNumber = 1900;
var maxYear = DateTime.UtcNow.Year + 3;
var count = maxYear - yearNumber + 1;
var numComplete = 0;
while (yearNumber < maxYear)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var year = _libraryManager.GetYear(yearNumber);
await year.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
{
_logger.ErrorException("Error refreshing year {0}", ex, yearNumber);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
yearNumber++;
}
}
}
}

View File

@ -28,7 +28,7 @@ using System.Xml;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events; using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -240,7 +240,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (requiresRefresh) if (requiresRefresh)
{ {
_libraryManager.ValidateMediaLibrary(new Progress<Double>(), CancellationToken.None); _libraryManager.ValidateMediaLibrary(new SimpleProgress<Double>(), CancellationToken.None);
} }
} }

View File

@ -15,6 +15,7 @@ using Emby.XmlTv.Classes;
using Emby.XmlTv.Entities; using Emby.XmlTv.Entities;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -75,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{ {
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
Url = path, Url = path,
Progress = new Progress<Double>(), Progress = new SimpleProgress<Double>(),
DecompressionMethod = CompressionMethod.Gzip, DecompressionMethod = CompressionMethod.Gzip,
// It's going to come back gzipped regardless of this value // It's going to come back gzipped regardless of this value

View File

@ -1273,8 +1273,8 @@ namespace Emby.Server.Implementations.LiveTv
if (coreService != null) if (coreService != null)
{ {
await coreService.RefreshSeriesTimers(cancellationToken, new Progress<double>()).ConfigureAwait(false); await coreService.RefreshSeriesTimers(cancellationToken, new SimpleProgress<double>()).ConfigureAwait(false);
await coreService.RefreshTimers(cancellationToken, new Progress<double>()).ConfigureAwait(false); await coreService.RefreshTimers(cancellationToken, new SimpleProgress<double>()).ConfigureAwait(false);
} }
// Load these now which will prefetch metadata // Load these now which will prefetch metadata
@ -1549,7 +1549,7 @@ namespace Emby.Server.Implementations.LiveTv
var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false); var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false);
await CleanDatabaseInternal(idList.ToList(), new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new Progress<double>(), cancellationToken).ConfigureAwait(false); await CleanDatabaseInternal(idList.ToList(), new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
_lastRecordingRefreshTime = DateTime.UtcNow; _lastRecordingRefreshTime = DateTime.UtcNow;
} }

View File

@ -15,6 +15,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Threading; using MediaBrowser.Model.Threading;
@ -82,7 +83,7 @@ namespace Emby.Server.Implementations.News
var requestOptions = new HttpRequestOptions var requestOptions = new HttpRequestOptions
{ {
Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog", Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog",
Progress = new Progress<double>(), Progress = new SimpleProgress<double>(),
UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.42 Safari/537.36", UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.42 Safari/537.36",
BufferContent = false BufferContent = false
}; };

View File

@ -23,7 +23,6 @@ namespace Emby.Server.Implementations.Notifications
public void Run() public void Run()
{ {
_notificationsRepo.NotificationAdded += _notificationsRepo_NotificationAdded; _notificationsRepo.NotificationAdded += _notificationsRepo_NotificationAdded;
_notificationsRepo.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead; _notificationsRepo.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead;
} }

View File

@ -8,6 +8,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks namespace Emby.Server.Implementations.ScheduledTasks
@ -77,7 +78,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
try try
{ {
await _installationManager.InstallPackage(i, true, new Progress<double>(), cancellationToken).ConfigureAwait(false); await _installationManager.InstallPackage(i, true, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
namespace Emby.Server.Implementations.ScheduledTasks namespace Emby.Server.Implementations.ScheduledTasks
@ -70,7 +71,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
EventHandler<double> innerProgressHandler = (sender, e) => progress.Report(e * .1); EventHandler<double> innerProgressHandler = (sender, e) => progress.Report(e * .1);
// Create a progress object for the update check // Create a progress object for the update check
var innerProgress = new Progress<double>(); var innerProgress = new SimpleProgress<double>();
innerProgress.ProgressChanged += innerProgressHandler; innerProgress.ProgressChanged += innerProgressHandler;
var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false); var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false);
@ -97,7 +98,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
innerProgressHandler = (sender, e) => progress.Report(e * .9 + .1); innerProgressHandler = (sender, e) => progress.Report(e * .9 + .1);
innerProgress = new Progress<double>(); innerProgress = new SimpleProgress<double>();
innerProgress.ProgressChanged += innerProgressHandler; innerProgress.ProgressChanged += innerProgressHandler;
await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, innerProgress).ConfigureAwait(false); await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, innerProgress).ConfigureAwait(false);

View File

@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.Updates
{ {
Url = "https://www.mb3admin.com/admin/service/MB3Packages.json", Url = "https://www.mb3admin.com/admin/service/MB3Packages.json",
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
Progress = new Progress<Double>() Progress = new SimpleProgress<Double>()
}).ConfigureAwait(false); }).ConfigureAwait(false);

View File

@ -65,7 +65,7 @@ namespace MediaBrowser.Api
_providerManager.QueueRefresh(item.Id, options, RefreshPriority.High); _providerManager.QueueRefresh(item.Id, options, RefreshPriority.High);
} }
private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request) private MetadataRefreshOptions GetRefreshOptions(RefreshItem request)
{ {
return new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) return new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
{ {
@ -73,8 +73,9 @@ namespace MediaBrowser.Api
ImageRefreshMode = request.ImageRefreshMode, ImageRefreshMode = request.ImageRefreshMode,
ReplaceAllImages = request.ReplaceAllImages, ReplaceAllImages = request.ReplaceAllImages,
ReplaceAllMetadata = request.ReplaceAllMetadata, ReplaceAllMetadata = request.ReplaceAllMetadata,
ForceSave = true, ForceSave = request.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || request.ImageRefreshMode == ImageRefreshMode.FullRefresh || request.ReplaceAllImages || request.ReplaceAllMetadata,
IsAutomated = false IsAutomated = false,
ValidateChildren = request.Recursive
}; };
} }
} }

View File

@ -28,6 +28,7 @@ using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
namespace MediaBrowser.Api.Library namespace MediaBrowser.Api.Library
{ {
@ -445,7 +446,7 @@ namespace MediaBrowser.Api.Library
} }
else else
{ {
Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)); Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None));
} }
} }
@ -483,7 +484,7 @@ namespace MediaBrowser.Api.Library
} }
else else
{ {
Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)); Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None));
} }
} }
@ -696,7 +697,7 @@ namespace MediaBrowser.Api.Library
{ {
try try
{ {
_libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -8,7 +8,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -208,7 +208,7 @@ namespace MediaBrowser.Api.Library
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object Get(GetVirtualFolders request) public object Get(GetVirtualFolders request)
{ {
var result = _libraryManager.GetVirtualFolders().OrderBy(i => i.Name).ToList(); var result = _libraryManager.GetVirtualFolders(true);
return ToOptimizedSerializedResultUsingCache(result); return ToOptimizedSerializedResultUsingCache(result);
} }
@ -290,7 +290,7 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (request.RefreshLibrary) if (request.RefreshLibrary)
{ {
_libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
} }
else else
{ {
@ -347,7 +347,7 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (request.RefreshLibrary) if (request.RefreshLibrary)
{ {
_libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
} }
else else
{ {
@ -400,7 +400,7 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (request.RefreshLibrary) if (request.RefreshLibrary)
{ {
_libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
} }
else else
{ {

View File

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
namespace MediaBrowser.Api namespace MediaBrowser.Api
@ -156,7 +157,7 @@ namespace MediaBrowser.Api
else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
{ {
var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new Progress<double>()).Result; var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress<double>()).Result;
if (updateCheckResult.IsUpdateAvailable) if (updateCheckResult.IsUpdateAvailable)
{ {
@ -233,7 +234,7 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name)); throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
} }
Task.Run(() => _installationManager.InstallPackage(package, true, new Progress<double>(), CancellationToken.None)); Task.Run(() => _installationManager.InstallPackage(package, true, new SimpleProgress<double>(), CancellationToken.None));
} }
/// <summary> /// <summary>

View File

@ -7,12 +7,13 @@ namespace MediaBrowser.Common.Progress
/// Class ActionableProgress /// Class ActionableProgress
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class ActionableProgress<T> : Progress<T>, IDisposable public class ActionableProgress<T> : IProgress<T>, IDisposable
{ {
/// <summary> /// <summary>
/// The _actions /// The _actions
/// </summary> /// </summary>
private readonly List<Action<T>> _actions = new List<Action<T>>(); private readonly List<Action<T>> _actions = new List<Action<T>>();
public event EventHandler<T> ProgressChanged;
/// <summary> /// <summary>
/// Registers the action. /// Registers the action.
@ -21,22 +22,6 @@ namespace MediaBrowser.Common.Progress
public void RegisterAction(Action<T> action) public void RegisterAction(Action<T> action)
{ {
_actions.Add(action); _actions.Add(action);
ProgressChanged -= ActionableProgress_ProgressChanged;
ProgressChanged += ActionableProgress_ProgressChanged;
}
/// <summary>
/// Actionables the progress_ progress changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
void ActionableProgress_ProgressChanged(object sender, T e)
{
foreach (var action in _actions)
{
action(e);
}
} }
/// <summary> /// <summary>
@ -55,9 +40,34 @@ namespace MediaBrowser.Common.Progress
{ {
if (disposing) if (disposing)
{ {
ProgressChanged -= ActionableProgress_ProgressChanged;
_actions.Clear(); _actions.Clear();
} }
} }
public void Report(T value)
{
if (ProgressChanged != null)
{
ProgressChanged(this, value);
}
foreach (var action in _actions)
{
action(value);
}
}
}
public class SimpleProgress<T> : IProgress<T>
{
public event EventHandler<T> ProgressChanged;
public void Report(T value)
{
if (ProgressChanged != null)
{
ProgressChanged(this, value);
}
}
} }
} }

View File

@ -6,6 +6,7 @@ using System.Linq;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
namespace MediaBrowser.Controller.Channels namespace MediaBrowser.Controller.Channels
{ {
@ -51,7 +52,7 @@ namespace MediaBrowser.Controller.Channels
SortBy = query.SortBy, SortBy = query.SortBy,
SortOrder = query.SortOrder SortOrder = query.SortOrder
}, new Progress<double>(), CancellationToken.None).Result; }, new SimpleProgress<double>(), CancellationToken.None).Result;
} }
catch catch
{ {

View File

@ -10,7 +10,8 @@ namespace MediaBrowser.Controller.Dto
{ {
private static readonly List<ItemFields> DefaultExcludedFields = new List<ItemFields> private static readonly List<ItemFields> DefaultExcludedFields = new List<ItemFields>
{ {
ItemFields.SeasonUserData ItemFields.SeasonUserData,
ItemFields.RefreshState
}; };
public List<ItemFields> Fields { get; set; } public List<ItemFields> Fields { get; set; }

View File

@ -235,8 +235,6 @@ namespace MediaBrowser.Controller.Entities.Audio
{ {
await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false); await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false);
} }
progress.Report(100);
} }
private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)

View File

@ -250,8 +250,6 @@ namespace MediaBrowser.Controller.Entities.Audio
percent /= totalItems; percent /= totalItems;
progress.Report(percent * 100); progress.Report(percent * 100);
} }
progress.Report(100);
} }
public ArtistInfo GetLookupInfo() public ArtistInfo GetLookupInfo()

View File

@ -1058,6 +1058,16 @@ namespace MediaBrowser.Controller.Entities
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken); return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken);
} }
protected virtual void TriggerOnRefreshStart()
{
}
protected virtual void TriggerOnRefreshComplete()
{
}
/// <summary> /// <summary>
/// Overrides the base implementation to refresh metadata for local trailers /// Overrides the base implementation to refresh metadata for local trailers
/// </summary> /// </summary>
@ -1066,6 +1076,8 @@ namespace MediaBrowser.Controller.Entities
/// <returns>true if a provider reports we changed</returns> /// <returns>true if a provider reports we changed</returns>
public async Task<ItemUpdateType> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken) public async Task<ItemUpdateType> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
{ {
TriggerOnRefreshStart();
var locationType = LocationType; var locationType = LocationType;
var requiresSave = false; var requiresSave = false;
@ -1091,14 +1103,21 @@ namespace MediaBrowser.Controller.Entities
} }
} }
var refreshOptions = requiresSave try
? new MetadataRefreshOptions(options) {
{ var refreshOptions = requiresSave
ForceSave = true ? new MetadataRefreshOptions(options)
} {
: options; ForceSave = true
}
: options;
return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
}
finally
{
TriggerOnRefreshComplete();
}
} }
[IgnoreDataMember] [IgnoreDataMember]
@ -2421,5 +2440,10 @@ namespace MediaBrowser.Controller.Entities
{ {
return new List<ExternalUrl>(); return new List<ExternalUrl>();
} }
public virtual double? GetRefreshProgress()
{
return null;
}
} }
} }

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
@ -199,6 +200,30 @@ namespace MediaBrowser.Controller.Entities
return changed; return changed;
} }
public override double? GetRefreshProgress()
{
var folders = GetPhysicalFolders(true).ToList();
double totalProgresses = 0;
var foldersWithProgress = 0;
foreach (var folder in folders)
{
var progress = ProviderManager.GetRefreshProgress(folder.Id);
if (progress.HasValue)
{
totalProgresses += progress.Value;
foldersWithProgress++;
}
}
if (foldersWithProgress == 0)
{
return null;
}
return (totalProgresses / foldersWithProgress);
}
protected override bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren) protected override bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
{ {
return RefreshLinkedChildrenInternal(true); return RefreshLinkedChildrenInternal(true);
@ -321,6 +346,11 @@ namespace MediaBrowser.Controller.Entities
return GetPhysicalFolders(true).SelectMany(c => c.Children); return GetPhysicalFolders(true).SelectMany(c => c.Children);
} }
public IEnumerable<Folder> GetPhysicalFolders()
{
return GetPhysicalFolders(true);
}
private IEnumerable<Folder> GetPhysicalFolders(bool enableCache) private IEnumerable<Folder> GetPhysicalFolders(bool enableCache)
{ {
if (enableCache) if (enableCache)

View File

@ -271,6 +271,11 @@ namespace MediaBrowser.Controller.Entities
return GetCachedChildren(); return GetCachedChildren();
} }
public override double? GetRefreshProgress()
{
return ProviderManager.GetRefreshProgress(Id);
}
public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken) public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
{ {
return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem))); return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)));
@ -318,6 +323,14 @@ namespace MediaBrowser.Controller.Entities
return current.IsValidFromResolver(newItem); return current.IsValidFromResolver(newItem);
} }
protected override void TriggerOnRefreshStart()
{
}
protected override void TriggerOnRefreshComplete()
{
}
/// <summary> /// <summary>
/// Validates the children internal. /// Validates the children internal.
/// </summary> /// </summary>
@ -328,7 +341,27 @@ namespace MediaBrowser.Controller.Entities
/// <param name="refreshOptions">The refresh options.</param> /// <param name="refreshOptions">The refresh options.</param>
/// <param name="directoryService">The directory service.</param> /// <param name="directoryService">The directory service.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
protected async virtual Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) protected virtual async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
if (recursive)
{
ProviderManager.OnRefreshStart(this);
}
try
{
await ValidateChildrenInternal2(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService).ConfigureAwait(false);
}
finally
{
if (recursive)
{
ProviderManager.OnRefreshComplete(this);
}
}
}
private async Task ValidateChildrenInternal2(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{ {
var locationType = LocationType; var locationType = LocationType;
@ -360,6 +393,11 @@ namespace MediaBrowser.Controller.Entities
progress.Report(5); progress.Report(5);
if (recursive)
{
ProviderManager.OnRefreshProgress(this, 5);
}
//build a dictionary of the current children we have now by Id so we can compare quickly and easily //build a dictionary of the current children we have now by Id so we can compare quickly and easily
var currentChildren = GetActualChildrenDictionary(); var currentChildren = GetActualChildrenDictionary();
@ -424,76 +462,99 @@ namespace MediaBrowser.Controller.Entities
await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false);
} }
} }
else
{
if (recursive || refreshChildMetadata)
{
// used below
validChildren = Children.ToList();
}
}
progress.Report(10); progress.Report(10);
if (recursive)
{
ProviderManager.OnRefreshProgress(this, 10);
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
if (recursive) if (recursive)
{ {
await ValidateSubFolders(Children.ToList().OfType<Folder>().ToList(), directoryService, progress, cancellationToken).ConfigureAwait(false); using (var innerProgress = new ActionableProgress<double>())
} {
var folder = this;
innerProgress.RegisterAction(p =>
{
double newPct = .70 * p + 10;
progress.Report(newPct);
ProviderManager.OnRefreshProgress(folder, newPct);
});
progress.Report(20); await ValidateSubFolders(validChildren.OfType<Folder>().ToList(), directoryService, innerProgress, cancellationToken).ConfigureAwait(false);
}
}
if (refreshChildMetadata) if (refreshChildMetadata)
{ {
progress.Report(80);
if (recursive)
{
ProviderManager.OnRefreshProgress(this, 80);
}
var container = this as IMetadataContainer; var container = this as IMetadataContainer;
var innerProgress = new ActionableProgress<double>(); using (var innerProgress = new ActionableProgress<double>())
innerProgress.RegisterAction(p => progress.Report(.80 * p + 20));
if (container != null)
{ {
await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); var folder = this;
} innerProgress.RegisterAction(p =>
else {
{ double newPct = .20 * p + 80;
await RefreshMetadataRecursive(refreshOptions, recursive, innerProgress, cancellationToken); progress.Report(newPct);
if (recursive)
{
ProviderManager.OnRefreshProgress(folder, newPct);
}
});
if (container != null)
{
await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false);
}
else
{
await RefreshMetadataRecursive(validChildren, refreshOptions, recursive, innerProgress, cancellationToken);
}
} }
} }
progress.Report(100);
} }
private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken) private async Task RefreshMetadataRecursive(List<BaseItem> children, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
{ {
var children = Children.ToList();
var percentages = new Dictionary<Guid, double>(children.Count);
var numComplete = 0; var numComplete = 0;
var count = children.Count; var count = children.Count;
double currentPercent = 0;
foreach (var child in children) foreach (var child in children)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
if (child.IsFolder) using (var innerProgress = new ActionableProgress<double>())
{ {
var innerProgress = new ActionableProgress<double>();
// Avoid implicitly captured closure // Avoid implicitly captured closure
var currentChild = child; var currentInnerPercent = currentPercent;
innerProgress.RegisterAction(p => innerProgress.RegisterAction(p =>
{ {
lock (percentages) double innerPercent = currentInnerPercent;
{ innerPercent += p / (count);
percentages[currentChild.Id] = p / 100; progress.Report(innerPercent);
var innerPercent = percentages.Values.Sum();
innerPercent /= count;
innerPercent *= 100;
progress.Report(innerPercent);
}
}); });
await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken) await RefreshChildMetadata(child, refreshOptions, recursive && child.IsFolder, innerProgress, cancellationToken)
.ConfigureAwait(false);
}
else
{
await RefreshChildMetadata(child, refreshOptions, false, new Progress<double>(), cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -501,11 +562,10 @@ namespace MediaBrowser.Controller.Entities
double percent = numComplete; double percent = numComplete;
percent /= count; percent /= count;
percent *= 100; percent *= 100;
currentPercent = percent;
progress.Report(percent); progress.Report(percent);
} }
progress.Report(100);
} }
private async Task RefreshChildMetadata(BaseItem child, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken) private async Task RefreshChildMetadata(BaseItem child, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
@ -526,11 +586,10 @@ namespace MediaBrowser.Controller.Entities
if (folder != null) if (folder != null)
{ {
await folder.RefreshMetadataRecursive(refreshOptions, true, progress, cancellationToken); await folder.RefreshMetadataRecursive(folder.Children.ToList(), refreshOptions, true, progress, cancellationToken);
} }
} }
} }
progress.Report(100);
} }
/// <summary> /// <summary>
@ -543,47 +602,40 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task ValidateSubFolders(IList<Folder> children, IDirectoryService directoryService, IProgress<double> progress, CancellationToken cancellationToken) private async Task ValidateSubFolders(IList<Folder> children, IDirectoryService directoryService, IProgress<double> progress, CancellationToken cancellationToken)
{ {
var list = children; var numComplete = 0;
var childCount = list.Count; var count = children.Count;
double currentPercent = 0;
var percentages = new Dictionary<Guid, double>(list.Count); foreach (var child in children)
foreach (var item in list)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var child = item; using (var innerProgress = new ActionableProgress<double>())
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p =>
{ {
lock (percentages) // Avoid implicitly captured closure
var currentInnerPercent = currentPercent;
innerProgress.RegisterAction(p =>
{ {
percentages[child.Id] = p / 100; double innerPercent = currentInnerPercent;
innerPercent += p / (count);
progress.Report(innerPercent);
});
var percent = percentages.Values.Sum(); await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService)
percent /= childCount; .ConfigureAwait(false);
}
progress.Report(10 * percent + 10); numComplete++;
} double percent = numComplete;
}); percent /= count;
percent *= 100;
currentPercent = percent;
await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService) progress.Report(percent);
.ConfigureAwait(false);
} }
} }
/// <summary>
/// Determines whether the specified path is offline.
/// </summary>
/// <param name="path">The path.</param>
/// <returns><c>true</c> if the specified path is offline; otherwise, <c>false</c>.</returns>
public static bool IsPathOffline(string path)
{
return IsPathOffline(path, LibraryManager.GetVirtualFolders().SelectMany(i => i.Locations).ToList());
}
public static bool IsPathOffline(string path, List<string> allLibraryPaths) public static bool IsPathOffline(string path, List<string> allLibraryPaths)
{ {
if (FileSystem.FileExists(path)) if (FileSystem.FileExists(path))
@ -926,7 +978,7 @@ namespace MediaBrowser.Controller.Entities
SortBy = query.SortBy, SortBy = query.SortBy,
SortOrder = query.SortOrder SortOrder = query.SortOrder
}, new Progress<double>(), CancellationToken.None).Result; }, new SimpleProgress<double>(), CancellationToken.None).Result;
} }
catch catch
{ {

View File

@ -414,8 +414,6 @@ namespace MediaBrowser.Controller.Entities.TV
refreshOptions = new MetadataRefreshOptions(refreshOptions); refreshOptions = new MetadataRefreshOptions(refreshOptions);
refreshOptions.IsPostRecursiveRefresh = true; refreshOptions.IsPostRecursiveRefresh = true;
await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
progress.Report(100);
} }
public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options) public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options)

View File

@ -132,7 +132,9 @@ namespace MediaBrowser.Controller.Library
/// Gets the default view. /// Gets the default view.
/// </summary> /// </summary>
/// <returns>IEnumerable{VirtualFolderInfo}.</returns> /// <returns>IEnumerable{VirtualFolderInfo}.</returns>
IEnumerable<VirtualFolderInfo> GetVirtualFolders(); List<VirtualFolderInfo> GetVirtualFolders();
List<VirtualFolderInfo> GetVirtualFolders(bool includeRefreshState);
/// <summary> /// <summary>
/// Gets the item by id. /// Gets the item by id.

View File

@ -320,7 +320,6 @@
<Compile Include="Plugins\IPluginConfigurationPage.cs" /> <Compile Include="Plugins\IPluginConfigurationPage.cs" />
<Compile Include="Plugins\IServerEntryPoint.cs" /> <Compile Include="Plugins\IServerEntryPoint.cs" />
<Compile Include="Providers\IImageEnhancer.cs" /> <Compile Include="Providers\IImageEnhancer.cs" />
<Compile Include="Providers\ProviderRefreshStatus.cs" />
<Compile Include="Resolvers\IResolverIgnoreRule.cs" /> <Compile Include="Resolvers\IResolverIgnoreRule.cs" />
<Compile Include="Resolvers\ResolverPriority.cs" /> <Compile Include="Resolvers\ResolverPriority.cs" />
<Compile Include="Library\TVUtils.cs" /> <Compile Include="Library\TVUtils.cs" />

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Events;
namespace MediaBrowser.Controller.Providers namespace MediaBrowser.Controller.Providers
{ {
@ -30,7 +31,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task RefreshFullItem(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken); Task RefreshFullItem(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Refreshes the metadata. /// Refreshes the metadata.
/// </summary> /// </summary>
@ -68,7 +69,7 @@ namespace MediaBrowser.Controller.Providers
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken); Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Adds the metadata providers. /// Adds the metadata providers.
/// </summary> /// </summary>
@ -128,7 +129,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="savers">The savers.</param> /// <param name="savers">The savers.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveMetadata(IHasMetadata item, ItemUpdateType updateType, IEnumerable<string> savers); Task SaveMetadata(IHasMetadata item, ItemUpdateType updateType, IEnumerable<string> savers);
/// <summary> /// <summary>
/// Gets the metadata options. /// Gets the metadata options.
/// </summary> /// </summary>
@ -158,6 +159,18 @@ namespace MediaBrowser.Controller.Providers
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{HttpResponseInfo}.</returns> /// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken); Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken);
Dictionary<Guid, Guid> GetRefreshQueue();
void OnRefreshStart(BaseItem item);
void OnRefreshProgress(BaseItem item, double progress);
void OnRefreshComplete(BaseItem item);
double? GetRefreshProgress(Guid id);
event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
} }
public enum RefreshPriority public enum RefreshPriority

View File

@ -1,22 +0,0 @@

namespace MediaBrowser.Controller.Providers
{
/// <summary>
/// Enum ProviderRefreshStatus
/// </summary>
public enum ProviderRefreshStatus
{
/// <summary>
/// The success
/// </summary>
Success = 0,
/// <summary>
/// The completed with errors
/// </summary>
CompletedWithErrors = 1,
/// <summary>
/// The failure
/// </summary>
Failure = 2
}
}

View File

@ -7,6 +7,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -62,7 +63,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Kick this off, but no need to wait on it // Kick this off, but no need to wait on it
var task = Task.Run(async () => var task = Task.Run(async () =>
{ {
await DownloadFontFile(fontsDirectory, fontFilename, new Progress<double>()).ConfigureAwait(false); await DownloadFontFile(fontsDirectory, fontFilename, new SimpleProgress<double>()).ConfigureAwait(false);
await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
}); });

View File

@ -47,5 +47,8 @@ namespace MediaBrowser.Model.Entities
/// </summary> /// </summary>
/// <value>The primary image item identifier.</value> /// <value>The primary image item identifier.</value>
public string PrimaryImageItemId { get; set; } public string PrimaryImageItemId { get; set; }
public double? RefreshProgress { get; set; }
public string RefreshStatus { get; set; }
} }
} }

View File

@ -228,6 +228,7 @@
ExternalSeriesId, ExternalSeriesId,
SeriesPresentationUniqueKey, SeriesPresentationUniqueKey,
DateLastRefreshed, DateLastRefreshed,
DateLastSaved DateLastSaved,
RefreshState
} }
} }

View File

@ -6,6 +6,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.ImagesByName namespace MediaBrowser.Providers.ImagesByName
@ -30,7 +31,7 @@ namespace MediaBrowser.Providers.ImagesByName
var temp = await httpClient.GetTempFile(new HttpRequestOptions var temp = await httpClient.GetTempFile(new HttpRequestOptions
{ {
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
Progress = new Progress<double>(), Progress = new SimpleProgress<double>(),
Url = url Url = url
}).ConfigureAwait(false); }).ConfigureAwait(false);

View File

@ -18,8 +18,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using Priority_Queue; using Priority_Queue;
@ -67,6 +69,10 @@ namespace MediaBrowser.Providers.Manager
private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly IMemoryStreamFactory _memoryStreamProvider;
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class. /// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// </summary> /// </summary>
@ -849,6 +855,89 @@ namespace MediaBrowser.Providers.Manager
}); });
} }
private Dictionary<Guid, double> _activeRefreshes = new Dictionary<Guid, double>();
public Dictionary<Guid, Guid> GetRefreshQueue()
{
lock (_refreshQueueLock)
{
var dict = new Dictionary<Guid, Guid>();
foreach (var item in _refreshQueue)
{
dict[item.Item1] = item.Item1;
}
return dict;
}
}
public void OnRefreshStart(BaseItem item)
{
//_logger.Info("OnRefreshStart {0}", item.Id.ToString("N"));
var id = item.Id;
lock (_activeRefreshes)
{
_activeRefreshes[id] = 0;
}
if (RefreshStarted != null)
{
RefreshStarted(this, new GenericEventArgs<BaseItem>(item));
}
}
public void OnRefreshComplete(BaseItem item)
{
//_logger.Info("OnRefreshComplete {0}", item.Id.ToString("N"));
lock (_activeRefreshes)
{
_activeRefreshes.Remove(item.Id);
}
if (RefreshCompleted != null)
{
RefreshCompleted(this, new GenericEventArgs<BaseItem>(item));
}
}
public double? GetRefreshProgress(Guid id)
{
lock (_activeRefreshes)
{
double value;
if (_activeRefreshes.TryGetValue(id, out value))
{
return value;
}
return null;
}
}
public void OnRefreshProgress(BaseItem item, double progress)
{
//_logger.Info("OnRefreshProgress {0} {1}", item.Id.ToString("N"), progress);
var id = item.Id;
lock (_activeRefreshes)
{
if (_activeRefreshes.ContainsKey(id))
{
_activeRefreshes[id] = progress;
if (RefreshProgress != null)
{
RefreshProgress(this, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(item, progress)));
}
}
else
{
throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N")));
}
}
}
private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue =
new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>(); new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>();
@ -906,7 +995,7 @@ namespace MediaBrowser.Providers.Manager
var folder = item as Folder; var folder = item as Folder;
if (folder != null) if (folder != null)
{ {
await folder.ValidateChildren(new Progress<double>(), cancellationToken).ConfigureAwait(false); await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
} }
} }
@ -951,14 +1040,14 @@ namespace MediaBrowser.Providers.Manager
if (folder != null) if (folder != null)
{ {
await folder.ValidateChildren(new Progress<double>(), cancellationToken, options).ConfigureAwait(false); await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options).ConfigureAwait(false);
} }
} }
} }
private async Task RefreshCollectionFolderChildren(MetadataRefreshOptions options, CollectionFolder collectionFolder, CancellationToken cancellationToken) private async Task RefreshCollectionFolderChildren(MetadataRefreshOptions options, CollectionFolder collectionFolder, CancellationToken cancellationToken)
{ {
foreach (var child in collectionFolder.Children.ToList()) foreach (var child in collectionFolder.GetPhysicalFolders().ToList())
{ {
await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
@ -966,7 +1055,7 @@ namespace MediaBrowser.Providers.Manager
{ {
var folder = (Folder)child; var folder = (Folder)child;
await folder.ValidateChildren(new Progress<double>(), cancellationToken, options, true).ConfigureAwait(false); await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options, true).ConfigureAwait(false);
} }
} }
} }
@ -991,7 +1080,7 @@ namespace MediaBrowser.Providers.Manager
.Where(i => i != null) .Where(i => i != null)
.ToList(); .ToList();
var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new Progress<double>(), cancellationToken, options, true)); var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options, true));
await Task.WhenAll(musicArtistRefreshTasks).ConfigureAwait(false); await Task.WhenAll(musicArtistRefreshTasks).ConfigureAwait(false);

View File

@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.TV
//await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false); //await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false);
//await series.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService)) //await series.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService))
// .ConfigureAwait(false); // .ConfigureAwait(false);
} }
} }

View File

@ -13,7 +13,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
@ -162,7 +162,7 @@ namespace MediaBrowser.Providers.TV
await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false); await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false);
await series.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService), true) await series.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService), true)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }