diff options
19 files changed, 740 insertions, 246 deletions
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 00ab867b2..e72ffdb0b 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -314,7 +314,7 @@ namespace MediaBrowser.Controller.Entities { throw new IOException("Unable to retrieve file system info for " + path); } - + var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths) { FileInfo = pathInfo, @@ -465,6 +465,12 @@ namespace MediaBrowser.Controller.Entities public virtual string OfficialRating { get; set; } /// <summary> + /// Gets or sets the official rating description. + /// </summary> + /// <value>The official rating description.</value> + public string OfficialRatingDescription { get; set; } + + /// <summary> /// Gets or sets the custom rating. /// </summary> /// <value>The custom rating.</value> diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 6d6fab3be..46a6d38df 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -152,16 +152,13 @@ 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> - /// <param name="?">The ?.</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<IMetadataSaver> savers); + IEnumerable<ILibraryPostScanTask> postscanTasks); /// <summary> /// Sorts the specified items. diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs index 419b65f6f..86e2738af 100644 --- a/MediaBrowser.Controller/Library/IMetadataSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs @@ -1,6 +1,5 @@ using MediaBrowser.Controller.Entities; using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Controller.Library { @@ -29,6 +28,6 @@ namespace MediaBrowser.Controller.Library /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task Save(BaseItem item, CancellationToken cancellationToken); + void Save(BaseItem item, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 645987437..d06c89a01 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -89,7 +89,7 @@ namespace MediaBrowser.Controller.Providers } private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - + /// <summary> /// Fetches metadata from one Xml Element /// </summary> @@ -205,6 +205,17 @@ namespace MediaBrowser.Controller.Providers break; } + case "MPAADescription": + { + var rating = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(rating)) + { + item.OfficialRatingDescription = rating; + } + break; + } + case "CustomRating": { var val = reader.ReadElementContentAsString(); @@ -306,7 +317,7 @@ namespace MediaBrowser.Controller.Providers { var actors = reader.ReadInnerXml(); - + if (actors.Contains("<")) { // This is one of the mis-named "Actors" full nodes created by MB2 @@ -378,7 +389,7 @@ namespace MediaBrowser.Controller.Providers { float val; // All external meta is saving this as '.' for decimal I believe...but just to be sure - if (float.TryParse(rating.Replace(',','.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out val)) + if (float.TryParse(rating.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out val)) { item.CommunityRating = val; } @@ -412,6 +423,14 @@ namespace MediaBrowser.Controller.Providers } break; + case "CollectionNumber": + var tmdbCollection = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(tmdbCollection)) + { + item.SetProviderId(MetadataProviders.TmdbCollection, tmdbCollection); + } + break; + case "TVcomId": var TVcomId = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(TVcomId)) @@ -602,8 +621,8 @@ namespace MediaBrowser.Controller.Providers { switch (reader.Name) { - // Removed support for "Value" tag as it conflicted with MPAA rating but leaving this function for possible - // future support of "Description" -ebr + // Removed support for "Value" tag as it conflicted with MPAA rating but leaving this function for possible + // future support of "Description" -ebr default: reader.Skip(); @@ -679,7 +698,7 @@ namespace MediaBrowser.Controller.Providers // Only split by comma if there is no pipe in the string // We have to be careful to not split names like Matthew, Jr. - var separator = value.IndexOf('|') == -1 ? ',' : '|'; + var separator = value.IndexOf('|') == -1 && value.IndexOf(';') == -1 ? new[] { ',' } : new[] { '|', ';' }; value = value.Trim().Trim(separator); @@ -690,12 +709,12 @@ namespace MediaBrowser.Controller.Providers /// Provides an additional overload for string.split /// </summary> /// <param name="val">The val.</param> - /// <param name="separator">The separator.</param> + /// <param name="separators">The separators.</param> /// <param name="options">The options.</param> /// <returns>System.String[][].</returns> - private static string[] Split(string val, char separator, StringSplitOptions options) + private static string[] Split(string val, char[] separators, StringSplitOptions options) { - return val.Split(new[] { separator }, options); + return val.Split(separators, options); } } diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 99860bac0..7f80973e9 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; using System.Collections.Generic; using System.IO; using System.Threading; @@ -6,6 +7,9 @@ using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers { + /// <summary> + /// Interface IProviderManager + /// </summary> public interface IProviderManager { /// <summary> @@ -21,8 +25,17 @@ namespace MediaBrowser.Controller.Providers /// <exception cref="System.ArgumentNullException">item</exception> Task<string> DownloadAndSaveImage(BaseItem item, string source, string targetName, bool saveLocally, SemaphoreSlim resourcePool, CancellationToken cancellationToken); + /// <summary> + /// Saves the image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="source">The source.</param> + /// <param name="targetName">Name of the target.</param> + /// <param name="saveLocally">if set to <c>true</c> [save locally].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{System.String}.</returns> Task<string> SaveImage(BaseItem item, Stream source, string targetName, bool saveLocally, CancellationToken cancellationToken); - + /// <summary> /// Saves to library filesystem. /// </summary> @@ -48,7 +61,9 @@ namespace MediaBrowser.Controller.Providers /// Adds the metadata providers. /// </summary> /// <param name="providers">The providers.</param> - void AddMetadataProviders(IEnumerable<BaseMetadataProvider> providers); + /// <param name="savers">The savers.</param> + void AddParts(IEnumerable<BaseMetadataProvider> providers, + IEnumerable<IMetadataSaver> savers); /// <summary> /// Gets the save path. diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 74397a458..06c921521 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -60,7 +60,6 @@ <Compile Include="Movies\FanArtMovieUpdatesPrescanTask.cs" /> <Compile Include="Movies\MovieDbImagesProvider.cs" /> <Compile Include="Movies\MovieDbProvider.cs" /> - <Compile Include="Movies\MovieProviderFromJson.cs" /> <Compile Include="Movies\MovieProviderFromXml.cs" /> <Compile Include="Movies\OpenMovieDatabaseProvider.cs" /> <Compile Include="Movies\PersonProviderFromJson.cs" /> @@ -77,7 +76,11 @@ <Compile Include="Music\LastfmHelper.cs" /> <Compile Include="Music\MusicArtistProviderFromJson.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Savers\EpisodeXmlSaver.cs" /> + <Compile Include="Savers\FolderXmlSaver.cs" /> <Compile Include="Savers\MovieXmlSaver.cs" /> + <Compile Include="Savers\SeriesXmlSaver.cs" /> + <Compile Include="Savers\XmlHelpers.cs" /> <Compile Include="TV\EpisodeImageFromMediaLocationProvider.cs" /> <Compile Include="TV\EpisodeIndexNumberProvider.cs" /> <Compile Include="TV\EpisodeProviderFromXml.cs" /> diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 6e42963d0..10234a094 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -180,40 +180,6 @@ namespace MediaBrowser.Providers.Movies } } - /// <summary> - /// The json provider - /// </summary> - protected MovieProviderFromJson JsonProvider; - - /// <summary> - /// Sets the persisted last refresh date on the item for this provider. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="value">The value.</param> - /// <param name="providerVersion">The provider version.</param> - /// <param name="status">The status.</param> - public override void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion, ProviderRefreshStatus status = ProviderRefreshStatus.Success) - { - base.SetLastRefreshed(item, value, providerVersion, status); - - if (ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem) - { - //in addition to ours, we need to set the last refreshed time for the local data provider - //so it won't see the new files we download and process them all over again - if (JsonProvider == null) JsonProvider = new MovieProviderFromJson(LogManager, ConfigurationManager, JsonSerializer, HttpClient, ProviderManager); - - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(JsonProvider.Id, out data)) - { - data = new BaseProviderInfo(); - } - - data.LastRefreshed = value; - item.ProviderData[JsonProvider.Id] = data; - } - } - private const string TmdbConfigUrl = "http://api.themoviedb.org/3/configuration?api_key={0}"; private const string Search3 = @"http://api.themoviedb.org/3/search/movie?api_key={1}&query={0}&language={2}"; private const string AltTitleSearch = @"http://api.themoviedb.org/3/movie/{0}/alternative_titles?api_key={1}&country={2}"; @@ -228,7 +194,6 @@ namespace MediaBrowser.Providers.Movies new Regex(@"(?<name>.*)") // last resort matches the whole string as the name }; - public const string LocalMetaFileName = "tmdb3.json"; public const string AltMetaFileName = "movie.xml"; protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) @@ -248,13 +213,6 @@ namespace MediaBrowser.Providers.Movies /// <returns>Task{System.Boolean}.</returns> public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { - if (HasAltMeta(item)) - { - Logger.Info("MovieDbProvider - Not fetching because 3rd party meta exists for " + item.Name); - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - cancellationToken.ThrowIfCancellationRequested(); await FetchMovieData(item, cancellationToken).ConfigureAwait(false); @@ -638,19 +596,6 @@ namespace MediaBrowser.Providers.Movies if (mainResult == null) return; ProcessMainInfo(item, mainResult); - - cancellationToken.ThrowIfCancellationRequested(); - - //and save locally - if (ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem) - { - var ms = new MemoryStream(); - JsonSerializer.SerializeToStream(mainResult, ms); - - cancellationToken.ThrowIfCancellationRequested(); - - await ProviderManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false); - } } /// <summary> diff --git a/MediaBrowser.Providers/Movies/MovieProviderFromJson.cs b/MediaBrowser.Providers/Movies/MovieProviderFromJson.cs deleted file mode 100644 index 529122f40..000000000 --- a/MediaBrowser.Providers/Movies/MovieProviderFromJson.cs +++ /dev/null @@ -1,122 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Movies -{ - /// <summary> - /// Class MovieProviderFromJson - /// </summary> - public class MovieProviderFromJson : MovieDbProvider - { - public MovieProviderFromJson(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager) - : base(logManager, configurationManager, jsonSerializer, httpClient, providerManager) - { - } - - public override bool Supports(BaseItem item) - { - if (item.LocationType != LocationType.FileSystem) - { - return false; - } - - var trailer = item as Trailer; - - if (trailer != null) - { - return !trailer.IsLocalTrailer; - } - - return item is Movie || item is BoxSet || item is MusicVideo; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get { return false; } - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LocalMetaFileName)); - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (item.ResolveArgs.ContainsMetaFileByName(AltMetaFileName)) - { - return false; // don't read our file if 3rd party data exists - } - - if (!item.ResolveArgs.ContainsMetaFileByName(LocalMetaFileName)) - { - return false; // nothing to read - } - - // Need to re-override to jump over intermediate implementation - return CompareDate(item) > providerInfo.LastRefreshed; - } - - /// <summary> - /// Fetches the async. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LocalMetaFileName)); - if (entry != null) - { - // read in our saved meta and pass to processing function - var movieData = JsonSerializer.DeserializeFromFile<CompleteMovieData>(entry.FullName); - - cancellationToken.ThrowIfCancellationRequested(); - - ProcessMainInfo(item, movieData); - - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } - return FalseTaskResult; - } - } -} diff --git a/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs b/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs index a427f1bac..8c700c944 100644 --- a/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs +++ b/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs @@ -15,9 +15,12 @@ namespace MediaBrowser.Providers.Movies /// </summary> public class MovieProviderFromXml : BaseMetadataProvider { + internal static MovieProviderFromXml Current { get; private set; } + public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) { + Current = this; } /// <summary> diff --git a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs new file mode 100644 index 000000000..c6e960332 --- /dev/null +++ b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs @@ -0,0 +1,93 @@ +using System; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using System.Globalization; +using System.IO; +using System.Security; +using System.Text; +using System.Threading; +using MediaBrowser.Providers.TV; + +namespace MediaBrowser.Providers.Savers +{ + public class EpisodeXmlSaver : IMetadataSaver + { + /// <summary> + /// Supportses the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + public bool Supports(BaseItem item) + { + if (item.LocationType != LocationType.FileSystem) + { + return false; + } + + return item is Episode; + } + + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + /// <summary> + /// Saves the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public void Save(BaseItem item, CancellationToken cancellationToken) + { + var episode = (Episode)item; + + var builder = new StringBuilder(); + + builder.Append("<Item>"); + + if (!string.IsNullOrEmpty(item.Name)) + { + builder.Append("<EpisodeName>" + SecurityElement.Escape(episode.Name) + "</EpisodeName>"); + } + + if (episode.IndexNumber.HasValue) + { + builder.Append("<EpisodeNumber>" + SecurityElement.Escape(episode.IndexNumber.Value.ToString(_usCulture)) + "</EpisodeNumber>"); + } + + if (episode.ParentIndexNumber.HasValue) + { + builder.Append("<SeasonNumber>" + SecurityElement.Escape(episode.ParentIndexNumber.Value.ToString(_usCulture)) + "</SeasonNumber>"); + } + + if (episode.PremiereDate.HasValue) + { + builder.Append("<FirstAired>" + SecurityElement.Escape(episode.PremiereDate.Value.ToString("yyyy-MM-dd")) + "</FirstAired>"); + } + + XmlHelpers.AddCommonNodes(item, builder); + XmlHelpers.AppendMediaInfo(episode, builder); + + builder.Append("</Item>"); + + var xmlFilePath = GetSavePath(item); + + XmlHelpers.Save(builder, xmlFilePath); + + // Set last refreshed so that the provider doesn't trigger after the file save + EpisodeProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); + } + + /// <summary> + /// Gets the save path. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + public string GetSavePath(BaseItem item) + { + var filename = Path.ChangeExtension(Path.GetFileName(item.Path), ".xml"); + + return Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename); + } + } +} diff --git a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs new file mode 100644 index 000000000..6790daa04 --- /dev/null +++ b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs @@ -0,0 +1,60 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using System.IO; +using System.Text; +using System.Threading; + +namespace MediaBrowser.Providers.Savers +{ + public class FolderXmlSaver : IMetadataSaver + { + /// <summary> + /// Supportses the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + public bool Supports(BaseItem item) + { + if (item.LocationType != LocationType.FileSystem) + { + return false; + } + + return item is Folder && !(item is Series) && !(item is BoxSet); + } + + /// <summary> + /// Saves the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public void Save(BaseItem item, CancellationToken cancellationToken) + { + var builder = new StringBuilder(); + + builder.Append("<Item>"); + + XmlHelpers.AddCommonNodes(item, builder); + + builder.Append("</Item>"); + + var xmlFilePath = GetSavePath(item); + + XmlHelpers.Save(builder, xmlFilePath); + } + + /// <summary> + /// Gets the save path. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + public string GetSavePath(BaseItem item) + { + return Path.Combine(item.Path, "folder.xml"); + } + } +} diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs index 7a618c74f..d3683d2b1 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs @@ -1,10 +1,12 @@ -using MediaBrowser.Controller.Entities; +using System.Text; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; +using MediaBrowser.Providers.Movies; +using System; using System.IO; using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Providers.Savers { @@ -42,15 +44,23 @@ namespace MediaBrowser.Providers.Savers /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - public Task Save(BaseItem item, CancellationToken cancellationToken) + public void Save(BaseItem item, CancellationToken cancellationToken) { - var video = (Video)item; + var builder = new StringBuilder(); + + builder.Append("<Title>"); + + XmlHelpers.AddCommonNodes(item, builder); + + builder.Append("</Title>"); var xmlFilePath = GetSavePath(item); - return Task.Run(() => { }); - } + XmlHelpers.Save(builder, xmlFilePath); + // Set last refreshed so that the provider doesn't trigger after the file save + MovieProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); + } public string GetSavePath(BaseItem item) { diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs new file mode 100644 index 000000000..53d4ac148 --- /dev/null +++ b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs @@ -0,0 +1,103 @@ +using System; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using System.IO; +using System.Security; +using System.Text; +using System.Threading; +using MediaBrowser.Providers.TV; + +namespace MediaBrowser.Providers.Savers +{ + public class SeriesXmlSaver : IMetadataSaver + { + /// <summary> + /// Supportses the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + public bool Supports(BaseItem item) + { + if (item.LocationType != LocationType.FileSystem) + { + return false; + } + + return item is Series; + } + + /// <summary> + /// Saves the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + public void Save(BaseItem item, CancellationToken cancellationToken) + { + var series = (Series)item; + + var builder = new StringBuilder(); + + builder.Append("<Series>"); + + var tvdb = item.GetProviderId(MetadataProviders.Tvdb); + + if (!string.IsNullOrEmpty(tvdb)) + { + builder.Append("<id>" + SecurityElement.Escape(tvdb) + "</id>"); + } + + if (!string.IsNullOrEmpty(item.Name)) + { + builder.Append("<SeriesName>" + SecurityElement.Escape(item.Name) + "</SeriesName>"); + } + + if (series.Status.HasValue) + { + builder.Append("<Status>" + SecurityElement.Escape(series.Status.Value.ToString()) + "</Status>"); + } + + if (series.Studios.Count > 0) + { + builder.Append("<Network>" + SecurityElement.Escape(item.Studios[0]) + "</Network>"); + } + + if (!string.IsNullOrEmpty(series.AirTime)) + { + builder.Append("<Airs_Time>" + SecurityElement.Escape(series.AirTime) + "</Airs_Time>"); + } + + if (series.AirDays.Count == 7) + { + builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape("Daily") + "</Airs_DayOfWeek>"); + } + else if (series.AirDays.Count > 0) + { + builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape(series.AirDays[0].ToString()) + "</Airs_DayOfWeek>"); + } + + XmlHelpers.AddCommonNodes(item, builder); + + builder.Append("</Series>"); + + var xmlFilePath = GetSavePath(item); + + XmlHelpers.Save(builder, xmlFilePath); + + // Set last refreshed so that the provider doesn't trigger after the file save + SeriesProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); + } + + /// <summary> + /// Gets the save path. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + public string GetSavePath(BaseItem item) + { + return Path.Combine(item.Path, "series.xml"); + } + } +} diff --git a/MediaBrowser.Providers/Savers/XmlHelpers.cs b/MediaBrowser.Providers/Savers/XmlHelpers.cs new file mode 100644 index 000000000..3838f6669 --- /dev/null +++ b/MediaBrowser.Providers/Savers/XmlHelpers.cs @@ -0,0 +1,346 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Model.Entities; +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Xml; + +namespace MediaBrowser.Providers.Savers +{ + /// <summary> + /// Class XmlHelpers + /// </summary> + public static class XmlHelpers + { + /// <summary> + /// The us culture + /// </summary> + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + /// <summary> + /// Saves the specified XML. + /// </summary> + /// <param name="xml">The XML.</param> + /// <param name="path">The path.</param> + public static void Save(StringBuilder xml, string path) + { + var xmlDocument = new XmlDocument(); + xmlDocument.LoadXml(xml.ToString()); + + //Add the new node to the document. + xmlDocument.InsertBefore(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "yes"), xmlDocument.DocumentElement); + + using (var streamWriter = new StreamWriter(path, false, Encoding.UTF8)) + { + xmlDocument.Save(streamWriter); + } + } + + /// <summary> + /// Adds the common nodes. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="builder">The builder.</param> + public static void AddCommonNodes(BaseItem item, StringBuilder builder) + { + if (!string.IsNullOrEmpty(item.OfficialRating)) + { + builder.Append("<ContentRating>" + SecurityElement.Escape(item.OfficialRating) + "</ContentRating>"); + builder.Append("<MPAARating>" + SecurityElement.Escape(item.OfficialRating) + "</MPAARating>"); + builder.Append("<certification>" + SecurityElement.Escape(item.OfficialRating) + "</certification>"); + } + + 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>"); + } + + if (!string.IsNullOrEmpty(item.DisplayMediaType)) + { + builder.Append("<Type>" + SecurityElement.Escape(item.DisplayMediaType) + "</Type>"); + } + + if (!string.IsNullOrEmpty(item.Overview)) + { + builder.Append("<Overview><![CDATA[" + item.Overview + "]]></Overview>"); + } + + if (!string.IsNullOrEmpty(item.CustomRating)) + { + builder.Append("<CustomRating>" + SecurityElement.Escape(item.CustomRating) + "</CustomRating>"); + } + + if (!string.IsNullOrEmpty(item.Name) && !(item is Episode)) + { + builder.Append("<LocalTitle>" + SecurityElement.Escape(item.Name) + "</LocalTitle>"); + } + + if (!string.IsNullOrEmpty(item.ForcedSortName)) + { + builder.Append("<SortTitle>" + SecurityElement.Escape(item.ForcedSortName) + "</SortTitle>"); + } + + if (item.Budget.HasValue) + { + builder.Append("<Budget>" + SecurityElement.Escape(item.Budget.Value.ToString(UsCulture)) + "</Budget>"); + } + + if (item.Revenue.HasValue) + { + builder.Append("<Revenue>" + SecurityElement.Escape(item.Revenue.Value.ToString(UsCulture)) + "</Revenue>"); + } + + if (item.CommunityRating.HasValue) + { + builder.Append("<Rating>" + SecurityElement.Escape(item.CommunityRating.Value.ToString(UsCulture)) + "</Rating>"); + } + + if (item.ProductionYear.HasValue) + { + builder.Append("<ProductionYear>" + SecurityElement.Escape(item.ProductionYear.Value.ToString(UsCulture)) + "</ProductionYear>"); + } + + if (!string.IsNullOrEmpty(item.HomePageUrl)) + { + builder.Append("<Website>" + SecurityElement.Escape(item.HomePageUrl) + "</Website>"); + } + + if (!string.IsNullOrEmpty(item.AspectRatio)) + { + builder.Append("<AspectRatio>" + SecurityElement.Escape(item.AspectRatio) + "</AspectRatio>"); + } + + if (!string.IsNullOrEmpty(item.Language)) + { + builder.Append("<Language>" + SecurityElement.Escape(item.Language) + "</Language>"); + } + + if (item.RunTimeTicks.HasValue) + { + var timespan = TimeSpan.FromTicks(item.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)) + { + builder.Append("<IMDB_ID>" + SecurityElement.Escape(imdb) + "</IMDB_ID>"); + builder.Append("<IMDB>" + SecurityElement.Escape(imdb) + "</IMDB>"); + builder.Append("<IMDbId>" + SecurityElement.Escape(imdb) + "</IMDbId>"); + } + + var tmdb = item.GetProviderId(MetadataProviders.Tmdb); + + if (!string.IsNullOrEmpty(tmdb)) + { + builder.Append("<TMDbId>" + SecurityElement.Escape(tmdb) + "</TMDbId>"); + } + + var tvcom = item.GetProviderId(MetadataProviders.Tvcom); + + if (!string.IsNullOrEmpty(tvcom)) + { + builder.Append("<TVcomId>" + SecurityElement.Escape(tvcom) + "</TVcomId>"); + } + + var rt = item.GetProviderId(MetadataProviders.RottenTomatoes); + + if (!string.IsNullOrEmpty(rt)) + { + builder.Append("<RottenTomatoesId>" + SecurityElement.Escape(rt) + "</RottenTomatoesId>"); + } + + var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection); + + if (!string.IsNullOrEmpty(tmdbCollection)) + { + builder.Append("<CollectionNumber>" + SecurityElement.Escape(tmdbCollection) + "</CollectionNumber>"); + } + + if (item.Genres.Count > 0) + { + builder.Append("<Genres>"); + + foreach (var genre in item.Genres) + { + builder.Append("<Genre>" + SecurityElement.Escape(genre) + "</Genre>"); + } + + builder.Append("</Genres>"); + } + + if (item.Studios.Count > 0) + { + builder.Append("<Studios>"); + + foreach (var studio in item.Studios) + { + builder.Append("<Studio>" + SecurityElement.Escape(studio) + "</Studio>"); + } + + builder.Append("</Studios>"); + } + + builder.Append("<Added>" + SecurityElement.Escape(item.DateCreated.ToString(UsCulture)) + "</Added>"); + } + + /// <summary> + /// Appends the media info. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="item">The item.</param> + /// <param name="builder">The builder.</param> + public static void AppendMediaInfo<T>(T item, StringBuilder builder) + where T : BaseItem, IHasMediaStreams + { + builder.Append("<MediaInfo>"); + + foreach (var stream in item.MediaStreams) + { + if (stream.Type == MediaStreamType.Video) + { + builder.Append("<Video>"); + + if (!string.IsNullOrEmpty(stream.Codec)) + { + builder.Append("<Codec>" + SecurityElement.Escape(stream.Codec) + "</Codec>"); + builder.Append("<FFCodec>" + SecurityElement.Escape(stream.Codec) + "</FFCodec>"); + } + + if (stream.BitRate.HasValue) + { + builder.Append("<BitRate>" + stream.BitRate.Value.ToString(UsCulture) + "</BitRate>"); + } + + if (stream.Width.HasValue) + { + builder.Append("<Width>" + stream.Width.Value.ToString(UsCulture) + "</Width>"); + } + + if (stream.Height.HasValue) + { + builder.Append("<Height>" + stream.Height.Value.ToString(UsCulture) + "</Height>"); + } + + if (!string.IsNullOrEmpty(stream.AspectRatio)) + { + builder.Append("<AspectRatio>" + SecurityElement.Escape(stream.AspectRatio) + "</AspectRatio>"); + } + + var framerate = stream.AverageFrameRate ?? stream.RealFrameRate; + + if (framerate.HasValue) + { + builder.Append("<FrameRate>" + framerate.Value.ToString(UsCulture) + "</FrameRate>"); + } + + if (!string.IsNullOrEmpty(stream.Language)) + { + builder.Append("<Language>" + SecurityElement.Escape(stream.Language) + "</Language>"); + } + + if (!string.IsNullOrEmpty(stream.ScanType)) + { + builder.Append("<ScanType>" + SecurityElement.Escape(stream.ScanType) + "</ScanType>"); + } + + if (item.RunTimeTicks.HasValue) + { + var timespan = TimeSpan.FromTicks(item.RunTimeTicks.Value); + + builder.Append("<Duration>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</Duration>"); + builder.Append("<DurationSeconds>" + Convert.ToInt32(timespan.TotalSeconds).ToString(UsCulture) + "</DurationSeconds>"); + } + + builder.Append("<Default>" + SecurityElement.Escape(stream.IsDefault.ToString()) + "</Default>"); + builder.Append("<Forced>" + SecurityElement.Escape(stream.IsForced.ToString()) + "</Forced>"); + + builder.Append("</Video>"); + } + else if (stream.Type == MediaStreamType.Audio) + { + builder.Append("<Audio>"); + + if (!string.IsNullOrEmpty(stream.Codec)) + { + builder.Append("<Codec>" + SecurityElement.Escape(stream.Codec) + "</Codec>"); + builder.Append("<FFCodec>" + SecurityElement.Escape(stream.Codec) + "</FFCodec>"); + } + + if (stream.Channels.HasValue) + { + builder.Append("<Channels>" + stream.Channels.Value.ToString(UsCulture) + "</Channels>"); + } + + if (stream.BitRate.HasValue) + { + builder.Append("<BitRate>" + stream.BitRate.Value.ToString(UsCulture) + "</BitRate>"); + } + + if (stream.SampleRate.HasValue) + { + builder.Append("<SamplingRate>" + stream.SampleRate.Value.ToString(UsCulture) + "</SamplingRate>"); + } + + if (!string.IsNullOrEmpty(stream.Language)) + { + builder.Append("<Language>" + SecurityElement.Escape(stream.Language) + "</Language>"); + } + + builder.Append("<Default>" + SecurityElement.Escape(stream.IsDefault.ToString()) + "</Default>"); + builder.Append("<Forced>" + SecurityElement.Escape(stream.IsForced.ToString()) + "</Forced>"); + + builder.Append("</Audio>"); + } + else if (stream.Type == MediaStreamType.Subtitle) + { + builder.Append("<Subtitle>"); + + if (!string.IsNullOrEmpty(stream.Language)) + { + builder.Append("<Language>" + SecurityElement.Escape(stream.Language) + "</Language>"); + } + + builder.Append("<Default>" + SecurityElement.Escape(stream.IsDefault.ToString()) + "</Default>"); + builder.Append("<Forced>" + SecurityElement.Escape(stream.IsForced.ToString()) + "</Forced>"); + + builder.Append("</Subtitle>"); + } + } + + builder.Append("</MediaInfo>"); + } + } +} diff --git a/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs b/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs index 498088eac..76deffa4e 100644 --- a/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs +++ b/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs @@ -16,9 +16,12 @@ namespace MediaBrowser.Providers.TV /// </summary> public class EpisodeProviderFromXml : BaseMetadataProvider { + internal static EpisodeProviderFromXml Current { get; private set; } + public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) { + Current = this; } /// <summary> diff --git a/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs b/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs index f1e188585..3aec76ad4 100644 --- a/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs +++ b/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs @@ -16,8 +16,12 @@ namespace MediaBrowser.Providers.TV /// </summary> public class SeriesProviderFromXml : BaseMetadataProvider { - public SeriesProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) + internal static SeriesProviderFromXml Current { get; private set; } + + public SeriesProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) { + Current = this; } /// <summary> diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index e174b9a23..d1b7634fb 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -74,12 +74,6 @@ namespace MediaBrowser.Server.Implementations.Library private IEnumerable<IBaseItemComparer> Comparers { get; set; } /// <summary> - /// Gets or sets the savers. - /// </summary> - /// <value>The savers.</value> - private IEnumerable<IMetadataSaver> Savers { get; set; } - - /// <summary> /// Gets the active item repository /// </summary> /// <value>The item repository.</value> @@ -197,15 +191,13 @@ 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<IMetadataSaver> savers) + IEnumerable<ILibraryPostScanTask> postscanTasks) { EntityResolutionIgnoreRules = rules; PluginFolderCreators = pluginFolders; @@ -214,7 +206,6 @@ namespace MediaBrowser.Server.Implementations.Library Comparers = itemComparers; PrescanTasks = prescanTasks; PostscanTasks = postscanTasks; - Savers = savers; } /// <summary> @@ -589,7 +580,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>UserRootFolder.</returns> public UserRootFolder GetUserRootFolder(string userRootPath) { - return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder)), typeof(UserRootFolder)) as UserRootFolder ?? + return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder)), typeof(UserRootFolder)) as UserRootFolder ?? (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath))); } @@ -649,7 +640,7 @@ namespace MediaBrowser.Server.Implementations.Library { return GetItemByName<MusicGenre>(ConfigurationManager.ApplicationPaths.MusicGenrePath, name, CancellationToken.None, allowSlowProviders); } - + /// <summary> /// Gets a Genre /// </summary> @@ -1001,7 +992,7 @@ namespace MediaBrowser.Server.Implementations.Library await RunPrescanTasks(progress, cancellationToken).ConfigureAwait(false); progress.Report(15); - + var innerProgress = new ActionableProgress<double>(); innerProgress.RegisterAction(pct => progress.Report(15 + pct * .65)); @@ -1010,7 +1001,7 @@ namespace MediaBrowser.Server.Implementations.Library await RootFolder.ValidateChildren(innerProgress, cancellationToken, recursive: true).ConfigureAwait(false); progress.Report(80); - + // Run post-scan tasks await RunPostScanTasks(progress, cancellationToken).ConfigureAwait(false); @@ -1044,7 +1035,7 @@ namespace MediaBrowser.Server.Implementations.Library progress.Report(2 + percent * .13); } }); - + try { await i.Run(innerProgress, cancellationToken); @@ -1301,11 +1292,7 @@ namespace MediaBrowser.Server.Implementations.Library foreach (var item in list) { UpdateItemInLibraryCache(item); - } - - foreach (var item in list) - { - await OnItemUpdated(item, CancellationToken.None).ConfigureAwait(false); + OnItemUpdated(item); } } @@ -1354,25 +1341,9 @@ namespace MediaBrowser.Server.Implementations.Library /// Called when [item updated]. /// </summary> /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - private async Task OnItemUpdated(BaseItem item, CancellationToken cancellationToken) + private void OnItemUpdated(BaseItem item) { - if (ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem) - { - foreach (var saver in Savers.Where(i => i.Supports(item))) - { - try - { - await saver.Save(item, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error in metadata saver", ex); - } - } - } - if (ItemUpdated != null) { try diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index beab6200f..3c1a7aa1e 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -3,7 +3,9 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; using System.Collections.Concurrent; @@ -58,6 +60,8 @@ 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> @@ -65,7 +69,8 @@ namespace MediaBrowser.Server.Implementations.Providers /// <param name="configurationManager">The configuration manager.</param> /// <param name="directoryWatchers">The directory watchers.</param> /// <param name="logManager">The log manager.</param> - public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager) + /// <param name="libraryManager">The library manager.</param> + public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager, ILibraryManager libraryManager) { _logger = logManager.GetLogger("ProviderManager"); _httpClient = httpClient; @@ -74,6 +79,37 @@ namespace MediaBrowser.Server.Implementations.Providers _remoteImageCache = new FileSystemRepository(configurationManager.ApplicationPaths.DownloadedImagesDataPath); configurationManager.ConfigurationUpdated += configurationManager_ConfigurationUpdated; + + libraryManager.ItemUpdated += libraryManager_ItemUpdated; + } + + void libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e) + { + var item = e.Item; + + if (ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem) + { + foreach (var saver in _savers.Where(i => i.Supports(item))) + { + var path = saver.GetSavePath(item); + + _directoryWatchers.TemporarilyIgnore(path); + + try + { + saver.Save(item, CancellationToken.None); + } + catch (Exception ex) + { + _logger.ErrorException("Error in metadata saver", ex); + } + finally + { + _directoryWatchers.RemoveTempIgnore(path); + } + } + } + } /// <summary> @@ -91,9 +127,12 @@ namespace MediaBrowser.Server.Implementations.Providers /// Adds the metadata providers. /// </summary> /// <param name="providers">The providers.</param> - public void AddMetadataProviders(IEnumerable<BaseMetadataProvider> providers) + /// <param name="savers">The savers.</param> + public void AddParts(IEnumerable<BaseMetadataProvider> providers, + IEnumerable<IMetadataSaver> savers) { MetadataProviders = providers.OrderBy(e => e.Priority).ToArray(); + _savers = savers; } /// <summary> diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 9f28006ba..09e33d7fd 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -271,7 +271,7 @@ namespace MediaBrowser.ServerApplication DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager); RegisterSingleInstance(DirectoryWatchers); - ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager); + ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, LibraryManager); RegisterSingleInstance(ProviderManager); RegisterSingleInstance<ILibrarySearchEngine>(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager)); @@ -397,10 +397,10 @@ namespace MediaBrowser.ServerApplication GetExports<IIntroProvider>(), GetExports<IBaseItemComparer>(), GetExports<ILibraryPrescanTask>(), - GetExports<ILibraryPostScanTask>(), - GetExports<IMetadataSaver>()); + GetExports<ILibraryPostScanTask>()); - ProviderManager.AddMetadataProviders(GetExports<BaseMetadataProvider>().ToArray()); + ProviderManager.AddParts(GetExports<BaseMetadataProvider>().ToArray(), + GetExports<IMetadataSaver>()); } /// <summary> |
