Only fire metadata savers when appropriate

pull/702/head
Luke Pulverenti 11 years ago
parent 12c6bc27f2
commit 92cd71143d

@ -283,8 +283,8 @@ namespace MediaBrowser.Api
var item = DtoBuilder.GetItemByClientId(request.ItemId, _userManager, _libraryManager);
UpdateItem(request, item);
return _libraryManager.UpdateItem(item, CancellationToken.None);
return _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None);
}
public void Post(UpdatePerson request)
@ -300,7 +300,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, CancellationToken.None).ConfigureAwait(false);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateArtist request)
@ -316,7 +316,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, CancellationToken.None).ConfigureAwait(false);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateStudio request)
@ -332,7 +332,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, CancellationToken.None).ConfigureAwait(false);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateMusicGenre request)
@ -348,7 +348,7 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, CancellationToken.None).ConfigureAwait(false);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
public void Post(UpdateGenre request)
@ -364,13 +364,19 @@ namespace MediaBrowser.Api
UpdateItem(request, item);
await _libraryManager.UpdateItem(item, CancellationToken.None).ConfigureAwait(false);
await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
}
private void UpdateItem(BaseItemDto request, BaseItem item)
{
item.Name = request.Name;
item.ForcedSortName = request.SortName;
// Only set the forced value if they changed it, or there's already one
if (!string.Equals(item.SortName, request.SortName) || !string.IsNullOrEmpty(item.ForcedSortName))
{
item.ForcedSortName = request.SortName;
}
item.DisplayMediaType = request.DisplayMediaType;
item.CommunityRating = request.CommunityRating;
item.HomePageUrl = request.HomePageUrl;

@ -389,6 +389,11 @@ namespace MediaBrowser.Controller.Dto
dto.SortName = item.SortName;
}
if (fields.Contains(ItemFields.CustomRating))
{
dto.CustomRating = item.CustomRating;
}
if (fields.Contains(ItemFields.Taglines))
{
dto.Taglines = item.Taglines;

@ -834,12 +834,15 @@ namespace MediaBrowser.Controller.Entities
cancellationToken.ThrowIfCancellationRequested();
// Get the result from the item task
var changed = await itemRefreshTask.ConfigureAwait(false);
var updateReason = await itemRefreshTask.ConfigureAwait(false);
var changed = updateReason.HasValue;
if (changed || forceSave || themeSongsChanged || themeVideosChanged || localTrailersChanged)
{
cancellationToken.ThrowIfCancellationRequested();
await LibraryManager.UpdateItem(this, cancellationToken).ConfigureAwait(false);
await LibraryManager.UpdateItem(this, updateReason ?? ItemUpdateType.Unspecified, cancellationToken).ConfigureAwait(false);
}
return changed;

@ -315,7 +315,9 @@ namespace MediaBrowser.Controller.Entities
ResolveArgs = null;
}
var changed = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders).ConfigureAwait(false);
var updateReason = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders).ConfigureAwait(false);
var changed = updateReason.HasValue;
if (changed || forceSave)
{

@ -152,13 +152,15 @@ namespace MediaBrowser.Controller.Library
/// <param name="itemComparers">The item comparers.</param>
/// <param name="prescanTasks">The prescan tasks.</param>
/// <param name="postscanTasks">The postscan tasks.</param>
/// <param name="savers">The savers.</param>
void AddParts(IEnumerable<IResolverIgnoreRule> rules,
IEnumerable<IVirtualFolderCreator> pluginFolders,
IEnumerable<IItemResolver> resolvers,
IEnumerable<IIntroProvider> introProviders,
IEnumerable<IBaseItemComparer> itemComparers,
IEnumerable<ILibraryPrescanTask> prescanTasks,
IEnumerable<ILibraryPostScanTask> postscanTasks);
IEnumerable<ILibraryPostScanTask> postscanTasks,
IEnumerable<IMetadataSaver> savers);
/// <summary>
/// Sorts the specified items.
@ -205,9 +207,10 @@ namespace MediaBrowser.Controller.Library
/// Updates the item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task UpdateItem(BaseItem item, CancellationToken cancellationToken);
Task UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the item.

@ -0,0 +1,13 @@
using System;
namespace MediaBrowser.Controller.Library
{
[Flags]
public enum ItemUpdateType
{
Unspecified = 1,
MetadataImport = 2,
ImageUpdate = 4,
MetadataEdit = 16
}
}

@ -81,6 +81,7 @@
<Compile Include="Library\ILibraryPostScanTask.cs" />
<Compile Include="Library\ILibraryPrescanTask.cs" />
<Compile Include="Library\IMetadataSaver.cs" />
<Compile Include="Library\ItemUpdateType.cs" />
<Compile Include="Localization\ILocalizationManager.cs" />
<Compile Include="Reflection\TypeMapper.cs" />
<Compile Include="Session\ISessionManager.cs" />

@ -122,6 +122,17 @@ namespace MediaBrowser.Controller.Providers
item.DisplayMediaType = type;
}
break;
}
case "CriticRating":
{
var text = reader.ReadElementContentAsString();
float value;
if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
{
item.CriticRating = value;
}
break;
}
case "Budget":
@ -163,6 +174,18 @@ namespace MediaBrowser.Controller.Providers
break;
}
case "CriticRatingSummary":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
item.CriticRatingSummary = val;
}
break;
}
case "TagLine":
{
var tagline = reader.ReadElementContentAsString();

@ -75,6 +75,11 @@ namespace MediaBrowser.Controller.Providers
}
}
public virtual ItemUpdateType ItemUpdateType
{
get { return RequiresInternet ? ItemUpdateType.MetadataEdit : ItemUpdateType.MetadataImport; }
}
/// <summary>
/// Gets a value indicating whether [refresh on version change].
/// </summary>

@ -55,15 +55,13 @@ namespace MediaBrowser.Controller.Providers
/// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <returns>Task{System.Boolean}.</returns>
Task<bool> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true);
Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true);
/// <summary>
/// Adds the metadata providers.
/// </summary>
/// <param name="providers">The providers.</param>
/// <param name="savers">The savers.</param>
void AddParts(IEnumerable<BaseMetadataProvider> providers,
IEnumerable<IMetadataSaver> savers);
void AddParts(IEnumerable<BaseMetadataProvider> providers);
/// <summary>
/// Gets the save path.

@ -26,6 +26,11 @@ namespace MediaBrowser.Model.Querying
/// </summary>
CriticRatingSummary,
/// <summary>
/// The custom rating
/// </summary>
CustomRating,
/// <summary>
/// The date created of the item
/// </summary>

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -22,6 +23,14 @@ namespace MediaBrowser.Providers
{
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Supportses the specified item.
/// </summary>

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System;
@ -22,6 +23,14 @@ namespace MediaBrowser.Providers
{
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Supportses the specified item.
/// </summary>

@ -1,5 +1,4 @@
using System.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@ -11,6 +10,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -113,6 +113,14 @@ namespace MediaBrowser.Providers.MediaInfo
get { return MetadataProviderPriority.Last; }
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
/// </summary>
@ -193,8 +201,6 @@ namespace MediaBrowser.Providers.MediaInfo
// Image is already in the cache
item.PrimaryImagePath = path;
await _libraryManager.UpdateItem(item, cancellationToken).ConfigureAwait(false);
}
/// <summary>

@ -5,6 +5,7 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -61,6 +62,14 @@ namespace MediaBrowser.Providers.Movies
Current = this;
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Gets a value indicating whether [refresh on version change].
/// </summary>

@ -3,6 +3,7 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -85,6 +86,14 @@ namespace MediaBrowser.Providers.Movies
return item is Movie || item is BoxSet || item is MusicVideo;
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Gets a value indicating whether [requires internet].
/// </summary>

@ -226,7 +226,7 @@ namespace MediaBrowser.Providers.Movies
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if [has alt meta] [the specified item]; otherwise, <c>false</c>.</returns>
private bool HasAltMeta(BaseItem item)
internal static bool HasAltMeta(BaseItem item)
{
return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName(AltMetaFileName);
}

@ -1,5 +1,4 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
@ -73,6 +72,17 @@ namespace MediaBrowser.Providers.Movies
}
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
// These values are now saved in movie.xml, so don't refresh if they're present
if (MovieDbProvider.HasAltMeta(item) && item.CriticRating.HasValue && !string.IsNullOrEmpty(item.CriticRatingSummary))
{
return false;
}
return base.NeedsRefreshInternal(item, providerInfo);
}
/// <summary>
/// Supports the specified item.
/// </summary>

@ -1,6 +1,7 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -66,6 +67,14 @@ namespace MediaBrowser.Providers.Movies
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataEdit;
}
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
if (HasAltMeta(item))

@ -3,6 +3,7 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -64,6 +65,14 @@ namespace MediaBrowser.Providers.Music
return item is MusicAlbum;
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Gets a value indicating whether [refresh on version change].
/// </summary>

@ -5,6 +5,7 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -63,6 +64,14 @@ namespace MediaBrowser.Providers.Music
/// </summary>
protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/artist/{0}/{1}/xml/all/1/1";
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Supportses the specified item.
/// </summary>

@ -59,6 +59,7 @@ namespace MediaBrowser.Providers.Savers
builder.Append("<Title>");
XmlSaverHelpers.AddCommonNodes(item, builder);
XmlSaverHelpers.AppendMediaInfo((Video)item, builder);
builder.Append("</Title>");

@ -89,9 +89,35 @@ namespace MediaBrowser.Providers.Savers
Directory.CreateDirectory(parentPath);
}
using (var streamWriter = new StreamWriter(path, false, Encoding.UTF8))
var wasHidden = false;
var file = new FileInfo(path);
// This will fail if the file is hidden
if (file.Exists)
{
if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
file.Attributes &= ~FileAttributes.Hidden;
wasHidden = true;
}
}
using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using (var streamWriter = new StreamWriter(filestream, Encoding.UTF8))
{
xmlDocument.Save(streamWriter);
}
}
if (wasHidden)
{
xmlDocument.Save(streamWriter);
file.Refresh();
// Add back the attribute
file.Attributes |= FileAttributes.Hidden;
}
}
@ -128,25 +154,21 @@ namespace MediaBrowser.Providers.Savers
builder.Append("<certification>" + SecurityElement.Escape(item.OfficialRating) + "</certification>");
}
if (item.People.Count > 0)
{
builder.Append("<Persons>");
builder.Append("<Added>" + SecurityElement.Escape(item.DateCreated.ToString(UsCulture)) + "</Added>");
foreach (var person in item.People)
{
builder.Append("<Person>");
builder.Append("<Name>" + SecurityElement.Escape(person.Name) + "</Name>");
builder.Append("<Type>" + SecurityElement.Escape(person.Type) + "</Type>");
builder.Append("<Role>" + SecurityElement.Escape(person.Role) + "</Role>");
builder.Append("</Person>");
}
if (!string.IsNullOrEmpty(item.DisplayMediaType))
{
builder.Append("<Type>" + SecurityElement.Escape(item.DisplayMediaType) + "</Type>");
}
builder.Append("</Persons>");
if (item.CriticRating.HasValue)
{
builder.Append("<CriticRating>" + SecurityElement.Escape(item.CriticRating.Value.ToString(UsCulture)) + "</CriticRating>");
}
if (!string.IsNullOrEmpty(item.DisplayMediaType))
if (!string.IsNullOrEmpty(item.CriticRatingSummary))
{
builder.Append("<Type>" + SecurityElement.Escape(item.DisplayMediaType) + "</Type>");
builder.Append("<CriticRatingSummary><![CDATA[" + item.Overview + "]]></CriticRatingSummary>");
}
if (!string.IsNullOrEmpty(item.Overview))
@ -209,28 +231,17 @@ namespace MediaBrowser.Providers.Savers
builder.Append("<Language>" + SecurityElement.Escape(item.Language) + "</Language>");
}
if (item.RunTimeTicks.HasValue)
// Use original runtime here, actual file runtime later in MediaInfo
var runTimeTicks = item.OriginalRunTimeTicks ?? item.RunTimeTicks;
if (runTimeTicks.HasValue)
{
var timespan = TimeSpan.FromTicks(item.RunTimeTicks.Value);
var timespan = TimeSpan.FromTicks(runTimeTicks.Value);
builder.Append("<RunningTime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</RunningTime>");
builder.Append("<Runtime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</Runtime>");
}
if (item.Taglines.Count > 0)
{
builder.Append("<TagLine>" + SecurityElement.Escape(item.Taglines[0]) + "</TagLine>");
builder.Append("<TagLines>");
foreach (var tagline in item.Taglines)
{
builder.Append("<Tagline>" + SecurityElement.Escape(tagline) + "</Tagline>");
}
builder.Append("</TagLines>");
}
var imdb = item.GetProviderId(MetadataProviders.Imdb);
if (!string.IsNullOrEmpty(imdb))
@ -275,6 +286,20 @@ namespace MediaBrowser.Providers.Savers
builder.Append("<CollectionNumber>" + SecurityElement.Escape(tmdbCollection) + "</CollectionNumber>");
}
if (item.Taglines.Count > 0)
{
builder.Append("<TagLine>" + SecurityElement.Escape(item.Taglines[0]) + "</TagLine>");
builder.Append("<TagLines>");
foreach (var tagline in item.Taglines)
{
builder.Append("<Tagline>" + SecurityElement.Escape(tagline) + "</Tagline>");
}
builder.Append("</TagLines>");
}
if (item.Genres.Count > 0)
{
builder.Append("<Genres>");
@ -311,7 +336,22 @@ namespace MediaBrowser.Providers.Savers
builder.Append("</Tags>");
}
builder.Append("<Added>" + SecurityElement.Escape(item.DateCreated.ToString(UsCulture)) + "</Added>");
if (item.People.Count > 0)
{
builder.Append("<Persons>");
foreach (var person in item.People)
{
builder.Append("<Person>");
builder.Append("<Name>" + SecurityElement.Escape(person.Name) + "</Name>");
builder.Append("<Type>" + SecurityElement.Escape(person.Type) + "</Type>");
builder.Append("<Role>" + SecurityElement.Escape(person.Role) + "</Role>");
builder.Append("</Person>");
}
builder.Append("</Persons>");
}
}
/// <summary>

@ -1,6 +1,7 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using System;
@ -21,6 +22,14 @@ namespace MediaBrowser.Providers.TV
{
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Supportses the specified item.
/// </summary>

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -35,6 +36,14 @@ namespace MediaBrowser.Providers.TV
_providerManager = providerManager;
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Supportses the specified item.
/// </summary>

@ -4,6 +4,7 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -48,6 +49,14 @@ namespace MediaBrowser.Providers.TV
return item is Series;
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Needses the refresh internal.
/// </summary>

@ -61,6 +61,14 @@ namespace MediaBrowser.Providers.TV
return item is Episode;
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataEdit;
}
}
/// <summary>
/// Gets the priority.
/// </summary>

@ -70,6 +70,14 @@ namespace MediaBrowser.Providers.TV
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Gets a value indicating whether [refresh on version change].
/// </summary>

@ -80,6 +80,14 @@ namespace MediaBrowser.Providers.TV
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Gets a value indicating whether [refresh on version change].
/// </summary>

@ -128,6 +128,10 @@ namespace MediaBrowser.Server.Implementations.Library
/// <value>The by reference items.</value>
private ConcurrentDictionary<Guid, BaseItem> ByReferenceItems { get; set; }
private IEnumerable<IMetadataSaver> _savers;
private readonly Func<IDirectoryWatchers> _directoryWatchersFactory;
/// <summary>
/// The _library items cache
/// </summary>
@ -167,13 +171,14 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataRepository userDataRepository)
public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataRepository userDataRepository, Func<IDirectoryWatchers> directoryWatchersFactory)
{
_logger = logger;
_taskManager = taskManager;
_userManager = userManager;
ConfigurationManager = configurationManager;
_userDataRepository = userDataRepository;
_directoryWatchersFactory = directoryWatchersFactory;
ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>();
ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
@ -191,13 +196,15 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="itemComparers">The item comparers.</param>
/// <param name="prescanTasks">The prescan tasks.</param>
/// <param name="postscanTasks">The postscan tasks.</param>
/// <param name="savers">The savers.</param>
public void AddParts(IEnumerable<IResolverIgnoreRule> rules,
IEnumerable<IVirtualFolderCreator> pluginFolders,
IEnumerable<IItemResolver> resolvers,
IEnumerable<IIntroProvider> introProviders,
IEnumerable<IBaseItemComparer> itemComparers,
IEnumerable<ILibraryPrescanTask> prescanTasks,
IEnumerable<ILibraryPostScanTask> postscanTasks)
IEnumerable<ILibraryPostScanTask> postscanTasks,
IEnumerable<IMetadataSaver> savers)
{
EntityResolutionIgnoreRules = rules;
PluginFolderCreators = pluginFolders;
@ -206,6 +213,7 @@ namespace MediaBrowser.Server.Implementations.Library
Comparers = itemComparers;
PrescanTasks = prescanTasks;
PostscanTasks = postscanTasks;
_savers = savers;
}
/// <summary>
@ -326,7 +334,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="newName">The new name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken)
private async Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken)
{
var seasons = RootFolder.RecursiveChildren
.OfType<Season>()
@ -336,9 +344,16 @@ namespace MediaBrowser.Server.Implementations.Library
foreach (var season in seasons)
{
season.Name = newName;
}
return UpdateItems(seasons, cancellationToken);
try
{
await UpdateItem(season, ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error saving {0}", ex, season.Path);
}
}
}
/// <summary>
@ -1278,33 +1293,35 @@ namespace MediaBrowser.Server.Implementations.Library
}
/// <summary>
/// Updates the items.
/// Updates the item.
/// </summary>
/// <param name="items">The items.</param>
/// <param name="item">The item.</param>
/// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task UpdateItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
public async Task UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
var list = items.ToList();
await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
await ItemRepository.SaveItems(list, cancellationToken).ConfigureAwait(false);
UpdateItemInLibraryCache(item);
foreach (var item in list)
// If metadata was downloaded or edited, save external metadata
if ((updateReason & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit)
{
UpdateItemInLibraryCache(item);
OnItemUpdated(item);
await SaveMetadata(item).ConfigureAwait(false);
}
}
/// <summary>
/// Updates the item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task UpdateItem(BaseItem item, CancellationToken cancellationToken)
{
return UpdateItems(new[] { item }, cancellationToken);
if (ItemUpdated != null)
{
try
{
ItemUpdated(this, new ItemChangeEventArgs { Item = item });
}
catch (Exception ex)
{
_logger.ErrorException("Error in ItemUpdated event handler", ex);
}
}
}
/// <summary>
@ -1337,22 +1354,38 @@ namespace MediaBrowser.Server.Implementations.Library
return ItemRepository.RetrieveItem(id, type);
}
private readonly ConcurrentDictionary<string, SemaphoreSlim> _fileLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
/// <summary>
/// Called when [item updated].
/// Saves the metadata.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>Task.</returns>
private void OnItemUpdated(BaseItem item)
private async Task SaveMetadata(BaseItem item)
{
if (ItemUpdated != null)
foreach (var saver in _savers.Where(i => i.Supports(item)))
{
var path = saver.GetSavePath(item);
var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1));
var directoryWatchers = _directoryWatchersFactory();
await semaphore.WaitAsync().ConfigureAwait(false);
try
{
ItemUpdated(this, new ItemChangeEventArgs { Item = item });
directoryWatchers.TemporarilyIgnore(path);
saver.Save(item, CancellationToken.None);
}
catch (Exception ex)
{
_logger.ErrorException("Error in ItemUpdated event handler", ex);
_logger.ErrorException("Error in metadata saver", ex);
}
finally
{
directoryWatchers.RemoveTempIgnore(path);
semaphore.Release();
}
}
}

@ -60,8 +60,6 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <value>The metadata providers enumerable.</value>
private BaseMetadataProvider[] MetadataProviders { get; set; }
private IEnumerable<IMetadataSaver> _savers;
/// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// </summary>
@ -79,44 +77,6 @@ namespace MediaBrowser.Server.Implementations.Providers
_remoteImageCache = new FileSystemRepository(configurationManager.ApplicationPaths.DownloadedImagesDataPath);
configurationManager.ConfigurationUpdated += configurationManager_ConfigurationUpdated;
libraryManager.ItemUpdated += libraryManager_ItemUpdated;
}
private readonly ConcurrentDictionary<string, SemaphoreSlim> _fileLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
/// <summary>
/// Handles the ItemUpdated event of the libraryManager control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
async void libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e)
{
var item = e.Item;
foreach (var saver in _savers.Where(i => i.Supports(item)))
{
var path = saver.GetSavePath(item);
var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1));
await semaphore.WaitAsync().ConfigureAwait(false);
try
{
_directoryWatchers.TemporarilyIgnore(path);
saver.Save(item, CancellationToken.None);
}
catch (Exception ex)
{
_logger.ErrorException("Error in metadata saver", ex);
}
finally
{
_directoryWatchers.RemoveTempIgnore(path);
semaphore.Release();
}
}
}
/// <summary>
@ -134,12 +94,9 @@ namespace MediaBrowser.Server.Implementations.Providers
/// Adds the metadata providers.
/// </summary>
/// <param name="providers">The providers.</param>
/// <param name="savers">The savers.</param>
public void AddParts(IEnumerable<BaseMetadataProvider> providers,
IEnumerable<IMetadataSaver> savers)
public void AddParts(IEnumerable<BaseMetadataProvider> providers)
{
MetadataProviders = providers.OrderBy(e => e.Priority).ToArray();
_savers = savers;
}
/// <summary>
@ -150,18 +107,14 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <returns>Task{System.Boolean}.</returns>
public async Task<bool> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true)
public async Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
// Allow providers of the same priority to execute in parallel
MetadataProviderPriority? currentPriority = null;
var currentTasks = new List<Task<bool>>();
var result = false;
ItemUpdateType? result = null;
cancellationToken.ThrowIfCancellationRequested();
@ -188,15 +141,6 @@ namespace MediaBrowser.Server.Implementations.Providers
continue;
}
// When a new priority is reached, await the ones that are currently running and clear the list
if (currentPriority.HasValue && currentPriority.Value != provider.Priority && currentTasks.Count > 0)
{
var results = await Task.WhenAll(currentTasks).ConfigureAwait(false);
result |= results.Contains(true);
currentTasks.Clear();
}
// Put this check below the await because the needs refresh of the next tier of providers may depend on the previous ones running
// This is the case for the fan art provider which depends on the movie and tv providers having run before them
if (provider.RequiresInternet && item.DontFetchMeta)
@ -216,14 +160,19 @@ namespace MediaBrowser.Server.Implementations.Providers
_logger.Error("Error determining NeedsRefresh for {0}", ex, item.Path);
}
currentTasks.Add(FetchAsync(provider, item, force, cancellationToken));
currentPriority = provider.Priority;
}
var updateType = await FetchAsync(provider, item, force, cancellationToken).ConfigureAwait(false);
if (currentTasks.Count > 0)
{
var results = await Task.WhenAll(currentTasks).ConfigureAwait(false);
result |= results.Contains(true);
if (updateType.HasValue)
{
if (result.HasValue)
{
result = result.Value | updateType.Value;
}
else
{
result = updateType;
}
}
}
return result;
@ -238,7 +187,7 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
private async Task<bool> FetchAsync(BaseMetadataProvider provider, BaseItem item, bool force, CancellationToken cancellationToken)
private async Task<ItemUpdateType?> FetchAsync(BaseMetadataProvider provider, BaseItem item, bool force, CancellationToken cancellationToken)
{
if (item == null)
{
@ -256,7 +205,14 @@ namespace MediaBrowser.Server.Implementations.Providers
try
{
return await provider.FetchAsync(item, force, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token).ConfigureAwait(false);
var changed = await provider.FetchAsync(item, force, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token).ConfigureAwait(false);
if (changed)
{
return provider.ItemUpdateType;
}
return null;
}
catch (OperationCanceledException ex)
{
@ -268,14 +224,15 @@ namespace MediaBrowser.Server.Implementations.Providers
throw;
}
return false;
return null;
}
catch (Exception ex)
{
_logger.ErrorException("{0} failed refreshing {1}", ex, provider.GetType().Name, item.Name);
provider.SetLastRefreshed(item, DateTime.UtcNow, ProviderRefreshStatus.Failure);
return true;
return ItemUpdateType.Unspecified;
}
finally
{

@ -294,7 +294,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
// Image is already in the cache
item.PrimaryImagePath = path;
await _libraryManager.UpdateItem(item, cancellationToken).ConfigureAwait(false);
await _libraryManager.UpdateItem(item, ItemUpdateType.ImageUpdate, cancellationToken).ConfigureAwait(false);
}
else
{

@ -262,7 +262,7 @@ namespace MediaBrowser.ServerApplication
UserManager = new UserManager(Logger, ServerConfigurationManager);
RegisterSingleInstance(UserManager);
LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataRepository);
LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataRepository, () => DirectoryWatchers);
RegisterSingleInstance(LibraryManager);
InstallationManager = new InstallationManager(HttpClient, PackageManager, JsonSerializer, Logger, this);
@ -397,10 +397,10 @@ namespace MediaBrowser.ServerApplication
GetExports<IIntroProvider>(),
GetExports<IBaseItemComparer>(),
GetExports<ILibraryPrescanTask>(),
GetExports<ILibraryPostScanTask>());
ProviderManager.AddParts(GetExports<BaseMetadataProvider>().ToArray(),
GetExports<ILibraryPostScanTask>(),
GetExports<IMetadataSaver>());
ProviderManager.AddParts(GetExports<BaseMetadataProvider>().ToArray());
}
/// <summary>

Loading…
Cancel
Save