mirror of
https://github.com/lidarr/Lidarr
synced 2025-03-13 07:23:14 +00:00
New: Cache spotify -> musicbrainz mapping
This commit is contained in:
parent
b050c73d87
commit
6a9887f7e2
9 changed files with 590 additions and 24 deletions
|
@ -120,9 +120,19 @@ namespace NzbDrone.Core.Test.ImportListTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_search_if_album_title_and_album_id()
|
||||
public void should_search_with_lidarr_id_if_album_id_and_no_artist_id()
|
||||
{
|
||||
WithAlbum();
|
||||
WithAlbumId();
|
||||
Subject.Execute(new ImportListSyncCommand());
|
||||
|
||||
Mocker.GetMock<ISearchForNewAlbum>()
|
||||
.Verify(v => v.SearchForNewAlbum($"lidarr:{_importListReports.First().AlbumMusicBrainzId}", null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_search_if_album_id_and_artist_id()
|
||||
{
|
||||
WithArtistId();
|
||||
WithAlbumId();
|
||||
Subject.Execute(new ImportListSyncCommand());
|
||||
|
||||
|
|
|
@ -0,0 +1,346 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Cloud;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ImportLists.Spotify;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ImportListTests
|
||||
{
|
||||
[TestFixture]
|
||||
// the base import list class is abstract so use the followed artists one
|
||||
public class SpotifyMappingFixture : CoreTest<SpotifyFollowedArtists>
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.SetConstant<ILidarrCloudRequestBuilder>(new LidarrCloudRequestBuilder());
|
||||
Mocker.SetConstant<IMetadataRequestBuilder>(Mocker.Resolve<MetadataRequestBuilder>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_artist_should_return_name_if_id_null()
|
||||
{
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = "Adele"
|
||||
};
|
||||
|
||||
Subject.MapArtistItem(data);
|
||||
|
||||
data.Artist.Should().Be("Adele");
|
||||
data.ArtistMusicBrainzId.Should().BeNull();
|
||||
data.Album.Should().BeNull();
|
||||
data.AlbumMusicBrainzId.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_artist_should_set_id_0_if_no_match()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Get<ArtistResource>(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>((x) => new HttpResponse<ArtistResource>(new HttpResponse(x, new HttpHeader(), new byte[0], HttpStatusCode.NotFound)));
|
||||
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = "Adele",
|
||||
ArtistSpotifyId = "id"
|
||||
};
|
||||
|
||||
Subject.MapArtistItem(data);
|
||||
data.ArtistMusicBrainzId.Should().Be("0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_artist_should_not_update_id_if_http_throws()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Get<ArtistResource>(It.IsAny<HttpRequest>()))
|
||||
.Throws(new Exception("Dummy exception"));
|
||||
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = "Adele",
|
||||
ArtistSpotifyId = "id"
|
||||
};
|
||||
|
||||
Subject.MapArtistItem(data);
|
||||
data.ArtistMusicBrainzId.Should().BeNull();
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_artist_should_work()
|
||||
{
|
||||
UseRealHttp();
|
||||
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = "Adele",
|
||||
ArtistSpotifyId = "4dpARuHxo51G3z768sgnrY"
|
||||
};
|
||||
|
||||
Subject.MapArtistItem(data);
|
||||
data.Should().NotBeNull();
|
||||
data.Artist.Should().Be("Adele");
|
||||
data.ArtistMusicBrainzId.Should().Be("cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493");
|
||||
data.Album.Should().BeNull();
|
||||
data.AlbumMusicBrainzId.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_album_should_return_name_if_uri_null()
|
||||
{
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
Artist = "Adele"
|
||||
};
|
||||
|
||||
Subject.MapAlbumItem(data);
|
||||
data.Should().NotBeNull();
|
||||
data.Artist.Should().Be("Adele");
|
||||
data.ArtistMusicBrainzId.Should().BeNull();
|
||||
data.Album.Should().Be("25");
|
||||
data.AlbumMusicBrainzId.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_album_should_set_id_0_if_no_match()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Get<AlbumResource>(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>((x) => new HttpResponse<AlbumResource>(new HttpResponse(x, new HttpHeader(), new byte[0], HttpStatusCode.NotFound)));
|
||||
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "id",
|
||||
Artist = "Adele"
|
||||
};
|
||||
|
||||
Subject.MapAlbumItem(data);
|
||||
data.AlbumMusicBrainzId.Should().Be("0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_album_should_not_update_id_if_http_throws()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Get<AlbumResource>(It.IsAny<HttpRequest>()))
|
||||
.Throws(new Exception("Dummy exception"));
|
||||
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "id",
|
||||
Artist = "Adele"
|
||||
};
|
||||
|
||||
|
||||
Subject.MapAlbumItem(data);
|
||||
data.Should().NotBeNull();
|
||||
data.Artist.Should().Be("Adele");
|
||||
data.ArtistMusicBrainzId.Should().BeNull();
|
||||
data.Album.Should().Be("25");
|
||||
data.AlbumMusicBrainzId.Should().BeNull();
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_album_should_work()
|
||||
{
|
||||
UseRealHttp();
|
||||
|
||||
var data = new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
Artist = "Adele"
|
||||
};
|
||||
|
||||
Subject.MapAlbumItem(data);
|
||||
|
||||
data.Should().NotBeNull();
|
||||
data.Artist.Should().Be("Adele");
|
||||
data.Album.Should().Be("25");
|
||||
data.AlbumMusicBrainzId.Should().Be("5537624c-3d2f-4f5c-8099-df916082c85c");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_spotify_releases_should_only_map_album_id_for_album()
|
||||
{
|
||||
var data = new List<SpotifyImportListItemInfo>
|
||||
{
|
||||
new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
Artist = "Adele",
|
||||
ArtistSpotifyId = "4dpARuHxo51G3z768sgnrY"
|
||||
}
|
||||
};
|
||||
|
||||
var map = new List<SpotifyMap>
|
||||
{
|
||||
new SpotifyMap
|
||||
{
|
||||
SpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
MusicbrainzId = "5537624c-3d2f-4f5c-8099-df916082c85c"
|
||||
},
|
||||
new SpotifyMap
|
||||
{
|
||||
SpotifyId = "4dpARuHxo51G3z768sgnrY",
|
||||
MusicbrainzId = "cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Post<List<SpotifyMap>>(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse<List<SpotifyMap>>(new HttpResponse(r, new HttpHeader(), map.ToJson())));
|
||||
|
||||
var result = Subject.MapSpotifyReleases(data);
|
||||
result[0].AlbumMusicBrainzId.Should().Be("5537624c-3d2f-4f5c-8099-df916082c85c");
|
||||
result[0].ArtistMusicBrainzId.Should().BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_spotify_releases_should_map_artist_id_for_artist()
|
||||
{
|
||||
var data = new List<SpotifyImportListItemInfo>
|
||||
{
|
||||
new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = "Adele",
|
||||
ArtistSpotifyId = "4dpARuHxo51G3z768sgnrY"
|
||||
}
|
||||
};
|
||||
|
||||
var map = new List<SpotifyMap>
|
||||
{
|
||||
new SpotifyMap
|
||||
{
|
||||
SpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
MusicbrainzId = "5537624c-3d2f-4f5c-8099-df916082c85c"
|
||||
},
|
||||
new SpotifyMap
|
||||
{
|
||||
SpotifyId = "4dpARuHxo51G3z768sgnrY",
|
||||
MusicbrainzId = "cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Post<List<SpotifyMap>>(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse<List<SpotifyMap>>(new HttpResponse(r, new HttpHeader(), map.ToJson())));
|
||||
|
||||
var result = Subject.MapSpotifyReleases(data);
|
||||
result[0].ArtistMusicBrainzId.Should().Be("cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_spotify_releases_should_drop_not_found()
|
||||
{
|
||||
var data = new List<SpotifyImportListItemInfo>
|
||||
{
|
||||
new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
Artist = "Adele"
|
||||
}
|
||||
};
|
||||
|
||||
var map = new List<SpotifyMap>
|
||||
{
|
||||
new SpotifyMap
|
||||
{
|
||||
SpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
MusicbrainzId = "0"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Post<List<SpotifyMap>>(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse<List<SpotifyMap>>(new HttpResponse(r, new HttpHeader(), map.ToJson())));
|
||||
|
||||
var result = Subject.MapSpotifyReleases(data);
|
||||
result.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_spotify_releases_should_catch_exception_from_api()
|
||||
{
|
||||
var data = new List<SpotifyImportListItemInfo>
|
||||
{
|
||||
new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
Artist = "Adele"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Post<List<SpotifyMap>>(It.IsAny<HttpRequest>()))
|
||||
.Throws(new Exception("Dummy exception"));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Get<AlbumResource>(It.IsAny<HttpRequest>()))
|
||||
.Throws(new Exception("Dummy exception"));
|
||||
|
||||
|
||||
var result = Subject.MapSpotifyReleases(data);
|
||||
result.Should().NotBeNull();
|
||||
ExceptionVerification.ExpectedErrors(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void map_spotify_releases_should_cope_with_duplicate_spotify_ids()
|
||||
{
|
||||
var data = new List<SpotifyImportListItemInfo>
|
||||
{
|
||||
new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
Artist = "Adele"
|
||||
},
|
||||
new SpotifyImportListItemInfo
|
||||
{
|
||||
Album = "25",
|
||||
AlbumSpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
Artist = "Adele"
|
||||
}
|
||||
};
|
||||
|
||||
var map = new List<SpotifyMap>
|
||||
{
|
||||
new SpotifyMap
|
||||
{
|
||||
SpotifyId = "7uwTHXmFa1Ebi5flqBosig",
|
||||
MusicbrainzId = "5537624c-3d2f-4f5c-8099-df916082c85c"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(x => x.Post<List<SpotifyMap>>(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse<List<SpotifyMap>>(new HttpResponse(r, new HttpHeader(), map.ToJson())));
|
||||
|
||||
var result = Subject.MapSpotifyReleases(data);
|
||||
result.Should().HaveCount(2);
|
||||
result[0].AlbumMusicBrainzId.Should().Be("5537624c-3d2f-4f5c-8099-df916082c85c");
|
||||
result[1].AlbumMusicBrainzId.Should().Be("5537624c-3d2f-4f5c-8099-df916082c85c");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,6 +104,18 @@ namespace NzbDrone.Core.ImportLists
|
|||
|
||||
}
|
||||
|
||||
// Map artist ID if we only have album ID
|
||||
if (report.AlbumMusicBrainzId.IsNotNullOrWhiteSpace() && report.ArtistMusicBrainzId.IsNullOrWhiteSpace())
|
||||
{
|
||||
var mappedAlbum = _albumSearchService.SearchForNewAlbum($"lidarr:{report.AlbumMusicBrainzId}", null)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (mappedAlbum == null) continue;
|
||||
|
||||
report.Artist = mappedAlbum.ArtistMetadata?.Value?.Name;
|
||||
report.ArtistMusicBrainzId = mappedAlbum?.ArtistMetadata?.Value?.ForeignArtistId;
|
||||
}
|
||||
|
||||
// Map MBid if we only have a artist name
|
||||
if (report.ArtistMusicBrainzId.IsNullOrWhiteSpace() && report.Artist.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@ using NLog;
|
|||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using SpotifyAPI.Web;
|
||||
|
@ -18,21 +19,22 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
public class SpotifyFollowedArtists : SpotifyImportListBase<SpotifyFollowedArtistsSettings>
|
||||
{
|
||||
public SpotifyFollowedArtists(ISpotifyProxy spotifyProxy,
|
||||
IMetadataRequestBuilder requestBuilder,
|
||||
IImportListStatusService importListStatusService,
|
||||
IImportListRepository importListRepository,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
IHttpClient httpClient,
|
||||
Logger logger)
|
||||
: base(spotifyProxy, importListStatusService, importListRepository, configService, parsingService, httpClient, logger)
|
||||
: base(spotifyProxy, requestBuilder, importListStatusService, importListRepository, configService, parsingService, httpClient, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Spotify Followed Artists";
|
||||
|
||||
public override IList<ImportListItemInfo> Fetch(SpotifyWebAPI api)
|
||||
public override IList<SpotifyImportListItemInfo> Fetch(SpotifyWebAPI api)
|
||||
{
|
||||
var result = new List<ImportListItemInfo>();
|
||||
var result = new List<SpotifyImportListItemInfo>();
|
||||
|
||||
var followedArtists = _spotifyProxy.GetFollowedArtists(this, api);
|
||||
var artists = followedArtists?.Artists;
|
||||
|
@ -61,12 +63,14 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
return result;
|
||||
}
|
||||
|
||||
private ImportListItemInfo ParseFullArtist(FullArtist artist)
|
||||
private SpotifyImportListItemInfo ParseFullArtist(FullArtist artist)
|
||||
{
|
||||
if (artist?.Name.IsNotNullOrWhiteSpace() ?? false)
|
||||
{
|
||||
return new ImportListItemInfo {
|
||||
return new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = artist.Name,
|
||||
ArtistSpotifyId = artist.Id
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
@ -21,8 +26,10 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
private IImportListRepository _importListRepository;
|
||||
|
||||
protected ISpotifyProxy _spotifyProxy;
|
||||
private readonly IMetadataRequestBuilder _requestBuilder;
|
||||
|
||||
protected SpotifyImportListBase(ISpotifyProxy spotifyProxy,
|
||||
IMetadataRequestBuilder requestBuilder,
|
||||
IImportListStatusService importListStatusService,
|
||||
IImportListRepository importListRepository,
|
||||
IConfigService configService,
|
||||
|
@ -34,6 +41,7 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
_httpClient = httpClient;
|
||||
_importListRepository = importListRepository;
|
||||
_spotifyProxy = spotifyProxy;
|
||||
_requestBuilder = requestBuilder;
|
||||
}
|
||||
|
||||
public override ImportListType ListType => ImportListType.Spotify;
|
||||
|
@ -93,15 +101,20 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
|
||||
public override IList<ImportListItemInfo> Fetch()
|
||||
{
|
||||
IList<SpotifyImportListItemInfo> releases;
|
||||
using (var api = GetApi())
|
||||
{
|
||||
_logger.Debug("Starting spotify import list sync");
|
||||
var releases = Fetch(api);
|
||||
return CleanupListItems(releases);
|
||||
}
|
||||
releases = Fetch(api);
|
||||
}
|
||||
|
||||
public abstract IList<ImportListItemInfo> Fetch(SpotifyWebAPI api);
|
||||
// map to musicbrainz ids
|
||||
releases = MapSpotifyReleases(releases);
|
||||
|
||||
return CleanupListItems(releases);
|
||||
}
|
||||
|
||||
public abstract IList<SpotifyImportListItemInfo> Fetch(SpotifyWebAPI api);
|
||||
|
||||
protected DateTime ParseSpotifyDate(string date, string precision)
|
||||
{
|
||||
|
@ -128,6 +141,156 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
return DateTime.TryParseExact(date, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime result) ? result : default(DateTime);
|
||||
}
|
||||
|
||||
public IList<SpotifyImportListItemInfo> MapSpotifyReleases(IList<SpotifyImportListItemInfo> items)
|
||||
{
|
||||
// first pass bulk lookup, server won't do search
|
||||
var spotifyIds = items.Select(x => x.ArtistSpotifyId)
|
||||
.Concat(items.Select(x => x.AlbumSpotifyId))
|
||||
.Where(x => x.IsNotNullOrWhiteSpace())
|
||||
.Distinct();
|
||||
var httpRequest = _requestBuilder.GetRequestBuilder().Create()
|
||||
.SetSegment("route", "spotify/lookup")
|
||||
.Build();
|
||||
httpRequest.SetContent(spotifyIds.ToJson());
|
||||
httpRequest.Headers.ContentType = "application/json";
|
||||
|
||||
_logger.Trace($"Requesting maps for:\n{spotifyIds.ToJson()}");
|
||||
|
||||
Dictionary<string, string> map;
|
||||
try
|
||||
{
|
||||
var httpResponse = _httpClient.Post<List<SpotifyMap>>(httpRequest);
|
||||
var mapList = httpResponse.Resource;
|
||||
|
||||
// Generate a mapping dictionary.
|
||||
// The API will return 0 to mean it has previously searched and can't find the item.
|
||||
// null means that it has never been searched before.
|
||||
map = mapList.Where(x => x.MusicbrainzId.IsNotNullOrWhiteSpace())
|
||||
.ToDictionary(x => x.SpotifyId, x => x.MusicbrainzId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
map = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
_logger.Trace("Got mapping:\n{0}", map.ToJson());
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.AlbumSpotifyId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
if (map.ContainsKey(item.AlbumSpotifyId))
|
||||
{
|
||||
item.AlbumMusicBrainzId = map[item.AlbumSpotifyId];
|
||||
}
|
||||
else
|
||||
{
|
||||
MapAlbumItem(item);
|
||||
}
|
||||
}
|
||||
else if (item.ArtistSpotifyId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
if (map.ContainsKey(item.ArtistSpotifyId))
|
||||
{
|
||||
item.ArtistMusicBrainzId = map[item.ArtistSpotifyId];
|
||||
}
|
||||
else
|
||||
{
|
||||
MapArtistItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip out items where mapped to not found
|
||||
return items.Where(x => x.AlbumMusicBrainzId != "0" && x.ArtistMusicBrainzId != "0").ToList();
|
||||
}
|
||||
|
||||
public void MapArtistItem(SpotifyImportListItemInfo item)
|
||||
{
|
||||
if (item.ArtistSpotifyId.IsNullOrWhiteSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var httpRequest = _requestBuilder.GetRequestBuilder().Create()
|
||||
.SetSegment("route", $"spotify/artist/{item.ArtistSpotifyId}")
|
||||
.Build();
|
||||
httpRequest.AllowAutoRedirect = true;
|
||||
httpRequest.SuppressHttpError = true;
|
||||
|
||||
try
|
||||
{
|
||||
var response = _httpClient.Get<ArtistResource>(httpRequest);
|
||||
|
||||
if (response.HasHttpError)
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
item.ArtistMusicBrainzId = "0";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpException(httpRequest, response);
|
||||
}
|
||||
}
|
||||
|
||||
item.ArtistMusicBrainzId = response.Resource.Id;
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
_logger.Warn(e, "Unable to communicate with LidarrAPI");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void MapAlbumItem(SpotifyImportListItemInfo item)
|
||||
{
|
||||
if (item.AlbumSpotifyId.IsNullOrWhiteSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var httpRequest = _requestBuilder.GetRequestBuilder().Create()
|
||||
.SetSegment("route", $"spotify/album/{item.AlbumSpotifyId}")
|
||||
.Build();
|
||||
httpRequest.AllowAutoRedirect = true;
|
||||
httpRequest.SuppressHttpError = true;
|
||||
|
||||
try
|
||||
{
|
||||
var response = _httpClient.Get<AlbumResource>(httpRequest);
|
||||
|
||||
if (response.HasHttpError)
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
item.AlbumMusicBrainzId = "0";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new HttpException(httpRequest, response);
|
||||
}
|
||||
}
|
||||
|
||||
item.ArtistMusicBrainzId = response.Resource.ArtistId;
|
||||
item.AlbumMusicBrainzId = response.Resource.Id;
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
_logger.Warn(e, "Unable to communicate with LidarrAPI");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Test(List<ValidationFailure> failures)
|
||||
{
|
||||
failures.AddIfNotNull(TestConnection());
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Spotify
|
||||
{
|
||||
public class SpotifyImportListItemInfo : ImportListItemInfo
|
||||
{
|
||||
public string ArtistSpotifyId { get; set; }
|
||||
public string AlbumSpotifyId { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}] {1}", ArtistSpotifyId, AlbumSpotifyId);
|
||||
}
|
||||
}
|
||||
}
|
8
src/NzbDrone.Core/ImportLists/Spotify/SpotifyMap.cs
Normal file
8
src/NzbDrone.Core/ImportLists/Spotify/SpotifyMap.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace NzbDrone.Core.ImportLists.Spotify
|
||||
{
|
||||
public class SpotifyMap
|
||||
{
|
||||
public string SpotifyId { get; set; }
|
||||
public string MusicbrainzId { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,8 +5,8 @@ using NLog;
|
|||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
using SpotifyAPI.Web;
|
||||
using SpotifyAPI.Web.Models;
|
||||
|
@ -16,30 +16,31 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
public class SpotifyPlaylist : SpotifyImportListBase<SpotifyPlaylistSettings>
|
||||
{
|
||||
public SpotifyPlaylist(ISpotifyProxy spotifyProxy,
|
||||
IMetadataRequestBuilder requestBuilder,
|
||||
IImportListStatusService importListStatusService,
|
||||
IImportListRepository importListRepository,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
IHttpClient httpClient,
|
||||
Logger logger)
|
||||
: base(spotifyProxy, importListStatusService, importListRepository, configService, parsingService, httpClient, logger)
|
||||
: base(spotifyProxy, requestBuilder, importListStatusService, importListRepository, configService, parsingService, httpClient, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Spotify Playlists";
|
||||
|
||||
public override IList<ImportListItemInfo> Fetch(SpotifyWebAPI api)
|
||||
public override IList<SpotifyImportListItemInfo> Fetch(SpotifyWebAPI api)
|
||||
{
|
||||
return Settings.PlaylistIds.SelectMany(x => Fetch(api, x)).ToList();
|
||||
}
|
||||
|
||||
public IList<ImportListItemInfo> Fetch(SpotifyWebAPI api, string playlistId)
|
||||
public IList<SpotifyImportListItemInfo> Fetch(SpotifyWebAPI api, string playlistId)
|
||||
{
|
||||
var result = new List<ImportListItemInfo>();
|
||||
var result = new List<SpotifyImportListItemInfo>();
|
||||
|
||||
_logger.Trace($"Processing playlist {playlistId}");
|
||||
|
||||
var playlistTracks = _spotifyProxy.GetPlaylistTracks(this, api, playlistId, "next, items(track(name, album(name,artists)))");
|
||||
var playlistTracks = _spotifyProxy.GetPlaylistTracks(this, api, playlistId, "next, items(track(name, artists(id, name), album(id, name, release_date, release_date_precision, artists(id, name))))");
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
@ -64,20 +65,23 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
return result;
|
||||
}
|
||||
|
||||
private ImportListItemInfo ParsePlaylistTrack(PlaylistTrack playlistTrack)
|
||||
private SpotifyImportListItemInfo ParsePlaylistTrack(PlaylistTrack playlistTrack)
|
||||
{
|
||||
// From spotify docs: "Note, a track object may be null. This can happen if a track is no longer available."
|
||||
if (playlistTrack?.Track?.Album != null)
|
||||
{
|
||||
var album = playlistTrack.Track.Album;
|
||||
|
||||
var albumName = album.Name;
|
||||
var artistName = album.Artists?.FirstOrDefault()?.Name ?? playlistTrack.Track?.Artists?.FirstOrDefault()?.Name;
|
||||
|
||||
if (albumName.IsNotNullOrWhiteSpace() && artistName.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return new ImportListItemInfo {
|
||||
return new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = artistName,
|
||||
Album = albumName,
|
||||
Album = album.Name,
|
||||
AlbumSpotifyId = album.Id,
|
||||
ReleaseDate = ParseSpotifyDate(album.ReleaseDate, album.ReleaseDatePrecision)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using NLog;
|
|||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using SpotifyAPI.Web;
|
||||
|
@ -19,21 +20,22 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
public class SpotifySavedAlbums : SpotifyImportListBase<SpotifySavedAlbumsSettings>
|
||||
{
|
||||
public SpotifySavedAlbums(ISpotifyProxy spotifyProxy,
|
||||
IMetadataRequestBuilder requestBuilder,
|
||||
IImportListStatusService importListStatusService,
|
||||
IImportListRepository importListRepository,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
IHttpClient httpClient,
|
||||
Logger logger)
|
||||
: base(spotifyProxy, importListStatusService, importListRepository, configService, parsingService, httpClient, logger)
|
||||
: base(spotifyProxy, requestBuilder, importListStatusService, importListRepository, configService, parsingService, httpClient, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Spotify Saved Albums";
|
||||
|
||||
public override IList<ImportListItemInfo> Fetch(SpotifyWebAPI api)
|
||||
public override IList<SpotifyImportListItemInfo> Fetch(SpotifyWebAPI api)
|
||||
{
|
||||
var result = new List<ImportListItemInfo>();
|
||||
var result = new List<SpotifyImportListItemInfo>();
|
||||
|
||||
var savedAlbums = _spotifyProxy.GetSavedAlbums(this, api);
|
||||
|
||||
|
@ -62,7 +64,7 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
return result;
|
||||
}
|
||||
|
||||
private ImportListItemInfo ParseSavedAlbum(SavedAlbum savedAlbum)
|
||||
private SpotifyImportListItemInfo ParseSavedAlbum(SavedAlbum savedAlbum)
|
||||
{
|
||||
var artistName = savedAlbum?.Album?.Artists?.FirstOrDefault()?.Name;
|
||||
var albumName = savedAlbum?.Album?.Name;
|
||||
|
@ -70,9 +72,11 @@ namespace NzbDrone.Core.ImportLists.Spotify
|
|||
|
||||
if (artistName.IsNotNullOrWhiteSpace() && albumName.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return new ImportListItemInfo {
|
||||
return new SpotifyImportListItemInfo
|
||||
{
|
||||
Artist = artistName,
|
||||
Album = albumName,
|
||||
AlbumSpotifyId = savedAlbum?.Album?.Id,
|
||||
ReleaseDate = ParseSpotifyDate(savedAlbum?.Album?.ReleaseDate, savedAlbum?.Album?.ReleaseDatePrecision)
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue