roadie/Roadie.Api.Library/SearchEngines/MetaData/Spotify/SpotifyHelper.cs

269 lines
No EOL
12 KiB
C#

using Microsoft.Extensions.Logging;
using RestSharp;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Extensions;
using Roadie.Library.MetaData;
using Roadie.Library.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Authentication;
using System.Threading.Tasks;
namespace Roadie.Library.SearchEngines.MetaData.Spotify
{
public class SpotifyHelper : MetaDataProviderBase, IArtistSearchEngine, IReleaseSearchEngine
{
public override bool IsEnabled
{
get
{
return this.Configuration.Integrations.SpotifyProviderEnabled;
}
}
public SpotifyHelper(IRoadieSettings configuration, ICacheManager cacheManager, ILogger logger)
: base(configuration, cacheManager, logger)
{
}
public async Task<OperationResult<IEnumerable<ArtistSearchResult>>> PerformArtistSearch(string query, int resultsCount)
{
ArtistSearchResult data = null;
try
{
// TODO update this to use https://github.com/JohnnyCrazy/SpotifyAPI-NET
this.Logger.LogTrace("SpotifyHelper:PerformArtistSearch:{0}", query);
var request = this.BuildSearchRequest(query, 1, "artist");
var client = new RestClient("http://api.spotify.com/v1");
client.UserAgent = WebHelper.UserAgent;
var response = await client.ExecuteTaskAsync<SpotifyResult>(request);
if (response.ResponseStatus == ResponseStatus.Error)
{
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new AuthenticationException("Unauthorized");
}
throw new Exception(string.Format("Request Error Message: {0}. Content: {1}.", response.ErrorMessage, response.Content));
}
Item spotifyArtist = null;
if (response.Data != null && response.Data.artists != null && response.Data.artists.items != null)
{
spotifyArtist = response.Data.artists.items.FirstOrDefault(x => x.name.Equals(query, StringComparison.OrdinalIgnoreCase));
}
if (spotifyArtist == null)
{
return new OperationResult<IEnumerable<ArtistSearchResult>>();
}
data = new ArtistSearchResult
{
ArtistName = spotifyArtist.name,
ArtistType = spotifyArtist.type,
SpotifyId = spotifyArtist.id
};
if (spotifyArtist.images != null)
{
data.ImageUrls = spotifyArtist.images.OrderByDescending(x => x.height).Take(1).Select(x => x.url).ToList();
}
if (spotifyArtist.genres != null)
{
data.ArtistGenres = spotifyArtist.genres.Select(x => x).ToList();
}
if (spotifyArtist.external_urls != null)
{
data.Urls = new string[] { spotifyArtist.external_urls.spotify };
}
}
catch (Exception ex)
{
this.Logger.LogError(ex);
}
return new OperationResult<IEnumerable<ArtistSearchResult>>
{
IsSuccess = data != null,
Data = new ArtistSearchResult[] { data }
};
}
public async Task<OperationResult<IEnumerable<ReleaseSearchResult>>> PerformReleaseSearch(string artistName, string query, int resultsCount)
{
var artistResult = await this.PerformArtistSearch(artistName, resultsCount);
if (!artistResult.IsSuccess)
{
return new OperationResult<IEnumerable<ReleaseSearchResult>>();
}
try
{
var tcs = new TaskCompletionSource<OperationResult<IEnumerable<ReleaseSearchResult>>>();
var request = new RestRequest(Method.GET);
ReleaseSearchResult result = null;
var response = await this.AlbumsForArtist(artistResult.Data.First().SpotifyId);
if (response != null && response.items != null)
{
string foundByAlternateName = null;
var spotifyAlbum = response.items.FirstOrDefault(x => x.name.Trim().Equals(query.Trim(), StringComparison.OrdinalIgnoreCase));
if (spotifyAlbum == null)
{
// No Exact match see if one starts with and use the first one
spotifyAlbum = response.items.FirstOrDefault(x => x.name.Trim().StartsWith(query.Trim(), StringComparison.OrdinalIgnoreCase));
if (spotifyAlbum == null)
{
return new OperationResult<IEnumerable<ReleaseSearchResult>>();
}
foundByAlternateName = spotifyAlbum.name;
}
var client = new RestClient(string.Format("http://api.spotify.com/v1/albums/{0}", spotifyAlbum.id));
var albumResult = await client.ExecuteTaskAsync<AlbumResult>(request);
if (albumResult != null && albumResult.Data != null)
{
var sa = albumResult.Data;
var resultTags = new List<string>();
var resultUrls = new List<string>();
if (sa.external_ids != null)
{
resultTags.Add("upc:" + sa.external_ids.upc);
}
result = new ReleaseSearchResult
{
ReleaseTitle = !string.IsNullOrEmpty(foundByAlternateName) ? query.ToTitleCase(false) : sa.name,
ReleaseDate = SafeParser.ToDateTime(sa.release_date),
ReleaseType = sa.album_type,
SpotifyId = sa.id,
Tags = resultTags,
AlternateNames = !string.IsNullOrEmpty(foundByAlternateName) ? new string[] { foundByAlternateName } : null
};
if (sa.artists != null && sa.artists.FirstOrDefault() != null)
{
var saArtist = sa.artists.First();
result.Artist = new ArtistSearchResult
{
ArtistName = saArtist.name,
ArtistType = saArtist.type,
SpotifyId = saArtist.id
};
}
if (sa.genres != null)
{
result.ReleaseGenres = sa.genres.ToList();
}
if (sa.external_urls != null)
{
resultUrls.Add(sa.external_urls.spotify);
}
if (sa.images != null)
{
result.ImageUrls = sa.images.OrderBy(x => x.width).Take(1).Select(x => x.url).ToList();
}
if (sa.tracks != null)
{
var releaseMediaCount = sa.tracks.items.Max(x => x.disc_number ?? 0);
var releaseMedias = new List<ReleaseMediaSearchResult>();
for (short? i = 1; i <= releaseMediaCount; i++)
{
var releaseTracks = new List<TrackSearchResult>();
foreach (var saTrack in sa.tracks.items)
{
ArtistSearchResult trackArtist = null;
if (saTrack.artists != null && saTrack.artists.FirstOrDefault() != null)
{
var saTrackArtist = saTrack.artists.First();
trackArtist = new ArtistSearchResult
{
ArtistName = saTrackArtist.name,
SpotifyId = saTrackArtist.id,
ArtistType = saTrackArtist.type
};
}
releaseTracks.Add(new TrackSearchResult
{
Artist = !trackArtist.SpotifyId.Equals(trackArtist.SpotifyId) ? trackArtist : null,
TrackNumber = saTrack.track_number,
Title = saTrack.name,
SpotifyId = saTrack.id,
Urls = new List<string> { saTrack.external_urls.spotify, saTrack.preview_url },
Duration = saTrack.duration_ms,
TrackType = saTrack.type
});
}
releaseMedias.Add(new ReleaseMediaSearchResult
{
ReleaseMediaNumber = i,
TrackCount = (short)sa.tracks.items.Count(x => x.disc_number == i),
Tracks = releaseTracks
});
}
result.ReleaseMedia = releaseMedias;
}
result.Urls = resultUrls;
}
}
return new OperationResult<IEnumerable<ReleaseSearchResult>>
{
IsSuccess = result != null,
Data = new List<ReleaseSearchResult> { result }
};
}
catch (Exception ex)
{
this.Logger.LogError(ex, ex.Serialize());
}
return new OperationResult<IEnumerable<ReleaseSearchResult>>();
}
private async Task<Albums> AlbumsForArtist(string spotifyId)
{
var cacheKey = string.Format("uri:spotify:AlbumsForArtist:{0}", spotifyId);
var result = this.CacheManager.Get<Albums>(cacheKey);
if (result == null)
{
var request = new RestRequest(Method.GET);
var client = new RestClient(string.Format("http://api.spotify.com/v1/artists/{0}/albums?offset=0&limit=25&album_type=album&market=US", spotifyId));
var artistAlbumsResponse = await client.ExecuteTaskAsync<Albums>(request);
result = artistAlbumsResponse != null && artistAlbumsResponse.Data != null ? artistAlbumsResponse.Data : null;
if (result != null)
{
this.CacheManager.Add(cacheKey, result);
}
}
return result;
}
private RestRequest BuildSearchRequest(string query, int resultsCount, string entityType)
{
var request = new RestRequest
{
Resource = "search",
Method = Method.GET,
RequestFormat = DataFormat.Json
};
request.AddParameter(new Parameter
{
Name = "type",
Value = entityType,
Type = ParameterType.GetOrPost
});
request.AddParameter(new Parameter
{
Name = "q",
Value = string.Format("{0}", query.Trim()),
Type = ParameterType.GetOrPost
});
request.AddParameter(new Parameter
{
Name = "market",
Value = "US",
Type = ParameterType.GetOrPost
});
return request;
}
}
}