From 68bb977a74ffee6f3f7fa4f1af5ad0d98270e07f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 2 Feb 2014 11:59:14 -0500 Subject: [PATCH] separate musicbrainz from lastfm artist providers --- .../Library/IMetadataSaver.cs | 17 +-- .../Manager/ProviderManager.cs | 69 ++++++---- .../MediaBrowser.Providers.csproj | 1 + .../Music/LastfmArtistProvider.cs | 74 ++--------- .../Music/MusicBrainzArtistProvider.cs | 119 ++++++++++++++++++ .../Savers/AlbumXmlSaver.cs | 2 +- .../Savers/ArtistXmlSaver.cs | 2 +- .../Savers/BoxSetXmlSaver.cs | 2 +- .../Savers/ChannelXmlSaver.cs | 2 +- .../Savers/EpisodeXmlSaver.cs | 2 +- .../Savers/FolderXmlSaver.cs | 2 +- .../Savers/GameSystemXmlSaver.cs | 2 +- MediaBrowser.Providers/Savers/GameXmlSaver.cs | 2 +- .../Savers/MovieXmlSaver.cs | 2 +- .../Savers/PersonXmlSaver.cs | 2 +- .../Savers/SeasonXmlSaver.cs | 2 +- .../Savers/SeriesXmlSaver.cs | 2 +- 17 files changed, 196 insertions(+), 108 deletions(-) create mode 100644 MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs index 0c75e7edcf..75d318057e 100644 --- a/MediaBrowser.Controller/Library/IMetadataSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs @@ -22,13 +22,6 @@ namespace MediaBrowser.Controller.Library /// true if [is enabled for] [the specified item]; otherwise, false. bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType); - /// - /// Gets the save path. - /// - /// The item. - /// System.String. - string GetSavePath(IHasMetadata item); - /// /// Saves the specified item. /// @@ -37,4 +30,14 @@ namespace MediaBrowser.Controller.Library /// Task. void Save(IHasMetadata item, CancellationToken cancellationToken); } + + public interface IMetadataFileSaver : IMetadataSaver + { + /// + /// Gets the save path. + /// + /// The item. + /// System.String. + string GetSavePath(IHasMetadata item); + } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index b4f228a1d9..1eb17d75d2 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; @@ -582,7 +583,15 @@ namespace MediaBrowser.Providers.Manager list.Add(GetPluginSummary()); list.Add(GetPluginSummary()); list.Add(GetPluginSummary()); - + + list.Add(GetPluginSummary()); + list.Add(GetPluginSummary()); + + list.Add(GetPluginSummary()); + list.Add(GetPluginSummary()); + list.Add(GetPluginSummary()); + list.Add(GetPluginSummary()); + return list; } @@ -672,33 +681,49 @@ namespace MediaBrowser.Providers.Manager /// Task. public async Task SaveMetadata(IHasMetadata item, ItemUpdateType updateType) { - var locationType = item.LocationType; - if (locationType == LocationType.Remote || locationType == LocationType.Virtual) - { - throw new ArgumentException("Only file-system based items can save metadata."); - } - foreach (var saver in _savers.Where(i => i.IsEnabledFor(item, updateType))) { - var path = saver.GetSavePath(item); + var fileSaver = saver as IMetadataFileSaver; - var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1)); - - await semaphore.WaitAsync().ConfigureAwait(false); - - try + if (fileSaver != null) { - _libraryMonitor.ReportFileSystemChangeBeginning(path); - saver.Save(item, CancellationToken.None); + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) + { + throw new ArgumentException("Only file-system based items can save metadata."); + } + + var path = fileSaver.GetSavePath(item); + + var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1)); + + await semaphore.WaitAsync().ConfigureAwait(false); + + try + { + _libraryMonitor.ReportFileSystemChangeBeginning(path); + saver.Save(item, CancellationToken.None); + } + catch (Exception ex) + { + _logger.ErrorException("Error in metadata saver", ex); + } + finally + { + _libraryMonitor.ReportFileSystemChangeComplete(path, false); + semaphore.Release(); + } } - catch (Exception ex) + else { - _logger.ErrorException("Error in metadata saver", ex); - } - finally - { - _libraryMonitor.ReportFileSystemChangeComplete(path, false); - semaphore.Release(); + try + { + saver.Save(item, CancellationToken.None); + } + catch (Exception ex) + { + _logger.ErrorException("Error in metadata saver", ex); + } } } } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 1838b85566..9170e7268c 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -105,6 +105,7 @@ + diff --git a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs index 2924180ffe..2ae329c452 100644 --- a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using System; using System.Globalization; @@ -15,11 +14,10 @@ using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; namespace MediaBrowser.Providers.Music { - public class LastfmArtistProvider : IRemoteMetadataProvider + public class LastfmArtistProvider : IRemoteMetadataProvider, IHasOrder { private readonly IJsonSerializer _json; private readonly IHttpClient _httpClient; @@ -44,7 +42,7 @@ namespace MediaBrowser.Providers.Music { var result = new MetadataResult(); - var musicBrainzId = id.GetProviderId(MetadataProviders.Musicbrainz) ?? await FindId(id, cancellationToken).ConfigureAwait(false); + var musicBrainzId = id.GetProviderId(MetadataProviders.Musicbrainz); if (!String.IsNullOrWhiteSpace(musicBrainzId)) { @@ -123,69 +121,6 @@ namespace MediaBrowser.Providers.Music LastfmHelper.SaveImageInfo(_config.ApplicationPaths, _logger, musicBrainzId, url, imageSize); } - - private async Task FindId(ItemId item, CancellationToken cancellationToken) - { - try - { - // If we don't get anything, go directly to music brainz - return await FindIdFromMusicBrainz(item, cancellationToken).ConfigureAwait(false); - } - catch (HttpException e) - { - if (e.StatusCode.HasValue && e.StatusCode.Value == HttpStatusCode.BadRequest) - { - // They didn't like a character in the name. Handle the exception so that the provider doesn't keep retrying over and over - return null; - } - - throw; - } - } - - /// - /// Finds the id from music brainz. - /// - /// The item. - /// The cancellation token. - /// Task{System.String}. - private async Task FindIdFromMusicBrainz(ItemId item, CancellationToken cancellationToken) - { - // They seem to throw bad request failures on any term with a slash - var nameToSearch = item.Name.Replace('/', ' '); - - var url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch)); - - var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); - - var ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); - var node = doc.SelectSingleNode("//mb:artist-list/mb:artist/@id", ns); - - if (node != null && node.Value != null) - { - return node.Value; - } - - if (HasDiacritics(item.Name)) - { - // Try again using the search with accent characters url - url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); - - doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); - - ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); - node = doc.SelectSingleNode("//mb:artist-list/mb:artist/@id", ns); - - if (node != null && node.Value != null) - { - return node.Value; - } - } - - return null; - } /// /// Determines whether the specified text has diacritics. @@ -225,5 +160,10 @@ namespace MediaBrowser.Providers.Music { get { return "last.fm"; } } + + public int Order + { + get { return 1; } + } } } diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs new file mode 100644 index 0000000000..4cc408a00f --- /dev/null +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -0,0 +1,119 @@ +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using System; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace MediaBrowser.Providers.Music +{ + public class MusicBrainzArtistProvider : IRemoteMetadataProvider + { + public async Task> GetMetadata(ItemId id, CancellationToken cancellationToken) + { + var result = new MetadataResult(); + + var musicBrainzId = id.GetProviderId(MetadataProviders.Musicbrainz) ?? await FindId(id, cancellationToken).ConfigureAwait(false); + + if (!string.IsNullOrWhiteSpace(musicBrainzId)) + { + cancellationToken.ThrowIfCancellationRequested(); + + result.Item = new MusicArtist(); + result.HasMetadata = true; + + result.Item.SetProviderId(MetadataProviders.Musicbrainz, musicBrainzId); + } + + return result; + } + + /// + /// Finds the id from music brainz. + /// + /// The item. + /// The cancellation token. + /// Task{System.String}. + private async Task FindId(ItemId item, CancellationToken cancellationToken) + { + // They seem to throw bad request failures on any term with a slash + var nameToSearch = item.Name.Replace('/', ' '); + + var url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch)); + + var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + + var ns = new XmlNamespaceManager(doc.NameTable); + ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); + var node = doc.SelectSingleNode("//mb:artist-list/mb:artist/@id", ns); + + if (node != null && node.Value != null) + { + return node.Value; + } + + if (HasDiacritics(item.Name)) + { + // Try again using the search with accent characters url + url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); + + doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + + ns = new XmlNamespaceManager(doc.NameTable); + ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); + node = doc.SelectSingleNode("//mb:artist-list/mb:artist/@id", ns); + + if (node != null && node.Value != null) + { + return node.Value; + } + } + + return null; + } + + /// + /// Determines whether the specified text has diacritics. + /// + /// The text. + /// true if the specified text has diacritics; otherwise, false. + private bool HasDiacritics(string text) + { + return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); + } + + /// + /// Removes the diacritics. + /// + /// The text. + /// System.String. + private string RemoveDiacritics(string text) + { + return String.Concat( + text.Normalize(NormalizationForm.FormD) + .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != + UnicodeCategory.NonSpacingMark) + ).Normalize(NormalizationForm.FormC); + } + + /// + /// Encodes an URL. + /// + /// The name. + /// System.String. + private string UrlEncode(string name) + { + return WebUtility.UrlEncode(name); + } + + public string Name + { + get { return "MusicBrainz"; } + } + } +} diff --git a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs index 7eb56e40bc..34f91ba284 100644 --- a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs @@ -9,7 +9,7 @@ using System.Threading; namespace MediaBrowser.Providers.Savers { - class AlbumXmlSaver : IMetadataSaver + class AlbumXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs index efaa967512..5c40f3b388 100644 --- a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs @@ -12,7 +12,7 @@ using System.Threading; namespace MediaBrowser.Providers.Savers { - class ArtistXmlSaver : IMetadataSaver + class ArtistXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs b/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs index 078b1feba5..35c53115d0 100644 --- a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs @@ -10,7 +10,7 @@ using MediaBrowser.Controller.Providers; namespace MediaBrowser.Providers.Savers { - public class BoxSetXmlSaver : IMetadataSaver + public class BoxSetXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs b/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs index ed9a9bd6dd..3d8cbb4151 100644 --- a/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Providers.Savers /// /// Class PersonXmlSaver /// - public class ChannelXmlSaver : IMetadataSaver + public class ChannelXmlSaver : IMetadataFileSaver { /// /// Determines whether [is enabled for] [the specified item]. diff --git a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs index a0e3097185..c1f6bb7de4 100644 --- a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs @@ -12,7 +12,7 @@ using System.Threading; namespace MediaBrowser.Providers.Savers { - public class EpisodeXmlSaver : IMetadataSaver + public class EpisodeXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; private readonly IItemRepository _itemRepository; diff --git a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs index 6d384e410d..fe8eec3a59 100644 --- a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs @@ -12,7 +12,7 @@ using System.Threading; namespace MediaBrowser.Providers.Savers { - public class FolderXmlSaver : IMetadataSaver + public class FolderXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs b/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs index d0d2ffcd0e..38af319236 100644 --- a/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs @@ -10,7 +10,7 @@ using System.Threading; namespace MediaBrowser.Providers.Savers { - public class GameSystemXmlSaver : IMetadataSaver + public class GameSystemXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/Savers/GameXmlSaver.cs b/MediaBrowser.Providers/Savers/GameXmlSaver.cs index 4e3f948a3c..b9abb37733 100644 --- a/MediaBrowser.Providers/Savers/GameXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/GameXmlSaver.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Savers /// /// Saves game.xml for games /// - public class GameXmlSaver : IMetadataSaver + public class GameXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs index f65c8f5df1..2c19cb628a 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.Savers /// /// Saves movie.xml for movies, trailers and music videos /// - public class MovieXmlSaver : IMetadataSaver + public class MovieXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; private readonly IItemRepository _itemRepository; diff --git a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs index 676f2dae05..12703aa3c5 100644 --- a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Providers.Savers /// /// Class PersonXmlSaver /// - public class PersonXmlSaver : IMetadataSaver + public class PersonXmlSaver : IMetadataFileSaver { public string Name { diff --git a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs b/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs index 52f310f0f7..6c4d3fb19a 100644 --- a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs @@ -9,7 +9,7 @@ using System.Threading; namespace MediaBrowser.Providers.Savers { - public class SeasonXmlSaver : IMetadataSaver + public class SeasonXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs index d472c2da25..21019fa0ed 100644 --- a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs @@ -11,7 +11,7 @@ using System.Threading; namespace MediaBrowser.Providers.Savers { - public class SeriesXmlSaver : IMetadataSaver + public class SeriesXmlSaver : IMetadataFileSaver { private readonly IServerConfigurationManager _config;