diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index e72ffdb0b..dbfeff747 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1142,6 +1142,19 @@ namespace MediaBrowser.Controller.Entities } } + public void AddTag(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + if (!Tags.Contains(name, StringComparer.OrdinalIgnoreCase)) + { + Tags.Add(name); + } + } + /// /// Adds a tagline to the item /// diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index d06c89a01..c9efd2737 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -65,6 +65,7 @@ namespace MediaBrowser.Controller.Providers item.Studios.Clear(); item.Genres.Clear(); item.People.Clear(); + item.Tags.Clear(); // Use european encoding as it will accept more characters using (var streamReader = new StreamReader(metadataFile, Encoding.GetEncoding("ISO-8859-1"))) @@ -397,6 +398,7 @@ namespace MediaBrowser.Controller.Providers break; } + case "PremiereDate": case "FirstAired": { var firstAired = reader.ReadElementContentAsString(); @@ -453,6 +455,10 @@ namespace MediaBrowser.Controller.Providers FetchFromGenresNode(reader.ReadSubtree(), item); break; + case "Tags": + FetchFromTagsNode(reader.ReadSubtree(), item); + break; + case "Persons": FetchDataFromPersonsNode(reader.ReadSubtree(), item); break; @@ -539,6 +545,35 @@ namespace MediaBrowser.Controller.Providers } } + private void FetchFromTagsNode(XmlReader reader, T item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Tag": + { + var tag = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(tag)) + { + item.AddTagline(tag); + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + /// /// Fetches the data from persons node. /// diff --git a/MediaBrowser.Providers/FolderProviderFromXml.cs b/MediaBrowser.Providers/FolderProviderFromXml.cs index 95f7d9b73..9edaf7767 100644 --- a/MediaBrowser.Providers/FolderProviderFromXml.cs +++ b/MediaBrowser.Providers/FolderProviderFromXml.cs @@ -15,7 +15,8 @@ namespace MediaBrowser.Providers /// public class FolderProviderFromXml : BaseMetadataProvider { - public FolderProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) + public FolderProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) { } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index ec0d0c2b9..bf96b22c1 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -64,6 +64,7 @@ + @@ -74,14 +75,15 @@ - + + - + diff --git a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs index 51d3e3edf..e7bde94c4 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs @@ -1,11 +1,9 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Providers.Music; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Music; using System; using System.Collections.Generic; using System.Globalization; @@ -14,7 +12,6 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.Movies { diff --git a/MediaBrowser.Providers/Music/ArtistProviderFromXml.cs b/MediaBrowser.Providers/Music/ArtistProviderFromXml.cs new file mode 100644 index 000000000..a5b67e7ca --- /dev/null +++ b/MediaBrowser.Providers/Music/ArtistProviderFromXml.cs @@ -0,0 +1,107 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.Music +{ + class ArtistProviderFromXml : BaseMetadataProvider + { + public ArtistProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) + { + } + + /// + /// Supportses the specified item. + /// + /// The item. + /// true if XXXX, false otherwise + public override bool Supports(BaseItem item) + { + return (item is Artist || item is MusicArtist) && item.LocationType == LocationType.FileSystem; + } + + /// + /// Gets the priority. + /// + /// The priority. + public override MetadataProviderPriority Priority + { + get { return MetadataProviderPriority.First; } + } + + /// + /// Override this to return the date that should be compared to the last refresh date + /// to determine if this provider should be re-fetched. + /// + /// The item. + /// DateTime. + protected override DateTime CompareDate(BaseItem item) + { + var entry = item.MetaLocation != null ? item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "artist.xml")) : null; + return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; + } + + /// + /// Fetches metadata and returns true or false indicating if any work that requires persistence was done + /// + /// The item. + /// if set to true [force]. + /// The cancellation token. + /// Task{System.Boolean}. + public override Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + { + return Fetch(item, cancellationToken); + } + + /// + /// Fetches the specified item. + /// + /// The item. + /// The cancellation token. + /// true if XXXX, false otherwise + private async Task Fetch(BaseItem item, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "artist.xml")); + + if (metadataFile != null) + { + var path = metadataFile.FullName; + + await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + var artist = item as Artist; + + if (artist != null) + { + new BaseItemXmlParser(Logger).Fetch(artist, path, cancellationToken); + } + else + { + new BaseItemXmlParser(Logger).Fetch((MusicArtist)item, path, cancellationToken); + } + } + finally + { + XmlParsingResourcePool.Release(); + } + + SetLastRefreshed(item, DateTime.UtcNow); + return true; + } + + return false; + } + } +} diff --git a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs b/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs index 10775c689..84a3c3cd3 100644 --- a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs @@ -7,12 +7,11 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; +using MoreLinq; using System; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MoreLinq; namespace MediaBrowser.Providers.Music { @@ -20,18 +19,9 @@ namespace MediaBrowser.Providers.Music { private static readonly Task BlankId = Task.FromResult(""); - private readonly IProviderManager _providerManager; - - /// - /// The name of the local json meta file for this item type - /// - protected string LocalMetaFileName { get; set; } - - public LastfmAlbumProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public LastfmAlbumProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) : base(jsonSerializer, httpClient, logManager, configurationManager) { - _providerManager = providerManager; - LocalMetaFileName = LastfmHelper.LocalAlbumMetaFileName; } protected override Task FindId(BaseItem item, CancellationToken cancellationToken) @@ -40,6 +30,11 @@ namespace MediaBrowser.Providers.Music return BlankId; } + private bool HasAltMeta(BaseItem item) + { + return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName("album.xml"); + } + /// /// Needses the refresh internal. /// @@ -48,6 +43,11 @@ namespace MediaBrowser.Providers.Music /// true if XXXX, false otherwise protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { + if (HasAltMeta(item)) + { + return false; + } + // If song metadata has changed and we don't have an mbid, refresh if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz)) && GetComparisonData(item as MusicAlbum) != providerInfo.FileStamp) @@ -65,17 +65,6 @@ namespace MediaBrowser.Providers.Music if (result != null && result.album != null) { LastfmHelper.ProcessAlbumData(item, result.album); - //And save locally if indicated - if (ConfigurationManager.Configuration.SaveLocalMeta) - { - var ms = new MemoryStream(); - JsonSerializer.SerializeToStream(result.album, ms); - - cancellationToken.ThrowIfCancellationRequested(); - - await _providerManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false); - - } } BaseProviderInfo data; diff --git a/MediaBrowser.Providers/Music/LastfmArtistByNameProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistByNameProvider.cs index 7eb28b9b9..d3bc28d3c 100644 --- a/MediaBrowser.Providers/Music/LastfmArtistByNameProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmArtistByNameProvider.cs @@ -3,7 +3,6 @@ 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; using MediaBrowser.Model.Serialization; @@ -26,10 +25,9 @@ namespace MediaBrowser.Providers.Music /// The HTTP client. /// The log manager. /// The configuration manager. - /// The provider manager. /// The library manager. - public LastfmArtistByNameProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, ILibraryManager libraryManager) - : base(jsonSerializer, httpClient, logManager, configurationManager, providerManager, libraryManager) + public LastfmArtistByNameProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, ILibraryManager libraryManager) + : base(jsonSerializer, httpClient, logManager, configurationManager, libraryManager) { } diff --git a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs index 0281b8c0b..ce1e0f467 100644 --- a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Providers.Music; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; @@ -26,37 +25,40 @@ namespace MediaBrowser.Providers.Music /// public class LastfmArtistProvider : LastfmBaseProvider { - /// - /// The _provider manager - /// - private readonly IProviderManager _providerManager; /// /// The _library manager /// protected readonly ILibraryManager LibraryManager; /// - /// The name of the local json meta file for this item type - /// - protected string LocalMetaFileName { get; set; } - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The json serializer. /// The HTTP client. /// The log manager. /// The configuration manager. - /// The provider manager. /// The library manager. - public LastfmArtistProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, ILibraryManager libraryManager) + public LastfmArtistProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, ILibraryManager libraryManager) : base(jsonSerializer, httpClient, logManager, configurationManager) { - _providerManager = providerManager; LibraryManager = libraryManager; - LocalMetaFileName = LastfmHelper.LocalArtistMetaFileName; } + protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) + { + if (HasAltMeta(item)) + { + return false; + } + + return base.NeedsRefreshInternal(item, providerInfo); + } + + private bool HasAltMeta(BaseItem item) + { + return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName("artist.xml"); + } + /// /// Finds the id. /// @@ -259,21 +261,6 @@ namespace MediaBrowser.Providers.Music if (result != null && result.artist != null) { LastfmHelper.ProcessArtistData(item, result.artist); - //And save locally if indicated - if (SaveLocalMeta) - { - var ms = new MemoryStream(); - JsonSerializer.SerializeToStream(result.artist, ms); - - if (cancellationToken.IsCancellationRequested) - { - ms.Dispose(); - cancellationToken.ThrowIfCancellationRequested(); - } - - await _providerManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false); - - } } } diff --git a/MediaBrowser.Providers/Music/LastfmHelper.cs b/MediaBrowser.Providers/Music/LastfmHelper.cs index e8541aa5f..e3fe49001 100644 --- a/MediaBrowser.Providers/Music/LastfmHelper.cs +++ b/MediaBrowser.Providers/Music/LastfmHelper.cs @@ -1,6 +1,5 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers.Music; using MediaBrowser.Model.Entities; using System; using System.Linq; @@ -9,9 +8,6 @@ namespace MediaBrowser.Providers.Music { public static class LastfmHelper { - public static string LocalArtistMetaFileName = "lastfmartist.json"; - public static string LocalAlbumMetaFileName = "lastfmalbum.json"; - public static void ProcessArtistData(BaseItem artist, LastfmArtist data) { var yearFormed = 0; diff --git a/MediaBrowser.Providers/Music/MusicArtistProviderFromJson.cs b/MediaBrowser.Providers/Music/MusicArtistProviderFromJson.cs deleted file mode 100644 index 687b8410e..000000000 --- a/MediaBrowser.Providers/Music/MusicArtistProviderFromJson.cs +++ /dev/null @@ -1,101 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Providers.Music; - -namespace MediaBrowser.Controller.Providers.Music -{ - public class MusicArtistProviderFromJson : BaseMetadataProvider - { - /// - /// Gets the json serializer. - /// - /// The json serializer. - protected IJsonSerializer JsonSerializer { get; private set; } - - public MusicArtistProviderFromJson(IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - if (jsonSerializer == null) - { - throw new ArgumentNullException("jsonSerializer"); - } - JsonSerializer = jsonSerializer; - - } - - public override Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LastfmHelper.LocalArtistMetaFileName)); - if (entry != null) - { - // read in our saved meta and pass to processing function - var data = JsonSerializer.DeserializeFromFile(entry.FullName); - - cancellationToken.ThrowIfCancellationRequested(); - - LastfmHelper.ProcessArtistData(item, data); - - item.SetProviderId(MetadataProviders.Musicbrainz, data.mbid); - - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } - return FalseTaskResult; - } - - public override MetadataProviderPriority Priority - { - get - { - return MetadataProviderPriority.First; - } - } - - public override bool Supports(BaseItem item) - { - return false; - } - - public override bool RequiresInternet - { - get - { - return false; - } - } - - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (!item.ResolveArgs.ContainsMetaFileByName(LastfmHelper.LocalArtistMetaFileName)) - { - return false; // nothing to read - } - - // Need to re-override to jump over intermediate implementation - return CompareDate(item) > providerInfo.LastRefreshed; - } - - /// - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// - /// The item. - /// DateTime. - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LastfmHelper.LocalArtistMetaFileName)); - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - } -} diff --git a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs new file mode 100644 index 000000000..829a80d31 --- /dev/null +++ b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs @@ -0,0 +1,77 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using MediaBrowser.Providers.Movies; +using System; +using System.IO; +using System.Text; +using System.Threading; + +namespace MediaBrowser.Providers.Savers +{ + class AlbumXmlSaver : IMetadataSaver + { + private readonly IServerConfigurationManager _config; + + public AlbumXmlSaver(IServerConfigurationManager config) + { + _config = config; + } + + /// + /// Supportses the specified item. + /// + /// The item. + /// true if XXXX, false otherwise + public bool Supports(BaseItem item) + { + if (item.LocationType != LocationType.FileSystem) + { + return false; + } + + if (item is MusicAlbum) + { + return _config.Configuration.SaveLocalMeta; + } + + return false; + } + + /// + /// Saves the specified item. + /// + /// The item. + /// The cancellation token. + /// Task. + public void Save(BaseItem item, CancellationToken cancellationToken) + { + var builder = new StringBuilder(); + + builder.Append(""); + + XmlSaverHelpers.AddCommonNodes(item, builder); + + builder.Append(""); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath); + + // Set last refreshed so that the provider doesn't trigger after the file save + PersonProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); + } + + /// + /// Gets the save path. + /// + /// The item. + /// System.String. + public string GetSavePath(BaseItem item) + { + return Path.Combine(item.Path, "album.xml"); + } + } +} diff --git a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs new file mode 100644 index 000000000..1ad929731 --- /dev/null +++ b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs @@ -0,0 +1,77 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; +using MediaBrowser.Providers.Movies; +using System; +using System.IO; +using System.Text; +using System.Threading; + +namespace MediaBrowser.Providers.Savers +{ + class ArtistXmlSaver : IMetadataSaver + { + private readonly IServerConfigurationManager _config; + + public ArtistXmlSaver(IServerConfigurationManager config) + { + _config = config; + } + + /// + /// Supportses the specified item. + /// + /// The item. + /// true if XXXX, false otherwise + public bool Supports(BaseItem item) + { + if (item.LocationType != LocationType.FileSystem) + { + return false; + } + + if (item is MusicArtist) + { + return _config.Configuration.SaveLocalMeta; + } + + return item is Artist; + } + + /// + /// Saves the specified item. + /// + /// The item. + /// The cancellation token. + /// Task. + public void Save(BaseItem item, CancellationToken cancellationToken) + { + var builder = new StringBuilder(); + + builder.Append(""); + + XmlSaverHelpers.AddCommonNodes(item, builder); + + builder.Append(""); + + var xmlFilePath = GetSavePath(item); + + XmlSaverHelpers.Save(builder, xmlFilePath); + + // Set last refreshed so that the provider doesn't trigger after the file save + PersonProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); + } + + /// + /// Gets the save path. + /// + /// The item. + /// System.String. + public string GetSavePath(BaseItem item) + { + return Path.Combine(item.Path, "artist.xml"); + } + } +} diff --git a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs index c6e960332..f5b5a5d44 100644 --- a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs @@ -65,14 +65,14 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(episode.PremiereDate.Value.ToString("yyyy-MM-dd")) + ""); } - XmlHelpers.AddCommonNodes(item, builder); - XmlHelpers.AppendMediaInfo(episode, builder); + XmlSaverHelpers.AddCommonNodes(item, builder); + XmlSaverHelpers.AppendMediaInfo(episode, builder); builder.Append(""); var xmlFilePath = GetSavePath(item); - XmlHelpers.Save(builder, xmlFilePath); + XmlSaverHelpers.Save(builder, xmlFilePath); // Set last refreshed so that the provider doesn't trigger after the file save EpisodeProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); diff --git a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs index 6790daa04..f348fccca 100644 --- a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -11,6 +13,13 @@ namespace MediaBrowser.Providers.Savers { public class FolderXmlSaver : IMetadataSaver { + private readonly IServerConfigurationManager _config; + + public FolderXmlSaver(IServerConfigurationManager config) + { + _config = config; + } + /// /// Supportses the specified item. /// @@ -18,12 +27,12 @@ namespace MediaBrowser.Providers.Savers /// true if XXXX, false otherwise public bool Supports(BaseItem item) { - if (item.LocationType != LocationType.FileSystem) + if (!_config.Configuration.SaveLocalMeta || item.LocationType != LocationType.FileSystem) { return false; } - return item is Folder && !(item is Series) && !(item is BoxSet); + return item is Folder && !(item is Series) && !(item is BoxSet) && !(item is MusicArtist) && !(item is MusicAlbum); } /// @@ -38,13 +47,13 @@ namespace MediaBrowser.Providers.Savers builder.Append(""); - XmlHelpers.AddCommonNodes(item, builder); + XmlSaverHelpers.AddCommonNodes(item, builder); builder.Append(""); var xmlFilePath = GetSavePath(item); - XmlHelpers.Save(builder, xmlFilePath); + XmlSaverHelpers.Save(builder, xmlFilePath); } /// diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs index d3683d2b1..c83a978d7 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs @@ -1,4 +1,4 @@ -using System.Text; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; @@ -6,6 +6,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Providers.Movies; using System; using System.IO; +using System.Text; using System.Threading; namespace MediaBrowser.Providers.Savers @@ -15,6 +16,13 @@ namespace MediaBrowser.Providers.Savers /// public class MovieXmlSaver : IMetadataSaver { + private readonly IServerConfigurationManager _config; + + public MovieXmlSaver(IServerConfigurationManager config) + { + _config = config; + } + /// /// Supportses the specified item. /// @@ -22,7 +30,7 @@ namespace MediaBrowser.Providers.Savers /// true if XXXX, false otherwise public bool Supports(BaseItem item) { - if (item.LocationType != LocationType.FileSystem) + if (!_config.Configuration.SaveLocalMeta || item.LocationType != LocationType.FileSystem) { return false; } @@ -50,13 +58,13 @@ namespace MediaBrowser.Providers.Savers builder.Append(""); - XmlHelpers.AddCommonNodes(item, builder); + XmlSaverHelpers.AddCommonNodes(item, builder); builder.Append(""); var xmlFilePath = GetSavePath(item); - XmlHelpers.Save(builder, xmlFilePath); + XmlSaverHelpers.Save(builder, xmlFilePath); // Set last refreshed so that the provider doesn't trigger after the file save MovieProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); diff --git a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs index 03b535312..fb11c148a 100644 --- a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs @@ -41,13 +41,13 @@ namespace MediaBrowser.Providers.Savers builder.Append(""); - XmlHelpers.AddCommonNodes(item, builder); + XmlSaverHelpers.AddCommonNodes(item, builder); builder.Append(""); var xmlFilePath = GetSavePath(item); - XmlHelpers.Save(builder, xmlFilePath); + XmlSaverHelpers.Save(builder, xmlFilePath); // Set last refreshed so that the provider doesn't trigger after the file save PersonProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs index 53d4ac148..b2ac44b90 100644 --- a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs @@ -1,4 +1,5 @@ using System; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -13,6 +14,13 @@ namespace MediaBrowser.Providers.Savers { public class SeriesXmlSaver : IMetadataSaver { + private readonly IServerConfigurationManager _config; + + public SeriesXmlSaver(IServerConfigurationManager config) + { + _config = config; + } + /// /// Supportses the specified item. /// @@ -20,7 +28,7 @@ namespace MediaBrowser.Providers.Savers /// true if XXXX, false otherwise public bool Supports(BaseItem item) { - if (item.LocationType != LocationType.FileSystem) + if (!_config.Configuration.SaveLocalMeta || item.LocationType != LocationType.FileSystem) { return false; } @@ -78,13 +86,13 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(series.AirDays[0].ToString()) + ""); } - XmlHelpers.AddCommonNodes(item, builder); + XmlSaverHelpers.AddCommonNodes(item, builder); builder.Append(""); var xmlFilePath = GetSavePath(item); - XmlHelpers.Save(builder, xmlFilePath); + XmlSaverHelpers.Save(builder, xmlFilePath); // Set last refreshed so that the provider doesn't trigger after the file save SeriesProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow); diff --git a/MediaBrowser.Providers/Savers/XmlHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs similarity index 92% rename from MediaBrowser.Providers/Savers/XmlHelpers.cs rename to MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index 3838f6669..a58da0710 100644 --- a/MediaBrowser.Providers/Savers/XmlHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -4,7 +4,6 @@ using MediaBrowser.Model.Entities; using System; using System.Globalization; using System.IO; -using System.Linq; using System.Security; using System.Text; using System.Xml; @@ -14,7 +13,7 @@ namespace MediaBrowser.Providers.Savers /// /// Class XmlHelpers /// - public static class XmlHelpers + public static class XmlSaverHelpers { /// /// The us culture @@ -34,6 +33,13 @@ namespace MediaBrowser.Providers.Savers //Add the new node to the document. xmlDocument.InsertBefore(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "yes"), xmlDocument.DocumentElement); + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + using (var streamWriter = new StreamWriter(path, false, Encoding.UTF8)) { xmlDocument.Save(streamWriter); @@ -94,6 +100,11 @@ namespace MediaBrowser.Providers.Savers { builder.Append("" + SecurityElement.Escape(item.ForcedSortName) + ""); } + + if (item.PremiereDate.HasValue) + { + builder.Append("" + SecurityElement.Escape(item.PremiereDate.Value.ToString("yyyy-MM-dd")) + ""); + } if (item.Budget.HasValue) { @@ -182,6 +193,13 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(rt) + ""); } + var mbz = item.GetProviderId(MetadataProviders.Musicbrainz); + + if (!string.IsNullOrEmpty(mbz)) + { + builder.Append("" + SecurityElement.Escape(mbz) + ""); + } + var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection); if (!string.IsNullOrEmpty(tmdbCollection)) @@ -213,6 +231,18 @@ namespace MediaBrowser.Providers.Savers builder.Append(""); } + if (item.Tags.Count > 0) + { + builder.Append(""); + + foreach (var tag in item.Tags) + { + builder.Append("" + SecurityElement.Escape(tag) + ""); + } + + builder.Append(""); + } + builder.Append("" + SecurityElement.Escape(item.DateCreated.ToString(UsCulture)) + ""); } diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 395a4298b..01c0659b6 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -83,40 +83,38 @@ namespace MediaBrowser.Server.Implementations.Providers 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. - void libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e) + async void libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e) { var item = e.Item; - if (ConfigurationManager.Configuration.SaveLocalMeta) + foreach (var saver in _savers.Where(i => i.Supports(item))) { - if (item.LocationType != LocationType.FileSystem) - { - return; - } + var path = saver.GetSavePath(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); - - try - { - saver.Save(item, CancellationToken.None); - } - catch (Exception ex) - { - _logger.ErrorException("Error in metadata saver", ex); - } - finally - { - _directoryWatchers.RemoveTempIgnore(path); - } + saver.Save(item, CancellationToken.None); + } + catch (Exception ex) + { + _logger.ErrorException("Error in metadata saver", ex); + } + finally + { + _directoryWatchers.RemoveTempIgnore(path); + semaphore.Release(); } } }