diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs
index 151f7016cc..dd15dbc30d 100644
--- a/MediaBrowser.Api/LibraryService.cs
+++ b/MediaBrowser.Api/LibraryService.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs
index 2e28d7ad63..2bd338431c 100644
--- a/MediaBrowser.Controller/Dto/DtoBuilder.cs
+++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index dbfeff7472..695ea25836 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -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;
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
index a2c5f98b85..79a813bb5e 100644
--- a/MediaBrowser.Controller/Entities/User.cs
+++ b/MediaBrowser.Controller/Entities/User.cs
@@ -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)
{
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 46a6d38df1..f1d5dce89c 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -152,13 +152,15 @@ namespace MediaBrowser.Controller.Library
/// The item comparers.
/// The prescan tasks.
/// The postscan tasks.
+ /// The savers.
void AddParts(IEnumerable rules,
IEnumerable pluginFolders,
IEnumerable resolvers,
IEnumerable introProviders,
IEnumerable itemComparers,
IEnumerable prescanTasks,
- IEnumerable postscanTasks);
+ IEnumerable postscanTasks,
+ IEnumerable savers);
///
/// Sorts the specified items.
@@ -205,9 +207,10 @@ namespace MediaBrowser.Controller.Library
/// Updates the item.
///
/// The item.
+ /// The update reason.
/// The cancellation token.
/// Task.
- Task UpdateItem(BaseItem item, CancellationToken cancellationToken);
+ Task UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken);
///
/// Retrieves the item.
diff --git a/MediaBrowser.Controller/Library/ItemUpdateType.cs b/MediaBrowser.Controller/Library/ItemUpdateType.cs
new file mode 100644
index 0000000000..56ae2cbbdc
--- /dev/null
+++ b/MediaBrowser.Controller/Library/ItemUpdateType.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace MediaBrowser.Controller.Library
+{
+ [Flags]
+ public enum ItemUpdateType
+ {
+ Unspecified = 1,
+ MetadataImport = 2,
+ ImageUpdate = 4,
+ MetadataEdit = 16
+ }
+}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 0bd82eaa7c..2c6a6df08b 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -81,6 +81,7 @@
+
diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
index 29cf43f288..6c3d08a14f 100644
--- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
+++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.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();
diff --git a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
index 625910c0bc..e415c68599 100644
--- a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
@@ -75,6 +75,11 @@ namespace MediaBrowser.Controller.Providers
}
}
+ public virtual ItemUpdateType ItemUpdateType
+ {
+ get { return RequiresInternet ? ItemUpdateType.MetadataEdit : ItemUpdateType.MetadataImport; }
+ }
+
///
/// Gets a value indicating whether [refresh on version change].
///
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 7f80973e9f..643dbe1c2c 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -55,15 +55,13 @@ namespace MediaBrowser.Controller.Providers
/// if set to true [force].
/// if set to true [allow slow providers].
/// Task{System.Boolean}.
- Task ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true);
+ Task ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true);
///
/// Adds the metadata providers.
///
/// The providers.
- /// The savers.
- void AddParts(IEnumerable providers,
- IEnumerable savers);
+ void AddParts(IEnumerable providers);
///
/// Gets the save path.
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index d136af8f03..a8a58d6a64 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -26,6 +26,11 @@ namespace MediaBrowser.Model.Querying
///
CriticRatingSummary,
+ ///
+ /// The custom rating
+ ///
+ CustomRating,
+
///
/// The date created of the item
///
diff --git a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs b/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
index 87c450d55e..883461cd03 100644
--- a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
+++ b/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
@@ -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;
+ }
+ }
+
///
/// Supportses the specified item.
///
diff --git a/MediaBrowser.Providers/ImagesByNameProvider.cs b/MediaBrowser.Providers/ImagesByNameProvider.cs
index dad3a25f3b..228ca94c58 100644
--- a/MediaBrowser.Providers/ImagesByNameProvider.cs
+++ b/MediaBrowser.Providers/ImagesByNameProvider.cs
@@ -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;
+ }
+ }
+
///
/// Supportses the specified item.
///
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index c20a31b7ea..ab3fbff55a 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -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;
+ }
+ }
+
///
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
///
@@ -193,8 +201,6 @@ namespace MediaBrowser.Providers.MediaInfo
// Image is already in the cache
item.PrimaryImagePath = path;
-
- await _libraryManager.UpdateItem(item, cancellationToken).ConfigureAwait(false);
}
///
diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
index be105b63af..1a1c34928e 100644
--- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
@@ -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;
+ }
+ }
+
///
/// Gets a value indicating whether [refresh on version change].
///
diff --git a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs
index f092decdf3..2c9fbcec8a 100644
--- a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs
@@ -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;
+ }
+ }
+
///
/// Gets a value indicating whether [requires internet].
///
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index 8fecf6f443..43eae7ca23 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
@@ -226,7 +226,7 @@ namespace MediaBrowser.Providers.Movies
///
/// The item.
/// true if [has alt meta] [the specified item]; otherwise, false.
- private bool HasAltMeta(BaseItem item)
+ internal static bool HasAltMeta(BaseItem item)
{
return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName(AltMetaFileName);
}
diff --git a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs b/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs
index c88169c809..3e12b2d87d 100644
--- a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs
+++ b/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs
@@ -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);
+ }
+
///
/// Supports the specified item.
///
diff --git a/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs b/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs
index fbf084218a..b5c11acb12 100644
--- a/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs
+++ b/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs
@@ -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))
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
index 299ae2d06b..d7990f4312 100644
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
@@ -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;
+ }
+ }
+
///
/// Gets a value indicating whether [refresh on version change].
///
diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
index 1685846d6f..f310934aff 100644
--- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
@@ -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
///
protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/artist/{0}/{1}/xml/all/1/1";
+ public override ItemUpdateType ItemUpdateType
+ {
+ get
+ {
+ return ItemUpdateType.ImageUpdate;
+ }
+ }
+
///
/// Supportses the specified item.
///
diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
index 32ec765b0f..c180c22673 100644
--- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
@@ -59,6 +59,7 @@ namespace MediaBrowser.Providers.Savers
builder.Append("");
XmlSaverHelpers.AddCommonNodes(item, builder);
+ XmlSaverHelpers.AppendMediaInfo((Video)item, builder);
builder.Append("");
diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
index 88d6ebec0e..2ceb82d712 100644
--- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
+++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
@@ -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("" + SecurityElement.Escape(item.OfficialRating) + "");
}
- if (item.People.Count > 0)
- {
- builder.Append("");
+ builder.Append("" + SecurityElement.Escape(item.DateCreated.ToString(UsCulture)) + "");
- foreach (var person in item.People)
- {
- builder.Append("");
- builder.Append("" + SecurityElement.Escape(person.Name) + "");
- builder.Append("" + SecurityElement.Escape(person.Type) + "");
- builder.Append("" + SecurityElement.Escape(person.Role) + "");
- builder.Append("");
- }
+ if (!string.IsNullOrEmpty(item.DisplayMediaType))
+ {
+ builder.Append("" + SecurityElement.Escape(item.DisplayMediaType) + "");
+ }
- builder.Append("");
+ if (item.CriticRating.HasValue)
+ {
+ builder.Append("" + SecurityElement.Escape(item.CriticRating.Value.ToString(UsCulture)) + "");
}
- if (!string.IsNullOrEmpty(item.DisplayMediaType))
+ if (!string.IsNullOrEmpty(item.CriticRatingSummary))
{
- builder.Append("" + SecurityElement.Escape(item.DisplayMediaType) + "");
+ builder.Append("");
}
if (!string.IsNullOrEmpty(item.Overview))
@@ -209,28 +231,17 @@ namespace MediaBrowser.Providers.Savers
builder.Append("" + SecurityElement.Escape(item.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("" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "");
builder.Append("" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "");
}
- if (item.Taglines.Count > 0)
- {
- builder.Append("" + SecurityElement.Escape(item.Taglines[0]) + "");
-
- builder.Append("");
-
- foreach (var tagline in item.Taglines)
- {
- builder.Append("" + SecurityElement.Escape(tagline) + "");
- }
-
- builder.Append("");
- }
-
var imdb = item.GetProviderId(MetadataProviders.Imdb);
if (!string.IsNullOrEmpty(imdb))
@@ -275,6 +286,20 @@ namespace MediaBrowser.Providers.Savers
builder.Append("" + SecurityElement.Escape(tmdbCollection) + "");
}
+ if (item.Taglines.Count > 0)
+ {
+ builder.Append("" + SecurityElement.Escape(item.Taglines[0]) + "");
+
+ builder.Append("");
+
+ foreach (var tagline in item.Taglines)
+ {
+ builder.Append("" + SecurityElement.Escape(tagline) + "");
+ }
+
+ builder.Append("");
+ }
+
if (item.Genres.Count > 0)
{
builder.Append("");
@@ -311,7 +336,22 @@ namespace MediaBrowser.Providers.Savers
builder.Append("");
}
- builder.Append("" + SecurityElement.Escape(item.DateCreated.ToString(UsCulture)) + "");
+ if (item.People.Count > 0)
+ {
+ builder.Append("");
+
+ foreach (var person in item.People)
+ {
+ builder.Append("");
+ builder.Append("" + SecurityElement.Escape(person.Name) + "");
+ builder.Append("" + SecurityElement.Escape(person.Type) + "");
+ builder.Append("" + SecurityElement.Escape(person.Role) + "");
+ builder.Append("");
+ }
+
+ builder.Append("");
+ }
+
}
///
diff --git a/MediaBrowser.Providers/TV/EpisodeImageFromMediaLocationProvider.cs b/MediaBrowser.Providers/TV/EpisodeImageFromMediaLocationProvider.cs
index 5b1490750c..8eb01a1431 100644
--- a/MediaBrowser.Providers/TV/EpisodeImageFromMediaLocationProvider.cs
+++ b/MediaBrowser.Providers/TV/EpisodeImageFromMediaLocationProvider.cs
@@ -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;
+ }
+ }
+
///
/// Supportses the specified item.
///
diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
index ccdfc2a814..d0a0735fb9 100644
--- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
@@ -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;
+ }
+ }
+
///
/// Supportses the specified item.
///
diff --git a/MediaBrowser.Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Providers/TV/FanArtTVProvider.cs
index 878cd26e2a..6dc61b35d2 100644
--- a/MediaBrowser.Providers/TV/FanArtTVProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArtTVProvider.cs
@@ -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;
+ }
+ }
+
///
/// Needses the refresh internal.
///
diff --git a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs
index 32345cd095..a3d44a0321 100644
--- a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs
@@ -61,6 +61,14 @@ namespace MediaBrowser.Providers.TV
return item is Episode;
}
+ public override ItemUpdateType ItemUpdateType
+ {
+ get
+ {
+ return ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataEdit;
+ }
+ }
+
///
/// Gets the priority.
///
diff --git a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs
index 179354c086..f8399ebdf9 100644
--- a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs
@@ -70,6 +70,14 @@ namespace MediaBrowser.Providers.TV
}
}
+ public override ItemUpdateType ItemUpdateType
+ {
+ get
+ {
+ return ItemUpdateType.ImageUpdate;
+ }
+ }
+
///
/// Gets a value indicating whether [refresh on version change].
///
diff --git a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
index bf57d3c8c2..2d8bf5ed69 100644
--- a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
@@ -80,6 +80,14 @@ namespace MediaBrowser.Providers.TV
}
}
+ public override ItemUpdateType ItemUpdateType
+ {
+ get
+ {
+ return ItemUpdateType.ImageUpdate;
+ }
+ }
+
///
/// Gets a value indicating whether [refresh on version change].
///
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index d1b7634fbc..0465cb5c38 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -128,6 +128,10 @@ namespace MediaBrowser.Server.Implementations.Library
/// The by reference items.
private ConcurrentDictionary ByReferenceItems { get; set; }
+ private IEnumerable _savers;
+
+ private readonly Func _directoryWatchersFactory;
+
///
/// The _library items cache
///
@@ -167,13 +171,14 @@ namespace MediaBrowser.Server.Implementations.Library
/// The user manager.
/// The configuration manager.
/// The user data repository.
- 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 directoryWatchersFactory)
{
_logger = logger;
_taskManager = taskManager;
_userManager = userManager;
ConfigurationManager = configurationManager;
_userDataRepository = userDataRepository;
+ _directoryWatchersFactory = directoryWatchersFactory;
ByReferenceItems = new ConcurrentDictionary();
ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -191,13 +196,15 @@ namespace MediaBrowser.Server.Implementations.Library
/// The item comparers.
/// The prescan tasks.
/// The postscan tasks.
+ /// The savers.
public void AddParts(IEnumerable rules,
IEnumerable pluginFolders,
IEnumerable resolvers,
IEnumerable introProviders,
IEnumerable itemComparers,
IEnumerable prescanTasks,
- IEnumerable postscanTasks)
+ IEnumerable postscanTasks,
+ IEnumerable savers)
{
EntityResolutionIgnoreRules = rules;
PluginFolderCreators = pluginFolders;
@@ -206,6 +213,7 @@ namespace MediaBrowser.Server.Implementations.Library
Comparers = itemComparers;
PrescanTasks = prescanTasks;
PostscanTasks = postscanTasks;
+ _savers = savers;
}
///
@@ -326,7 +334,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// The new name.
/// The cancellation token.
/// Task.
- private Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken)
+ private async Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken)
{
var seasons = RootFolder.RecursiveChildren
.OfType()
@@ -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);
+ }
+ }
}
///
@@ -1278,33 +1293,35 @@ namespace MediaBrowser.Server.Implementations.Library
}
///
- /// Updates the items.
+ /// Updates the item.
///
- /// The items.
+ /// The item.
+ /// The update reason.
/// The cancellation token.
/// Task.
- private async Task UpdateItems(IEnumerable 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);
}
- }
- ///
- /// Updates the item.
- ///
- /// The item.
- /// The cancellation token.
- /// Task.
- 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);
+ }
+ }
}
///
@@ -1337,22 +1354,38 @@ namespace MediaBrowser.Server.Implementations.Library
return ItemRepository.RetrieveItem(id, type);
}
+ private readonly ConcurrentDictionary _fileLocks = new ConcurrentDictionary();
+
///
- /// Called when [item updated].
+ /// Saves the metadata.
///
/// The item.
/// Task.
- 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();
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs
index 01c0659b6f..ee8bb4c096 100644
--- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs
+++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs
@@ -60,8 +60,6 @@ namespace MediaBrowser.Server.Implementations.Providers
/// The metadata providers enumerable.
private BaseMetadataProvider[] MetadataProviders { get; set; }
- private IEnumerable _savers;
-
///
/// Initializes a new instance of the class.
///
@@ -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 _fileLocks = new ConcurrentDictionary();
-
- ///
- /// Handles the ItemUpdated event of the libraryManager control.
- ///
- /// The source of the event.
- /// The instance containing the event data.
- 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();
- }
- }
}
///
@@ -134,12 +94,9 @@ namespace MediaBrowser.Server.Implementations.Providers
/// Adds the metadata providers.
///
/// The providers.
- /// The savers.
- public void AddParts(IEnumerable providers,
- IEnumerable savers)
+ public void AddParts(IEnumerable providers)
{
MetadataProviders = providers.OrderBy(e => e.Priority).ToArray();
- _savers = savers;
}
///
@@ -150,18 +107,14 @@ namespace MediaBrowser.Server.Implementations.Providers
/// if set to true [force].
/// if set to true [allow slow providers].
/// Task{System.Boolean}.
- public async Task ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true)
+ public async Task 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>();
-
- 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
/// The cancellation token.
/// Task{System.Boolean}.
///
- private async Task FetchAsync(BaseMetadataProvider provider, BaseItem item, bool force, CancellationToken cancellationToken)
+ private async Task 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
{
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs
index d63494c1e8..dc8425ac88 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs
+++ b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs
@@ -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
{
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 09e33d7fdf..70cd319ca6 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -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(),
GetExports(),
GetExports(),
- GetExports());
-
- ProviderManager.AddParts(GetExports().ToArray(),
+ GetExports(),
GetExports());
+
+ ProviderManager.AddParts(GetExports().ToArray());
}
///