separate musicbrainz from lastfm artist providers

pull/702/head
Luke Pulverenti 10 years ago
parent 5ea002771a
commit 68bb977a74

@ -22,13 +22,6 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType);
/// <summary>
/// Gets the save path.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
string GetSavePath(IHasMetadata item);
/// <summary>
/// Saves the specified item.
/// </summary>
@ -37,4 +30,14 @@ namespace MediaBrowser.Controller.Library
/// <returns>Task.</returns>
void Save(IHasMetadata item, CancellationToken cancellationToken);
}
public interface IMetadataFileSaver : IMetadataSaver
{
/// <summary>
/// Gets the save path.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
string GetSavePath(IHasMetadata item);
}
}

@ -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<Studio>());
list.Add(GetPluginSummary<GameGenre>());
list.Add(GetPluginSummary<MusicGenre>());
list.Add(GetPluginSummary<AdultVideo>());
list.Add(GetPluginSummary<MusicVideo>());
list.Add(GetPluginSummary<LiveTvChannel>());
list.Add(GetPluginSummary<LiveTvProgram>());
list.Add(GetPluginSummary<LiveTvVideoRecording>());
list.Add(GetPluginSummary<LiveTvAudioRecording>());
return list;
}
@ -672,33 +681,49 @@ namespace MediaBrowser.Providers.Manager
/// <returns>Task.</returns>
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));
if (fileSaver != null)
{
var locationType = item.LocationType;
if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
{
throw new ArgumentException("Only file-system based items can save metadata.");
}
await semaphore.WaitAsync().ConfigureAwait(false);
var path = fileSaver.GetSavePath(item);
try
{
_libraryMonitor.ReportFileSystemChangeBeginning(path);
saver.Save(item, CancellationToken.None);
}
catch (Exception ex)
{
_logger.ErrorException("Error in metadata saver", ex);
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();
}
}
finally
else
{
_libraryMonitor.ReportFileSystemChangeComplete(path, false);
semaphore.Release();
try
{
saver.Save(item, CancellationToken.None);
}
catch (Exception ex)
{
_logger.ErrorException("Error in metadata saver", ex);
}
}
}
}

@ -105,6 +105,7 @@
<Compile Include="Music\AlbumMetadataService.cs" />
<Compile Include="Music\ArtistMetadataService.cs" />
<Compile Include="Music\LastfmArtistProvider.cs" />
<Compile Include="Music\MusicBrainzArtistProvider.cs" />
<Compile Include="People\MovieDbPersonImageProvider.cs" />
<Compile Include="Movies\MovieUpdatesPrescanTask.cs" />
<Compile Include="Movies\MovieXmlParser.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<MusicArtist>
public class LastfmArtistProvider : IRemoteMetadataProvider<MusicArtist>, IHasOrder
{
private readonly IJsonSerializer _json;
private readonly IHttpClient _httpClient;
@ -44,7 +42,7 @@ namespace MediaBrowser.Providers.Music
{
var result = new MetadataResult<MusicArtist>();
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<string> 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;
}
}
/// <summary>
/// Finds the id from music brainz.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.String}.</returns>
private async Task<string> 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;
}
/// <summary>
/// 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; }
}
}
}

@ -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<MusicArtist>
{
public async Task<MetadataResult<MusicArtist>> GetMetadata(ItemId id, CancellationToken cancellationToken)
{
var result = new MetadataResult<MusicArtist>();
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;
}
/// <summary>
/// Finds the id from music brainz.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.String}.</returns>
private async Task<string> 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;
}
/// <summary>
/// Determines whether the specified text has diacritics.
/// </summary>
/// <param name="text">The text.</param>
/// <returns><c>true</c> if the specified text has diacritics; otherwise, <c>false</c>.</returns>
private bool HasDiacritics(string text)
{
return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal);
}
/// <summary>
/// Removes the diacritics.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>System.String.</returns>
private string RemoveDiacritics(string text)
{
return String.Concat(
text.Normalize(NormalizationForm.FormD)
.Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
UnicodeCategory.NonSpacingMark)
).Normalize(NormalizationForm.FormC);
}
/// <summary>
/// Encodes an URL.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>System.String.</returns>
private string UrlEncode(string name)
{
return WebUtility.UrlEncode(name);
}
public string Name
{
get { return "MusicBrainz"; }
}
}
}

@ -9,7 +9,7 @@ using System.Threading;
namespace MediaBrowser.Providers.Savers
{
class AlbumXmlSaver : IMetadataSaver
class AlbumXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

@ -12,7 +12,7 @@ using System.Threading;
namespace MediaBrowser.Providers.Savers
{
class ArtistXmlSaver : IMetadataSaver
class ArtistXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

@ -10,7 +10,7 @@ using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Providers.Savers
{
public class BoxSetXmlSaver : IMetadataSaver
public class BoxSetXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

@ -11,7 +11,7 @@ namespace MediaBrowser.Providers.Savers
/// <summary>
/// Class PersonXmlSaver
/// </summary>
public class ChannelXmlSaver : IMetadataSaver
public class ChannelXmlSaver : IMetadataFileSaver
{
/// <summary>
/// Determines whether [is enabled for] [the specified item].

@ -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;

@ -12,7 +12,7 @@ using System.Threading;
namespace MediaBrowser.Providers.Savers
{
public class FolderXmlSaver : IMetadataSaver
public class FolderXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

@ -10,7 +10,7 @@ using System.Threading;
namespace MediaBrowser.Providers.Savers
{
public class GameSystemXmlSaver : IMetadataSaver
public class GameSystemXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Savers
/// <summary>
/// Saves game.xml for games
/// </summary>
public class GameXmlSaver : IMetadataSaver
public class GameXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.Savers
/// <summary>
/// Saves movie.xml for movies, trailers and music videos
/// </summary>
public class MovieXmlSaver : IMetadataSaver
public class MovieXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;
private readonly IItemRepository _itemRepository;

@ -12,7 +12,7 @@ namespace MediaBrowser.Providers.Savers
/// <summary>
/// Class PersonXmlSaver
/// </summary>
public class PersonXmlSaver : IMetadataSaver
public class PersonXmlSaver : IMetadataFileSaver
{
public string Name
{

@ -9,7 +9,7 @@ using System.Threading;
namespace MediaBrowser.Providers.Savers
{
public class SeasonXmlSaver : IMetadataSaver
public class SeasonXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

@ -11,7 +11,7 @@ using System.Threading;
namespace MediaBrowser.Providers.Savers
{
public class SeriesXmlSaver : IMetadataSaver
public class SeriesXmlSaver : IMetadataFileSaver
{
private readonly IServerConfigurationManager _config;

Loading…
Cancel
Save