mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-03 06:18:28 +01:00
support individual library refreshing
This commit is contained in:
@@ -235,8 +235,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||
{
|
||||
await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
|
||||
|
||||
@@ -250,8 +250,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||
percent /= totalItems;
|
||||
progress.Report(percent * 100);
|
||||
}
|
||||
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
public ArtistInfo GetLookupInfo()
|
||||
|
||||
@@ -1058,6 +1058,16 @@ namespace MediaBrowser.Controller.Entities
|
||||
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual void TriggerOnRefreshStart()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void TriggerOnRefreshComplete()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the base implementation to refresh metadata for local trailers
|
||||
/// </summary>
|
||||
@@ -1066,6 +1076,8 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <returns>true if a provider reports we changed</returns>
|
||||
public async Task<ItemUpdateType> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
TriggerOnRefreshStart();
|
||||
|
||||
var locationType = LocationType;
|
||||
|
||||
var requiresSave = false;
|
||||
@@ -1091,14 +1103,21 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
var refreshOptions = requiresSave
|
||||
? new MetadataRefreshOptions(options)
|
||||
{
|
||||
ForceSave = true
|
||||
}
|
||||
: options;
|
||||
try
|
||||
{
|
||||
var refreshOptions = requiresSave
|
||||
? new MetadataRefreshOptions(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]
|
||||
@@ -2421,5 +2440,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
return new List<ExternalUrl>();
|
||||
}
|
||||
|
||||
public virtual double? GetRefreshProgress()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
||||
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
@@ -199,6 +200,30 @@ namespace MediaBrowser.Controller.Entities
|
||||
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)
|
||||
{
|
||||
return RefreshLinkedChildrenInternal(true);
|
||||
@@ -321,6 +346,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
return GetPhysicalFolders(true).SelectMany(c => c.Children);
|
||||
}
|
||||
|
||||
public IEnumerable<Folder> GetPhysicalFolders()
|
||||
{
|
||||
return GetPhysicalFolders(true);
|
||||
}
|
||||
|
||||
private IEnumerable<Folder> GetPhysicalFolders(bool enableCache)
|
||||
{
|
||||
if (enableCache)
|
||||
|
||||
@@ -271,6 +271,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
return GetCachedChildren();
|
||||
}
|
||||
|
||||
public override double? GetRefreshProgress()
|
||||
{
|
||||
return ProviderManager.GetRefreshProgress(Id);
|
||||
}
|
||||
|
||||
public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)));
|
||||
@@ -318,6 +323,14 @@ namespace MediaBrowser.Controller.Entities
|
||||
return current.IsValidFromResolver(newItem);
|
||||
}
|
||||
|
||||
protected override void TriggerOnRefreshStart()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void TriggerOnRefreshComplete()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the children internal.
|
||||
/// </summary>
|
||||
@@ -328,7 +341,27 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <param name="refreshOptions">The refresh options.</param>
|
||||
/// <param name="directoryService">The directory service.</param>
|
||||
/// <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;
|
||||
|
||||
@@ -360,6 +393,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
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
|
||||
var currentChildren = GetActualChildrenDictionary();
|
||||
|
||||
@@ -424,76 +462,99 @@ namespace MediaBrowser.Controller.Entities
|
||||
await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (recursive || refreshChildMetadata)
|
||||
{
|
||||
// used below
|
||||
validChildren = Children.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
progress.Report(10);
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
ProviderManager.OnRefreshProgress(this, 10);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
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)
|
||||
{
|
||||
progress.Report(80);
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
ProviderManager.OnRefreshProgress(this, 80);
|
||||
}
|
||||
|
||||
var container = this as IMetadataContainer;
|
||||
|
||||
var innerProgress = new ActionableProgress<double>();
|
||||
|
||||
innerProgress.RegisterAction(p => progress.Report(.80 * p + 20));
|
||||
|
||||
if (container != null)
|
||||
using (var innerProgress = new ActionableProgress<double>())
|
||||
{
|
||||
await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RefreshMetadataRecursive(refreshOptions, recursive, innerProgress, cancellationToken);
|
||||
var folder = this;
|
||||
innerProgress.RegisterAction(p =>
|
||||
{
|
||||
double newPct = .20 * p + 80;
|
||||
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 count = children.Count;
|
||||
double currentPercent = 0;
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (child.IsFolder)
|
||||
using (var innerProgress = new ActionableProgress<double>())
|
||||
{
|
||||
var innerProgress = new ActionableProgress<double>();
|
||||
|
||||
// Avoid implicitly captured closure
|
||||
var currentChild = child;
|
||||
var currentInnerPercent = currentPercent;
|
||||
|
||||
innerProgress.RegisterAction(p =>
|
||||
{
|
||||
lock (percentages)
|
||||
{
|
||||
percentages[currentChild.Id] = p / 100;
|
||||
|
||||
var innerPercent = percentages.Values.Sum();
|
||||
innerPercent /= count;
|
||||
innerPercent *= 100;
|
||||
progress.Report(innerPercent);
|
||||
}
|
||||
double innerPercent = currentInnerPercent;
|
||||
innerPercent += p / (count);
|
||||
progress.Report(innerPercent);
|
||||
});
|
||||
|
||||
await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RefreshChildMetadata(child, refreshOptions, false, new Progress<double>(), cancellationToken)
|
||||
await RefreshChildMetadata(child, refreshOptions, recursive && child.IsFolder, innerProgress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -501,11 +562,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
double percent = numComplete;
|
||||
percent /= count;
|
||||
percent *= 100;
|
||||
currentPercent = percent;
|
||||
|
||||
progress.Report(percent);
|
||||
}
|
||||
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
await folder.RefreshMetadataRecursive(refreshOptions, true, progress, cancellationToken);
|
||||
await folder.RefreshMetadataRecursive(folder.Children.ToList(), refreshOptions, true, progress, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -543,47 +602,40 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <returns>Task.</returns>
|
||||
private async Task ValidateSubFolders(IList<Folder> children, IDirectoryService directoryService, IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var list = children;
|
||||
var childCount = list.Count;
|
||||
var numComplete = 0;
|
||||
var count = children.Count;
|
||||
double currentPercent = 0;
|
||||
|
||||
var percentages = new Dictionary<Guid, double>(list.Count);
|
||||
|
||||
foreach (var item in list)
|
||||
foreach (var child in children)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var child = item;
|
||||
|
||||
var innerProgress = new ActionableProgress<double>();
|
||||
|
||||
innerProgress.RegisterAction(p =>
|
||||
using (var innerProgress = new ActionableProgress<double>())
|
||||
{
|
||||
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();
|
||||
percent /= childCount;
|
||||
await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService)
|
||||
.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)
|
||||
.ConfigureAwait(false);
|
||||
progress.Report(percent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (FileSystem.FileExists(path))
|
||||
@@ -926,7 +978,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
SortBy = query.SortBy,
|
||||
SortOrder = query.SortOrder
|
||||
|
||||
}, new Progress<double>(), CancellationToken.None).Result;
|
||||
}, new SimpleProgress<double>(), CancellationToken.None).Result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -414,8 +414,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
refreshOptions = new MetadataRefreshOptions(refreshOptions);
|
||||
refreshOptions.IsPostRecursiveRefresh = true;
|
||||
await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
progress.Report(100);
|
||||
}
|
||||
|
||||
public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options)
|
||||
|
||||
Reference in New Issue
Block a user