implement music identification

This commit is contained in:
Luke Pulverenti 2014-03-13 23:23:58 -04:00
parent 45a1113d8f
commit 87ebe39107
4 changed files with 209 additions and 51 deletions

View File

@ -3,6 +3,7 @@ using MediaBrowser.Common.IO;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -67,6 +68,18 @@ namespace MediaBrowser.Api
{ {
} }
[Route("/Items/RemoteSearch/MusicArtist", "POST")]
[Api(Description = "Gets external id infos for an item")]
public class GetMusicArtistRemoteSearchResults : RemoteSearchQuery<ArtistInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/MusicAlbum", "POST")]
[Api(Description = "Gets external id infos for an item")]
public class GetMusicAlbumRemoteSearchResults : RemoteSearchQuery<AlbumInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/Person", "POST")] [Route("/Items/RemoteSearch/Person", "POST")]
[Api(Description = "Gets external id infos for an item")] [Api(Description = "Gets external id infos for an item")]
public class GetPersonRemoteSearchResults : RemoteSearchQuery<PersonLookupInfo>, IReturn<List<RemoteSearchResult>> public class GetPersonRemoteSearchResults : RemoteSearchQuery<PersonLookupInfo>, IReturn<List<RemoteSearchResult>>
@ -167,6 +180,20 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result); return ToOptimizedResult(result);
} }
public object Post(GetMusicAlbumRemoteSearchResults request)
{
var result = _providerManager.GetRemoteSearchResults<MusicAlbum, AlbumInfo>(request, CancellationToken.None).Result;
return ToOptimizedResult(result);
}
public object Post(GetMusicArtistRemoteSearchResults request)
{
var result = _providerManager.GetRemoteSearchResults<MusicArtist, ArtistInfo>(request, CancellationToken.None).Result;
return ToOptimizedResult(result);
}
public object Get(GetRemoteSearchImage request) public object Get(GetRemoteSearchImage request)
{ {
var result = GetRemoteImage(request).Result; var result = GetRemoteImage(request).Result;

View File

@ -96,6 +96,9 @@
<Name>MediaBrowser.Model</Name> <Name>MediaBrowser.Model</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Server\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -7,6 +7,7 @@ using MediaBrowser.Model.Providers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -31,9 +32,86 @@ namespace MediaBrowser.Providers.Music
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
{ {
var releaseId = searchInfo.GetReleaseId();
string url = null;
if (!string.IsNullOrEmpty(releaseId))
{
url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=reid:{0}", releaseId);
}
else
{
var artistMusicBrainzId = searchInfo.GetMusicBrainzArtistId();
if (!string.IsNullOrWhiteSpace(artistMusicBrainzId))
{
url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND arid:{1}",
WebUtility.UrlEncode(searchInfo.Name),
artistMusicBrainzId);
}
else
{
url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
WebUtility.UrlEncode(searchInfo.Name),
WebUtility.UrlEncode(searchInfo.GetAlbumArtist()));
}
}
if (!string.IsNullOrWhiteSpace(url))
{
var doc = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
return GetResultsFromResponse(doc);
}
return new List<RemoteSearchResult>(); return new List<RemoteSearchResult>();
} }
private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc)
{
var ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#");
var list = new List<RemoteSearchResult>();
var nodes = doc.SelectNodes("//mb:release-list/mb:release", ns);
if (nodes != null)
{
foreach (var node in nodes.Cast<XmlNode>())
{
if (node.Attributes != null)
{
string name = null;
string mbzId = node.Attributes["id"].Value;
var nameNode = node.SelectSingleNode("//mb:title", ns);
if (nameNode != null)
{
name = nameNode.InnerText;
}
if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name))
{
var result = new RemoteSearchResult
{
Name = name
};
result.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbzId);
list.Add(result);
}
}
}
}
return list;
}
public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken) public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken)
{ {
var releaseId = id.GetReleaseId(); var releaseId = id.GetReleaseId();

View File

@ -19,70 +19,120 @@ namespace MediaBrowser.Providers.Music
{ {
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{ {
return new List<RemoteSearchResult>(); var musicBrainzId = searchInfo.GetMusicBrainzArtistId();
}
public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo id, CancellationToken cancellationToken)
{
var result = new MetadataResult<MusicArtist>();
var musicBrainzId = id.GetMusicBrainzArtistId() ?? await FindId(id, cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(musicBrainzId)) if (!string.IsNullOrWhiteSpace(musicBrainzId))
{ {
cancellationToken.ThrowIfCancellationRequested(); var url = string.Format("http://www.musicbrainz.org/ws/2/artist/?query=arid:{0}", musicBrainzId);
result.Item = new MusicArtist(); var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken)
result.HasMetadata = true; .ConfigureAwait(false);
result.Item.SetProviderId(MetadataProviders.MusicBrainzArtist, musicBrainzId); return GetResultsFromResponse(doc);
} }
else
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(ItemLookupInfo item, CancellationToken cancellationToken)
{ {
// They seem to throw bad request failures on any term with a slash // They seem to throw bad request failures on any term with a slash
var nameToSearch = item.Name.Replace('/', ' '); var nameToSearch = searchInfo.Name.Replace('/', ' ');
var url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch)); 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 doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
var ns = new XmlNamespaceManager(doc.NameTable); var results = GetResultsFromResponse(doc).ToList();
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) if (results.Count > 0)
{ {
return node.Value; return results;
} }
if (HasDiacritics(item.Name)) if (HasDiacritics(searchInfo.Name))
{ {
// Try again using the search with accent characters url // Try again using the search with accent characters url
url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); url = String.Format("http://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
ns = new XmlNamespaceManager(doc.NameTable); return GetResultsFromResponse(doc);
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 new List<RemoteSearchResult>();
}
private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc)
{ {
return node.Value; var ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#");
var list = new List<RemoteSearchResult>();
var nodes = doc.SelectNodes("//mb:artist-list/mb:artist", ns);
if (nodes != null)
{
foreach (var node in nodes.Cast<XmlNode>())
{
if (node.Attributes != null)
{
string name = null;
string mbzId = node.Attributes["id"].Value;
var nameNode = node.SelectSingleNode("//mb:name", ns);
if (nameNode != null)
{
name = nameNode.InnerText;
}
if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name))
{
var result = new RemoteSearchResult
{
Name = name
};
result.SetProviderId(MetadataProviders.MusicBrainzArtist, mbzId);
list.Add(result);
}
}
} }
} }
return null; return list;
}
public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo id, CancellationToken cancellationToken)
{
var result = new MetadataResult<MusicArtist>
{
Item = new MusicArtist()
};
var musicBrainzId = id.GetMusicBrainzArtistId();
if (string.IsNullOrWhiteSpace(musicBrainzId))
{
var searchResults = await GetSearchResults(id, cancellationToken).ConfigureAwait(false);
var singleResult = searchResults.FirstOrDefault();
if (singleResult != null)
{
musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist);
result.Item.Name = singleResult.Name;
}
}
if (!string.IsNullOrWhiteSpace(musicBrainzId))
{
result.HasMetadata = true;
result.Item.SetProviderId(MetadataProviders.MusicBrainzArtist, musicBrainzId);
}
return result;
} }
/// <summary> /// <summary>