From bebb82ec9fc87e07f786039105dd5d37223aba5d Mon Sep 17 00:00:00 2001 From: mdespain Date: Thu, 4 May 2017 10:41:59 -0600 Subject: [PATCH 01/11] Fixing my epic fail of not setting the columns to allow null. --- .../Migration/112_Add_music_fields_to_NamingConfig.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NzbDrone.Core/Datastore/Migration/112_Add_music_fields_to_NamingConfig.cs b/src/NzbDrone.Core/Datastore/Migration/112_Add_music_fields_to_NamingConfig.cs index f48ce9988..b855e1e28 100644 --- a/src/NzbDrone.Core/Datastore/Migration/112_Add_music_fields_to_NamingConfig.cs +++ b/src/NzbDrone.Core/Datastore/Migration/112_Add_music_fields_to_NamingConfig.cs @@ -8,8 +8,8 @@ namespace NzbDrone.Core.Datastore.Migration { protected override void MainDbUpgrade() { - Alter.Table("NamingConfig").AddColumn("ArtistFolderFormat").AsAnsiString(); - Alter.Table("NamingConfig").AddColumn("AlbumFolderFormat").AsAnsiString(); + Alter.Table("NamingConfig").AddColumn("ArtistFolderFormat").AsAnsiString().Nullable(); + Alter.Table("NamingConfig").AddColumn("AlbumFolderFormat").AsAnsiString().Nullable(); } } } From 0ec8830def1633f8175ff2a9ed49f5c9a6787fee Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Fri, 5 May 2017 08:19:49 -0500 Subject: [PATCH 02/11] Fixed issue with Artist page not rendering the artists in DB --- src/NzbDrone.Api/Music/ArtistModule.cs | 2 +- src/UI/AddSeries/AddSeriesLayout.js | 2 +- src/UI/Artist/ArtistController.js | 17 ++++--- src/UI/Handlebars/Helpers/Series.js | 10 +++++ .../SeriesOverviewItemViewTemplate.hbs | 18 ++++---- src/UI/Series/Index/SeriesIndexLayout.js | 44 ++++++++++--------- src/UI/main.js | 4 +- src/UI/vent.js | 2 + 8 files changed, 57 insertions(+), 42 deletions(-) diff --git a/src/NzbDrone.Api/Music/ArtistModule.cs b/src/NzbDrone.Api/Music/ArtistModule.cs index d616becfb..1f7c433cf 100644 --- a/src/NzbDrone.Api/Music/ArtistModule.cs +++ b/src/NzbDrone.Api/Music/ArtistModule.cs @@ -98,7 +98,7 @@ namespace NzbDrone.Api.Music { //var seriesStats = _seriesStatisticsService.SeriesStatistics(); var artistResources = _artistService.GetAllArtists().ToResource(); - + Console.WriteLine("[DEBUG] Returning {0} Artists", artistResources.Count); MapCoversToLocal(artistResources.ToArray()); //LinkSeriesStatistics(seriesResources, seriesStats); //PopulateAlternateTitles(seriesResources); diff --git a/src/UI/AddSeries/AddSeriesLayout.js b/src/UI/AddSeries/AddSeriesLayout.js index 166aedb5a..949ae4942 100644 --- a/src/UI/AddSeries/AddSeriesLayout.js +++ b/src/UI/AddSeries/AddSeriesLayout.js @@ -6,7 +6,7 @@ var ExistingSeriesCollectionView = require('./Existing/AddExistingSeriesCollecti var AddSeriesView = require('./AddSeriesView'); var ProfileCollection = require('../Profile/ProfileCollection'); var RootFolderCollection = require('./RootFolders/RootFolderCollection'); -require('../Series/SeriesCollection'); +require('../Artist/ArtistCollection'); module.exports = Marionette.Layout.extend({ template : 'AddSeries/AddSeriesLayoutTemplate', diff --git a/src/UI/Artist/ArtistController.js b/src/UI/Artist/ArtistController.js index 838018670..4805bd9bf 100644 --- a/src/UI/Artist/ArtistController.js +++ b/src/UI/Artist/ArtistController.js @@ -1,34 +1,33 @@ var NzbDroneController = require('../Shared/NzbDroneController'); var AppLayout = require('../AppLayout'); var ArtistCollection = require('./ArtistCollection'); -var SeriesIndexLayout = require('./Index/SeriesIndexLayout'); +var SeriesIndexLayout = require('../Series/Index/SeriesIndexLayout'); var SeriesDetailsLayout = require('../Series/Details/SeriesDetailsLayout'); module.exports = NzbDroneController.extend({ _originalInit : NzbDroneController.prototype.initialize, initialize : function() { - this.route('', this.series); - this.route('artist', this.series); - this.route('artist/:query', this.seriesDetails); + this.route('', this.artist); + this.route('artist', this.artist); + this.route('artist/:query', this.artistDetails); this._originalInit.apply(this, arguments); }, artist : function() { this.setTitle('Lidarr'); - this.setArtistName('Lidarr'); this.showMainRegion(new SeriesIndexLayout()); }, - seriesDetails : function(query) { + artistDetails : function(query) { var artists = ArtistCollection.where({ artistNameSlug : query }); - console.log('seriesDetails, artists: ', artists); + console.log('artistDetails, artists: ', artists); if (artists.length !== 0) { var targetSeries = artists[0]; console.log("[ArtistController] targetSeries: ", targetSeries); - this.setTitle(targetSeries.get('title')); - this.setArtistName(targetSeries.get('artistName')); + this.setTitle(targetSeries.get('artistName')); // TODO: Update NzbDroneController + //this.setArtistName(targetSeries.get('artistName')); this.showMainRegion(new SeriesDetailsLayout({ model : targetSeries })); } else { this.showNotFound(); diff --git a/src/UI/Handlebars/Helpers/Series.js b/src/UI/Handlebars/Helpers/Series.js index ff3ffd7f1..ecade70eb 100644 --- a/src/UI/Handlebars/Helpers/Series.js +++ b/src/UI/Handlebars/Helpers/Series.js @@ -71,6 +71,16 @@ Handlebars.registerHelper('seasonCountHelper', function() { return new Handlebars.SafeString('{0} Seasons'.format(seasonCount)); }); +Handlebars.registerHelper('albumCountHelper', function() { + var albumCount = this.albumCount; + + if (albumCount === 1) { + return new Handlebars.SafeString('{0} Albums'.format(albumCount)); + } + + return new Handlebars.SafeString('{0} Albums'.format(albumCount)); +}); + /*Handlebars.registerHelper('titleWithYear', function() { if (this.title.endsWith(' ({0})'.format(this.year))) { return this.title; diff --git a/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs b/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs index 1bc7f1a3b..4e9785b50 100644 --- a/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs +++ b/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs @@ -8,14 +8,14 @@
@@ -35,15 +35,17 @@
- {{#if_eq status compare="ended"}} + + - {{seasonCountHelper}} + {{albumCountHelper}} {{profile profileId}}
diff --git a/src/UI/Series/Index/SeriesIndexLayout.js b/src/UI/Series/Index/SeriesIndexLayout.js index 9cd77d08e..049bd267b 100644 --- a/src/UI/Series/Index/SeriesIndexLayout.js +++ b/src/UI/Series/Index/SeriesIndexLayout.js @@ -4,7 +4,7 @@ var Backgrid = require('backgrid'); var PosterCollectionView = require('./Posters/SeriesPostersCollectionView'); var ListCollectionView = require('./Overview/SeriesOverviewCollectionView'); var EmptyView = require('./EmptyView'); -var SeriesCollection = require('../SeriesCollection'); +var ArtistCollection = require('../../Artist/ArtistCollection'); var RelativeDateCell = require('../../Cells/RelativeDateCell'); var SeriesTitleCell = require('../../Cells/SeriesTitleCell'); var TemplatedCell = require('../../Cells/TemplatedCell'); @@ -111,28 +111,28 @@ module.exports = Marionette.Layout.extend({ }, initialize : function() { - this.seriesCollection = SeriesCollection.clone(); - this.seriesCollection.shadowCollection.bindSignalR(); + this.artistCollection = ArtistCollection.clone(); + this.artistCollection.shadowCollection.bindSignalR(); - this.listenTo(this.seriesCollection.shadowCollection, 'sync', function(model, collection, options) { - this.seriesCollection.fullCollection.resetFiltered(); + this.listenTo(this.artistCollection, 'sync', function(model, collection, options) { + this.artistCollection.fullCollection.resetFiltered(); this._renderView(); }); - this.listenTo(this.seriesCollection.shadowCollection, 'add', function(model, collection, options) { - this.seriesCollection.fullCollection.resetFiltered(); + this.listenTo(this.artistCollection, 'add', function(model, collection, options) { + this.artistCollection.fullCollection.resetFiltered(); this._renderView(); }); - this.listenTo(this.seriesCollection.shadowCollection, 'remove', function(model, collection, options) { - this.seriesCollection.fullCollection.resetFiltered(); + this.listenTo(this.artistCollection, 'remove', function(model, collection, options) { + this.artistCollection.fullCollection.resetFiltered(); this._renderView(); }); this.sortingOptions = { type : 'sorting', storeState : false, - viewCollection : this.seriesCollection, + viewCollection : this.artistCollection, items : [ { title : 'Title', @@ -243,7 +243,7 @@ module.exports = Marionette.Layout.extend({ _showTable : function() { this.currentView = new Backgrid.Grid({ - collection : this.seriesCollection, + collection : this.artistCollection, columns : this.columns, className : 'table table-hover' }); @@ -253,7 +253,7 @@ module.exports = Marionette.Layout.extend({ _showList : function() { this.currentView = new ListCollectionView({ - collection : this.seriesCollection + collection : this.artistCollection }); this._renderView(); @@ -261,14 +261,15 @@ module.exports = Marionette.Layout.extend({ _showPosters : function() { this.currentView = new PosterCollectionView({ - collection : this.seriesCollection + collection : this.artistCollection }); this._renderView(); }, _renderView : function() { - if (SeriesCollection.length === 0) { + // Problem is this is calling before artistCollection has updated. Where are the promises with backbone? + if (this.artistCollection.length === 0) { this.seriesRegion.show(new EmptyView()); this.toolbar.close(); @@ -282,13 +283,14 @@ module.exports = Marionette.Layout.extend({ }, _fetchCollection : function() { - this.seriesCollection.fetch(); + this.artistCollection.fetch(); + console.log('index page, collection: ', this.artistCollection); }, _setFilter : function(buttonContext) { var mode = buttonContext.model.get('key'); - this.seriesCollection.setFilterMode(mode); + this.artistCollection.setFilterMode(mode); }, _showToolbar : function() { @@ -317,22 +319,22 @@ module.exports = Marionette.Layout.extend({ _showFooter : function() { var footerModel = new FooterModel(); - var series = SeriesCollection.models.length; + var series = this.artistCollection.models.length; var episodes = 0; var episodeFiles = 0; var ended = 0; var continuing = 0; var monitored = 0; - _.each(SeriesCollection.models, function(model) { - episodes += model.get('episodeCount'); + _.each(this.artistCollection.models, function(model) { + episodes += model.get('episodeCount'); // TODO: Refactor to Seasons and Tracks episodeFiles += model.get('episodeFileCount'); - if (model.get('status').toLowerCase() === 'ended') { + /*if (model.get('status').toLowerCase() === 'ended') { ended++; } else { continuing++; - } + }*/ if (model.get('monitored')) { monitored++; diff --git a/src/UI/main.js b/src/UI/main.js index f46f68b93..40b9e9efe 100644 --- a/src/UI/main.js +++ b/src/UI/main.js @@ -5,7 +5,7 @@ var RouteBinder = require('./jQuery/RouteBinder'); var SignalRBroadcaster = require('./Shared/SignalRBroadcaster'); var NavbarLayout = require('./Navbar/NavbarLayout'); var AppLayout = require('./AppLayout'); -var SeriesController = require('./Series/SeriesController'); +var ArtistController = require('./Artist/ArtistController'); var Router = require('./Router'); var ModalController = require('./Shared/Modal/ModalController'); var ControlPanelController = require('./Shared/ControlPanel/ControlPanelController'); @@ -20,7 +20,7 @@ require('./Hotkeys/Hotkeys'); require('./Shared/piwikCheck'); require('./Shared/VersionChangeMonitor'); -new SeriesController(); +new ArtistController(); new ModalController(); new ControlPanelController(); new Router(); diff --git a/src/UI/vent.js b/src/UI/vent.js index 1b9346529..0baf8d0d4 100644 --- a/src/UI/vent.js +++ b/src/UI/vent.js @@ -5,6 +5,8 @@ var vent = new Wreqr.EventAggregator(); vent.Events = { SeriesAdded : 'series:added', SeriesDeleted : 'series:deleted', + ArtistAdded : 'artist:added', + ArtistDeleted : 'artist:deleted', CommandComplete : 'command:complete', ServerUpdated : 'server:updated', EpisodeFileDeleted : 'episodefile:deleted' From 5302c0915ee17ee5671da853e9b0aeb3409f3129 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Fri, 5 May 2017 08:22:12 -0500 Subject: [PATCH 03/11] Fixed wording --- src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs b/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs index 08ef40273..5372a9680 100644 --- a/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs +++ b/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Update.UpdateEngine } catch (InvalidOperationException e) { - _logger.Warn(e, "Couldn't start Lidarr Service (Most likely due to permission issues). falling back to console."); + _logger.Warn(e, "Couldn't start Lidarr Service (Most likely due to permission issues). Falling back to console."); StartConsole(installationFolder); } } From f97ed62fae66b822c27a54b5406e943e3aa6edfd Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Fri, 5 May 2017 12:57:58 -0500 Subject: [PATCH 04/11] Partially implemented artist overview. Needs review --- src/NzbDrone.Api/Music/ArtistModule.cs | 15 +---- src/NzbDrone.Api/Music/ArtistResource.cs | 5 +- .../Cloud/SonarrCloudRequestBuilder.cs | 6 ++ .../Datastore/Migration/111_setup_music.cs | 1 + .../SkyHook/Resource/ArtistResource.cs | 37 ++++++++++ .../MetadataSource/SkyHook/SkyHookProxy.cs | 67 ++++++++++++++++++- src/NzbDrone.Core/Music/Artist.cs | 1 + 7 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/NzbDrone.Api/Music/ArtistModule.cs b/src/NzbDrone.Api/Music/ArtistModule.cs index 1f7c433cf..c598713d6 100644 --- a/src/NzbDrone.Api/Music/ArtistModule.cs +++ b/src/NzbDrone.Api/Music/ArtistModule.cs @@ -98,7 +98,6 @@ namespace NzbDrone.Api.Music { //var seriesStats = _seriesStatisticsService.SeriesStatistics(); var artistResources = _artistService.GetAllArtists().ToResource(); - Console.WriteLine("[DEBUG] Returning {0} Artists", artistResources.Count); MapCoversToLocal(artistResources.ToArray()); //LinkSeriesStatistics(seriesResources, seriesStats); //PopulateAlternateTitles(seriesResources); @@ -106,9 +105,9 @@ namespace NzbDrone.Api.Music return artistResources; } - private int AddArtist(ArtistResource seriesResource) + private int AddArtist(ArtistResource artistResource) { - var model = seriesResource.ToModel(); + var model = artistResource.ToModel(); return _addSeriesService.AddArtist(model).Id; } @@ -175,16 +174,6 @@ namespace NzbDrone.Api.Music BroadcastResourceChange(ModelAction.Updated, message.Artist.Id); } - //public void Handle(ArtistDeletedEvent message) - //{ - // BroadcastResourceChange(ModelAction.Deleted, message.Artist.ToResource()); - //} - - //public void Handle(ArtistRenamedEvent message) - //{ - // BroadcastResourceChange(ModelAction.Updated, message.Artist.Id); - //} - //public void Handle(MediaCoversUpdatedEvent message) //{ // BroadcastResourceChange(ModelAction.Updated, message.Artist.Id); diff --git a/src/NzbDrone.Api/Music/ArtistResource.cs b/src/NzbDrone.Api/Music/ArtistResource.cs index c42c8f4d7..89bc764a3 100644 --- a/src/NzbDrone.Api/Music/ArtistResource.cs +++ b/src/NzbDrone.Api/Music/ArtistResource.cs @@ -22,6 +22,7 @@ namespace NzbDrone.Api.Music public int ItunesId { get; set; } //public List AlternateTitles { get; set; } //public string SortTitle { get; set; } + public string Overview { get; set; } public int AlbumCount { @@ -82,7 +83,7 @@ namespace NzbDrone.Api.Music //EpisodeFileCount //SizeOnDisk //Status = resource.Status, - //Overview = resource.Overview, + Overview = model.Overview, //NextAiring //PreviousAiring //Network = resource.Network, @@ -135,7 +136,7 @@ namespace NzbDrone.Api.Music //EpisodeFileCount //SizeOnDisk //Status = resource.Status, - //Overview = resource.Overview, + Overview = resource.Overview, //NextAiring //PreviousAiring //Network = resource.Network, diff --git a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs index 29f0e9d78..fa734300a 100644 --- a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs +++ b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs @@ -6,6 +6,7 @@ namespace NzbDrone.Common.Cloud { IHttpRequestBuilderFactory Services { get; } IHttpRequestBuilderFactory Search { get; } + IHttpRequestBuilderFactory InternalSearch { get; } IHttpRequestBuilderFactory SkyHookTvdb { get; } } @@ -19,6 +20,9 @@ namespace NzbDrone.Common.Cloud Search = new HttpRequestBuilder("https://itunes.apple.com/{route}/") .CreateFactory(); + InternalSearch = new HttpRequestBuilder("https://itunes.apple.com/WebObjects/MZStore.woa/wa/{route}") //viewArtist or search + .CreateFactory(); + SkyHookTvdb = new HttpRequestBuilder("http://skyhook.lidarr.tv/v1/tvdb/{route}/{language}/") .SetSegment("language", "en") .CreateFactory(); @@ -28,6 +32,8 @@ namespace NzbDrone.Common.Cloud public IHttpRequestBuilderFactory Search { get; } + public IHttpRequestBuilderFactory InternalSearch { get; } + public IHttpRequestBuilderFactory SkyHookTvdb { get; } } } diff --git a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs index faa998a0a..8a342e20c 100644 --- a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs +++ b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.Datastore.Migration .WithColumn("ArtistSlug").AsString().Nullable() //.Unique() .WithColumn("CleanTitle").AsString().Nullable() // Do we need this? .WithColumn("Monitored").AsBoolean() + .WithColumn("Overview").AsString().Nullable() .WithColumn("AlbumFolder").AsBoolean().Nullable() .WithColumn("ArtistFolder").AsBoolean().Nullable() .WithColumn("LastInfoSync").AsDateTime().Nullable() diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs index 88ad6eae6..5113d5394 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs @@ -5,6 +5,42 @@ using System.Text; namespace NzbDrone.Core.MetadataSource.SkyHook.Resource { + public class StorePlatformDataResource + { + public StorePlatformDataResource() { } + public ArtistInfoResource Artist { get; set; } + //public Lockup lockup { get; set; } + } + + public class ArtistInfoResource + { + public ArtistInfoResource() { } + public Dictionary Results { get; set; } + + public bool HasArtistBio { get; set; } + + public string url { get; set; } + public string shortUrl { get; set; } + + public List artistContemporaries { get; set; } + public List genreNames { get; set; } + public bool hasSocialPosts { get; set; } + public string artistBio { get; set; } + public bool isGroup { get; set; } + public string id { get; set; } + public string bornOrFormed { get; set; } + public string name { get; set; } + public string latestAlbumContentId { get; set; } + public string nameRaw { get; set; } + + //public string kind { get; set; } + //public List gallery { get; set; } + //public List genres { get; set; } + public List artistInfluencers { get; set; } + public List artistFollowers { get; set; } + //public string umcArtistImageUrl { get; set; } + } + public class AlbumResource { public AlbumResource() @@ -37,5 +73,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public List Results { get; set; } //public string ArtistName { get; set; } //public List Albums { get; set; } + public StorePlatformDataResource StorePlatformData { get; set; } } } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index e7b97350b..16f3b3a50 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -22,11 +22,13 @@ namespace NzbDrone.Core.MetadataSource.SkyHook private readonly Logger _logger; private readonly IHttpRequestBuilderFactory _requestBuilder; + private readonly IHttpRequestBuilderFactory _internalRequestBuilder; public SkyHookProxy(IHttpClient httpClient, ILidarrCloudRequestBuilder requestBuilder, Logger logger) { _httpClient = httpClient; _requestBuilder = requestBuilder.Search; + _internalRequestBuilder = requestBuilder.InternalSearch; _logger = logger; } @@ -124,14 +126,59 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } } + //public Artist GetArtistInfo(int itunesId) + //{ + // Console.WriteLine("[GetArtistInfo] id:" + itunesId); + // //https://itunes.apple.com/lookup?id=909253 + // //var httpRequest = _requestBuilder.Create() + // // .SetSegment("route", "lookup") + // // .AddQueryParam("id", itunesId.ToString()) + // // .Build(); + + // // TODO: Add special header, add Overview to Artist model + // var httpRequest = _requestBuilder.Create() + // .SetSegment("route", "viewArtist") + // .AddQueryParam("id", itunesId.ToString()) + // .Build(); + // httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); + + // httpRequest.AllowAutoRedirect = true; + // httpRequest.SuppressHttpError = true; + + // var httpResponse = _httpClient.Get(httpRequest); + + // if (httpResponse.HasHttpError) + // { + // if (httpResponse.StatusCode == HttpStatusCode.NotFound) + // { + // throw new ArtistNotFoundException(itunesId); + // } + // else + // { + // throw new HttpException(httpRequest, httpResponse); + // } + // } + + // Console.WriteLine("GetArtistInfo, GetArtistInfo"); + // return MapArtists(httpResponse.Resource)[0]; + //} + public Tuple> GetArtistInfo(int itunesId) { Console.WriteLine("[GetArtistInfo] id:" + itunesId); //https://itunes.apple.com/lookup?id=909253 - var httpRequest = _requestBuilder.Create() - .SetSegment("route", "lookup") + //var httpRequest = _requestBuilder.Create() + // .SetSegment("route", "lookup") + // .AddQueryParam("id", itunesId.ToString()) + // .Build(); + + // TODO: Add special header, add Overview to Artist model + var httpRequest = _internalRequestBuilder.Create() + .SetSegment("route", "viewArtist") .AddQueryParam("id", itunesId.ToString()) .Build(); + httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); + httpRequest.Headers.ContentType = "application/json"; httpRequest.AllowAutoRedirect = true; httpRequest.SuppressHttpError = true; @@ -154,7 +201,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook //var tracks = httpResponse.Resource.Episodes.Select(MapEpisode); //var artist = MapArtist(httpResponse.Resource); // I don't know how we are getting tracks from iTunes yet. - return new Tuple>(MapArtists(httpResponse.Resource)[0], new List()); + return new Tuple>(MapArtistInfo(httpResponse.Resource.StorePlatformData.Artist.Results[0]), new List()); //return new Tuple>(artist, tracks.ToList()); } public List SearchForNewArtist(string title) @@ -208,6 +255,20 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } } + private Artist MapArtistInfo(ArtistInfoResource resource) + { + // This expects ArtistInfoResource, thus just need to populate one artist + Artist artist = new Artist(); + artist.Overview = resource.artistBio; + artist.ArtistName = resource.name; + foreach(var genre in resource.genreNames) + { + artist.Genres.Add(genre); + } + + return artist; + } + private List MapArtists(ArtistResource resource) { Album tempAlbum; diff --git a/src/NzbDrone.Core/Music/Artist.cs b/src/NzbDrone.Core/Music/Artist.cs index 2fde7c0ac..457176bab 100644 --- a/src/NzbDrone.Core/Music/Artist.cs +++ b/src/NzbDrone.Core/Music/Artist.cs @@ -26,6 +26,7 @@ namespace NzbDrone.Core.Music public string ArtistName { get; set; } public string ArtistSlug { get; set; } public string CleanTitle { get; set; } + public string Overview { get; set; } public bool Monitored { get; set; } public bool AlbumFolder { get; set; } public bool ArtistFolder { get; set; } From 3662bb933bf94926e199529363c2091103a26905 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Fri, 5 May 2017 13:35:28 -0500 Subject: [PATCH 05/11] Fixed up API to instead call multiple APIs and mash results together --- .../MetadataSource/SkyHook/SkyHookProxy.cs | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 16f3b3a50..735ee3716 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -165,25 +165,24 @@ namespace NzbDrone.Core.MetadataSource.SkyHook public Tuple> GetArtistInfo(int itunesId) { - Console.WriteLine("[GetArtistInfo] id:" + itunesId); - //https://itunes.apple.com/lookup?id=909253 - //var httpRequest = _requestBuilder.Create() - // .SetSegment("route", "lookup") - // .AddQueryParam("id", itunesId.ToString()) - // .Build(); - - // TODO: Add special header, add Overview to Artist model - var httpRequest = _internalRequestBuilder.Create() + _logger.Debug("Getting Artist with iTunesID of {0}", itunesId); + var httpRequest1 = _requestBuilder.Create() + .SetSegment("route", "lookup") + .AddQueryParam("id", itunesId.ToString()) + .Build(); + + var httpRequest2 = _internalRequestBuilder.Create() .SetSegment("route", "viewArtist") .AddQueryParam("id", itunesId.ToString()) .Build(); - httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); - httpRequest.Headers.ContentType = "application/json"; + httpRequest2.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); + httpRequest2.Headers.ContentType = "application/json"; - httpRequest.AllowAutoRedirect = true; - httpRequest.SuppressHttpError = true; + httpRequest1.AllowAutoRedirect = true; + httpRequest1.SuppressHttpError = true; - var httpResponse = _httpClient.Get(httpRequest); + var httpResponse = _httpClient.Get(httpRequest1); + var httpResponse2 = _httpClient.Get(httpRequest2); if (httpResponse.HasHttpError) { @@ -193,16 +192,22 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } else { - throw new HttpException(httpRequest, httpResponse); + throw new HttpException(httpRequest1, httpResponse); + } + } + + List artists = MapArtists(httpResponse.Resource); + + if (httpResponse2.HasHttpError) + { + if (artists.Count == 1) + { + artists[0].Overview = httpResponse2.Resource.StorePlatformData.Artist.Results[itunesId].artistBio; } } - Console.WriteLine("GetArtistInfo, GetArtistInfo"); - //var tracks = httpResponse.Resource.Episodes.Select(MapEpisode); - //var artist = MapArtist(httpResponse.Resource); // I don't know how we are getting tracks from iTunes yet. - return new Tuple>(MapArtistInfo(httpResponse.Resource.StorePlatformData.Artist.Results[0]), new List()); - //return new Tuple>(artist, tracks.ToList()); + return new Tuple>(artists[0], new List()); } public List SearchForNewArtist(string title) { From 6aff6de378b1d221731236c3cb24343239055975 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Fri, 5 May 2017 14:33:46 -0500 Subject: [PATCH 06/11] Added Artist Overview. --- .../MetadataSource/SkyHook/SkyHookProxy.cs | 47 +++++++++++++++---- src/NzbDrone.Core/Music/AddArtistService.cs | 7 ++- src/UI/Handlebars/Helpers/Series.js | 12 +++++ .../SeriesOverviewItemViewTemplate.hbs | 10 ++-- src/UI/Series/Index/SeriesIndexLayout.js | 1 + src/UI/Series/series.less | 6 +++ 6 files changed, 64 insertions(+), 19 deletions(-) diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 735ee3716..84600ae18 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -182,7 +182,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook httpRequest1.SuppressHttpError = true; var httpResponse = _httpClient.Get(httpRequest1); - var httpResponse2 = _httpClient.Get(httpRequest2); + if (httpResponse.HasHttpError) { @@ -197,18 +197,18 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } List artists = MapArtists(httpResponse.Resource); - - if (httpResponse2.HasHttpError) + List newArtists = new List(artists.Count); + int count = 0; + foreach (var artist in artists) { - if (artists.Count == 1) - { - artists[0].Overview = httpResponse2.Resource.StorePlatformData.Artist.Results[itunesId].artistBio; - } + newArtists.Add(AddOverview(artist)); + count++; } // I don't know how we are getting tracks from iTunes yet. - return new Tuple>(artists[0], new List()); + return new Tuple>(newArtists[0], new List()); } + public List SearchForNewArtist(string title) { try @@ -247,7 +247,18 @@ namespace NzbDrone.Core.MetadataSource.SkyHook var httpResponse = _httpClient.Get(httpRequest); - return MapArtists(httpResponse.Resource); + + List artists = MapArtists(httpResponse.Resource); + List newArtists = new List(artists.Count); + int count = 0; + foreach (var artist in artists) + { + newArtists.Add(AddOverview(artist)); + count++; + } + + + return newArtists; } catch (HttpException) { @@ -260,6 +271,24 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } } + private Artist AddOverview(Artist artist) + { + var httpRequest = _internalRequestBuilder.Create() + .SetSegment("route", "viewArtist") + .AddQueryParam("id", artist.ItunesId.ToString()) + .Build(); + httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); + httpRequest.Headers.ContentType = "application/json"; + var httpResponse = _httpClient.Get(httpRequest); + + if (!httpResponse.HasHttpError) + { + artist.Overview = httpResponse.Resource.StorePlatformData.Artist.Results[artist.ItunesId].artistBio; + } + + return artist; + } + private Artist MapArtistInfo(ArtistInfoResource resource) { // This expects ArtistInfoResource, thus just need to populate one artist diff --git a/src/NzbDrone.Core/Music/AddArtistService.cs b/src/NzbDrone.Core/Music/AddArtistService.cs index 072a67754..3ca636d0b 100644 --- a/src/NzbDrone.Core/Music/AddArtistService.cs +++ b/src/NzbDrone.Core/Music/AddArtistService.cs @@ -79,18 +79,17 @@ namespace NzbDrone.Core.Music } catch (SeriesNotFoundException) { - _logger.Error("tvdbid {1} was not found, it may have been removed from TheTVDB.", newArtist.ItunesId); + _logger.Error("iTunesId {1} was not found, it may have been removed from iTunes.", newArtist.ItunesId); throw new ValidationException(new List { - new ValidationFailure("TvdbId", "A series with this ID was not found", newArtist.ItunesId) + new ValidationFailure("iTunesId", "An artist with this ID was not found", newArtist.ItunesId) }); } var artist = tuple.Item1; - // If seasons were passed in on the new series use them, otherwise use the seasons from Skyhook - // TODO: Refactor for albums + // If albums were passed in on the new artist use them, otherwise use the albums from Skyhook newArtist.Albums = newArtist.Albums != null && newArtist.Albums.Any() ? newArtist.Albums : artist.Albums; artist.ApplyChanges(newArtist); diff --git a/src/UI/Handlebars/Helpers/Series.js b/src/UI/Handlebars/Helpers/Series.js index ecade70eb..4003a7e5b 100644 --- a/src/UI/Handlebars/Helpers/Series.js +++ b/src/UI/Handlebars/Helpers/Series.js @@ -71,6 +71,18 @@ Handlebars.registerHelper('seasonCountHelper', function() { return new Handlebars.SafeString('{0} Seasons'.format(seasonCount)); }); +Handlebars.registerHelper ('truncate', function (str, len) { + if (str && str.length > len && str.length > 0) { + var new_str = str + " "; + new_str = str.substr (0, len); + new_str = str.substr (0, new_str.lastIndexOf(" ")); + new_str = (new_str.length > 0) ? new_str : str.substr (0, len); + + return new Handlebars.SafeString ( new_str +'...' ); + } + return str; +}); + Handlebars.registerHelper('albumCountHelper', function() { var albumCount = this.albumCount; diff --git a/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs b/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs index 4e9785b50..f562800fc 100644 --- a/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs +++ b/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs @@ -20,12 +20,10 @@
-
- -
- {{overview}} -
-
+
+
+ {{truncate overview 600}} +
diff --git a/src/UI/Series/Index/SeriesIndexLayout.js b/src/UI/Series/Index/SeriesIndexLayout.js index 049bd267b..a8ae3d61c 100644 --- a/src/UI/Series/Index/SeriesIndexLayout.js +++ b/src/UI/Series/Index/SeriesIndexLayout.js @@ -20,6 +20,7 @@ require('../../Mixins/backbone.signalr.mixin'); module.exports = Marionette.Layout.extend({ template : 'Series/Index/SeriesIndexLayoutTemplate', + regions : { seriesRegion : '#x-series', toolbar : '#x-toolbar', diff --git a/src/UI/Series/series.less b/src/UI/Series/series.less index c023a7da5..f17229f6f 100644 --- a/src/UI/Series/series.less +++ b/src/UI/Series/series.less @@ -8,6 +8,12 @@ max-width: 100%; } +.truncate { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + .edit-series-modal, .delete-series-modal { overflow : visible; From a8b40ce1f6ee49640c32dbb68e0633fc3b5b716a Mon Sep 17 00:00:00 2001 From: runraid Date: Sat, 6 May 2017 07:44:46 -0700 Subject: [PATCH 07/11] Update readme, replace sonarr->lidarr --- .github/ISSUE_TEMPLATE.md | 2 +- .gitignore | 2 +- .idea/.name | 2 +- CLA.md | 4 ++-- CONTRIBUTING.md | 8 ++++---- README.md | 6 ++++-- debian/control | 8 ++++---- debian/copyright | 4 ++-- gulp/start.js | 4 ++-- package.json | 8 ++++---- setup/nzbdrone.iss | 8 ++++---- src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs | 6 +++--- 12 files changed, 32 insertions(+), 30 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6bd416a38..8246c9977 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,4 +2,4 @@ Provide a description of the feature request or bug, the more details the better. -Please use https://forums.sonarr.tv/ for support or other questions. (When in doubt, use the forums) +Please use http://lidarr.audio for support or other questions. (When in doubt, use the forums) diff --git a/.gitignore b/.gitignore index 9ad1a84be..a340f3295 100644 --- a/.gitignore +++ b/.gitignore @@ -101,7 +101,7 @@ App_Data/*.ldf _NCrunch_* _TeamCity* -# Sonarr +# Lidarr config.xml nzbdrone.log*txt UpdateLogs/ diff --git a/.idea/.name b/.idea/.name index 02629676e..e402c1d9d 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -Sonarr \ No newline at end of file +Lidarr \ No newline at end of file diff --git a/CLA.md b/CLA.md index 40adac7f6..463c6c14c 100644 --- a/CLA.md +++ b/CLA.md @@ -1,6 +1,6 @@ -# Sonarr Individual Contributor License Agreement # +# Lidarr Individual Contributor License Agreement # -Thank you for your interest in contributing to Sonarr ("We" or "Us"). +Thank you for your interest in contributing to Lidarr ("We" or "Us"). This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please complete the form below. This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us. ## 1. Definitions ## diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ab945cb0c..f402f5c63 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # How to Contribute # -We're always looking for people to help make Sonarr even better, there are a number of ways to contribute. +We're always looking for people to help make Lidarr even better, there are a number of ways to contribute. ## Documentation ## Setup guides, FAQ, the more information we have on the wiki the better. @@ -15,7 +15,7 @@ Setup guides, FAQ, the more information we have on the wiki the better. ### Getting started ### -1. Fork Sonarr +1. Fork Lidarr 2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)* 3. Run `npm install` 4. Run `npm start` - Used to compile the UI components and copy them. @@ -24,8 +24,8 @@ Setup guides, FAQ, the more information we have on the wiki the better. 5. Compile in Visual Studio ### Contributing Code ### -- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Sonarr/Sonarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first) -- Rebase from Sonarr's develop branch, don't merge +- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/lidarr/Lidarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first) +- Rebase from Lidarr's develop branch, don't merge - Make meaningful commits, or squash them - Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements - Reach out to us on the forums or on IRC if you have any questions diff --git a/README.md b/README.md index ac8e58d06..75e7bfdf8 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ Lidarr is a music collection manager for Usenet and BitTorrent users. It can mon ## Feature Requests -[![Feature Requests](http://feathub.com/mattman86/Lidarr?format=svg)](http://feathub.com/mattman86/Lidarr) +[![Feature Requests](http://feathub.com/lidarr/Lidarr?format=svg)](http://feathub.com/lidarr/Lidarr) ## Configuring Development Environment: ### Requirements -* Visual Studio 2015 (https://www.visualstudio.com/vs/) +* Visual Studio 2015 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/). * [Git](https://git-scm.com/downloads) * [NodeJS](https://nodejs.org/en/download/) @@ -35,6 +35,8 @@ Lidarr is a music collection manager for Usenet and BitTorrent users. It can mon * Grab the submodules `git submodule init && git submodule update` * Install the required Node Packages `npm install` * Start gulp to monitor your dev environment for any changes that need post processing using `npm start` command. +* Run the project in Visual Studio +* Open http://localhost:8686 *Please note gulp must be running at all times while you are working with Lidarr client source files.* diff --git a/debian/control b/debian/control index ba30c02ee..34586f51d 100644 --- a/debian/control +++ b/debian/control @@ -2,11 +2,11 @@ Section: web Priority: optional Maintainer: Sonarr Source: nzbdrone -Homepage: https://sonarr.tv -Vcs-Git: git@github.com:Sonarr/Sonarr.git -Vcs-Browser: https://github.com/Sonarr/Sonarr +Homepage: https://lidarr.audio +Vcs-Git: git@github.com:lidarr/Lidarr.git +Vcs-Browser: https://github.com/lidarr/Lidarr Package: nzbdrone Architecture: all Depends: libmono-cil-dev (>= 3.2), sqlite3 (>= 3.7), mediainfo (>= 0.7.52) -Description: Sonarr is an internet PVR +Description: Lidarr is a music collection manager diff --git a/debian/copyright b/debian/copyright index 466d37fce..667d82a43 100755 --- a/debian/copyright +++ b/debian/copyright @@ -1,9 +1,9 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: nzbdrone -Source: https://github.com/Sonarr/Sonarr +Source: https://github.com/lidarr/Lidarr Files: * -Copyright: 2010-2016 Sonarr +Copyright: 2010-2016 Lidarr License: GPL-3.0+ diff --git a/gulp/start.js b/gulp/start.js index 5b5f88044..296eb6836 100644 --- a/gulp/start.js +++ b/gulp/start.js @@ -1,4 +1,4 @@ -// will download and run sonarr (server) in a non-windows enviroment +// will download and run Lidarr (server) in a non-windows enviroment // you can use this if you don't care about the server code and just want to work // with the web code. @@ -31,7 +31,7 @@ function getLatest(cb) { } }); - var url = 'http://services.sonarr.tv/v1/update/' + branch + '?os=osx'; + var url = 'http://services.lidarr.audio/v1/update/' + branch + '?os=osx'; console.log('Checking for latest version:', url); diff --git a/package.json b/package.json index c3556ed7f..12bc565c3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "Sonarr", - "version": "2.0.0", - "description": "Sonarr", + "name": "Lidarr", + "version": "1.0.0", + "description": "Lidarr", "main": "main.js", "scripts": { "build": "gulp build", @@ -9,7 +9,7 @@ }, "repository": { "type": "git", - "url": "git://github.com/Sonarr/Sonarr.git" + "url": "git://github.com/lidarr/Lidarr.git" }, "author": "", "license": "GPL-3.0", diff --git a/setup/nzbdrone.iss b/setup/nzbdrone.iss index e667c0d03..fd8094b0c 100644 --- a/setup/nzbdrone.iss +++ b/setup/nzbdrone.iss @@ -1,10 +1,10 @@ ; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! -#define AppName "Sonarr" -#define AppPublisher "Team Sonarr" -#define AppURL "https://sonarr.tv/" -#define ForumsURL "https://forums.sonarr.tv/" +#define AppName "Lidarr" +#define AppPublisher "Team Lidarr" +#define AppURL "https://lidarr.audio/" +#define ForumsURL "https://forums.lidarr.audio/" #define AppExeName "NzbDrone.exe" #define BuildNumber "2.0" #define BuildNumber GetEnv('BUILD_NUMBER') diff --git a/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs b/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs index 40c89af35..ad5ae103d 100644 --- a/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs +++ b/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs @@ -15,7 +15,7 @@
reddit.com/r/Lidarr
Source
-
github.com/Lidarr/Lidarr
+
github.com/Lidarr/Lidarr
Contributors
DB and API - Majora2007
@@ -25,8 +25,8 @@
Consultation - Galli-leo
Feature Requests
-
feathub.com/mattman86/Lidarr
-
github.com/mattman86/Lidarr/issues (Please post issues on the forum first and not on github)
+
feathub.com/lidarr/Lidarr
+
github.com/lidarr/Lidarr/issues (Please post issues on the forum first and not on github)
From d04cd2ae95cd7d68edfc03d7078e9203d39d58fe Mon Sep 17 00:00:00 2001 From: runraid Date: Sat, 6 May 2017 08:03:41 -0700 Subject: [PATCH 08/11] Revert girl url --- src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs b/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs index ad5ae103d..b74f00ac5 100644 --- a/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs +++ b/src/UI/System/Info/MoreInfo/MoreInfoViewTemplate.hbs @@ -15,7 +15,7 @@
reddit.com/r/Lidarr
Source
-
github.com/Lidarr/Lidarr
+
github.com/Lidarr/Lidarr
Contributors
DB and API - Majora2007
From b481bc6e45e71ecf0a53b73817b7e81d7e9c2c5b Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Sun, 7 May 2017 11:58:24 -0500 Subject: [PATCH 09/11] Partially implemented ArtistRefreshCommand. In order to move forward, API may need to be switched to Spotify. --- .../Datastore/Migration/111_setup_music.cs | 1 + src/NzbDrone.Core/Jobs/TaskManager.cs | 6 +- .../MetadataSource/SkyHook/SkyHookProxy.cs | 7 + src/NzbDrone.Core/Music/AddArtistValidator.cs | 4 +- src/NzbDrone.Core/Music/Artist.cs | 23 --- src/NzbDrone.Core/Music/ArtistAddedHandler.cs | 26 +++ .../Music/Commands/RefreshArtistCommand.cs | 26 +++ .../Events/ArtistRefreshStartingEvent.cs | 18 ++ .../Music/Events/TrackInfoRefreshedEvent.cs | 23 +++ .../Music/RefreshArtistService.cs | 173 ++++++++++++++++++ .../Music/RefreshTrackService.cs | 125 +++++++++++++ src/NzbDrone.Core/Music/Track.cs | 10 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 6 + .../SeriesOverviewItemViewTemplate.hbs | 5 +- 14 files changed, 420 insertions(+), 33 deletions(-) create mode 100644 src/NzbDrone.Core/Music/ArtistAddedHandler.cs create mode 100644 src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs create mode 100644 src/NzbDrone.Core/Music/Events/ArtistRefreshStartingEvent.cs create mode 100644 src/NzbDrone.Core/Music/Events/TrackInfoRefreshedEvent.cs create mode 100644 src/NzbDrone.Core/Music/RefreshArtistService.cs create mode 100644 src/NzbDrone.Core/Music/RefreshTrackService.cs diff --git a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs index 8a342e20c..52067b23b 100644 --- a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs +++ b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs @@ -55,6 +55,7 @@ namespace NzbDrone.Core.Datastore.Migration .WithColumn("Title").AsString().Nullable() .WithColumn("Ignored").AsBoolean().Nullable() .WithColumn("Explict").AsBoolean() + .WithColumn("Monitored").AsBoolean() .WithColumn("TrackExplicitName").AsString().Nullable() .WithColumn("TrackCensoredName").AsString().Nullable() .WithColumn("TrackFileId").AsInt32().Nullable() diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index 3ad7b909a..ef974f612 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -16,6 +16,7 @@ using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv.Commands; using NzbDrone.Core.Update.Commands; +using NzbDrone.Core.Music.Commands; namespace NzbDrone.Core.Jobs { @@ -64,9 +65,10 @@ namespace NzbDrone.Core.Jobs new ScheduledTask{ Interval = 1, TypeName = typeof(CheckForFinishedDownloadCommand).FullName}, new ScheduledTask{ Interval = 5, TypeName = typeof(MessagingCleanupCommand).FullName}, new ScheduledTask{ Interval = 6*60, TypeName = typeof(ApplicationUpdateCommand).FullName}, - new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName}, + //new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName}, new ScheduledTask{ Interval = 6*60, TypeName = typeof(CheckHealthCommand).FullName}, - new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshSeriesCommand).FullName}, + new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshArtistCommand).FullName}, + new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshSeriesCommand).FullName}, // TODO: Remove new ScheduledTask{ Interval = 24*60, TypeName = typeof(HousekeepingCommand).FullName}, new ScheduledTask{ Interval = 7*24*60, TypeName = typeof(BackupCommand).FullName}, diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 84600ae18..a35e33835 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -32,6 +32,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook _logger = logger; } + + public Tuple> GetSeriesInfo(int tvdbSeriesId) { Console.WriteLine("[GetSeriesInfo] id:" + tvdbSeriesId); @@ -165,6 +167,11 @@ namespace NzbDrone.Core.MetadataSource.SkyHook public Tuple> GetArtistInfo(int itunesId) { + // TODO: [GetArtistInfo]: This needs to return a set of tracks from iTunes. + // This call is expected to return information about an artist and the tracks that make up said artist. + // To do this, we need 2-3 API calls. 1st is to gather information about the artist and the albums the artist has. This is https://itunes.apple.com/search?entity=album&id=itunesId + // Next call is to populate the overview field and calls the internal API + // Finally, we need to, for each album, get all tracks, which means calling this N times: https://itunes.apple.com/search?entity=musicTrack&term=artistName (id will not work) _logger.Debug("Getting Artist with iTunesID of {0}", itunesId); var httpRequest1 = _requestBuilder.Create() .SetSegment("route", "lookup") diff --git a/src/NzbDrone.Core/Music/AddArtistValidator.cs b/src/NzbDrone.Core/Music/AddArtistValidator.cs index a21e3bac5..ab789c2fc 100644 --- a/src/NzbDrone.Core/Music/AddArtistValidator.cs +++ b/src/NzbDrone.Core/Music/AddArtistValidator.cs @@ -19,7 +19,7 @@ namespace NzbDrone.Core.Music SeriesPathValidator seriesPathValidator, DroneFactoryValidator droneFactoryValidator, SeriesAncestorValidator seriesAncestorValidator, - ArtistSlugValidator seriesTitleSlugValidator) + ArtistSlugValidator artistTitleSlugValidator) { RuleFor(c => c.Path).Cascade(CascadeMode.StopOnFirstFailure) .IsValidPath() @@ -28,7 +28,7 @@ namespace NzbDrone.Core.Music .SetValidator(droneFactoryValidator) .SetValidator(seriesAncestorValidator); - RuleFor(c => c.ArtistSlug).SetValidator(seriesTitleSlugValidator);// TODO: Check if we are going to use a slug or artistName + RuleFor(c => c.ArtistSlug).SetValidator(artistTitleSlugValidator);// TODO: Check if we are going to use a slug or artistName } } } diff --git a/src/NzbDrone.Core/Music/Artist.cs b/src/NzbDrone.Core/Music/Artist.cs index 457176bab..ef89b2998 100644 --- a/src/NzbDrone.Core/Music/Artist.cs +++ b/src/NzbDrone.Core/Music/Artist.cs @@ -32,35 +32,19 @@ namespace NzbDrone.Core.Music public bool ArtistFolder { get; set; } public DateTime? LastInfoSync { get; set; } public DateTime? LastDiskSync { get; set; } - public int Status { get; set; } // TODO: Figure out what this is, do we need it? public string Path { get; set; } public List Images { get; set; } public List Genres { get; set; } public int QualityProfileId { get; set; } - public string RootFolderPath { get; set; } public DateTime Added { get; set; } public LazyLoaded Profile { get; set; } public int ProfileId { get; set; } public List Albums { get; set; } public HashSet Tags { get; set; } - public AddSeriesOptions AddOptions { get; set; } - //public string SortTitle { get; set; } - //public SeriesStatusType Status { get; set; } - //public int Runtime { get; set; } - //public SeriesTypes SeriesType { get; set; } - //public string Network { get; set; } - //public bool UseSceneNumbering { get; set; } - //public string TitleSlug { get; set; } - //public int Year { get; set; } - //public Ratings Ratings { get; set; } - //public List Actors { get; set; } // MOve to album? - //public string Certification { get; set; } - //public DateTime? FirstAired { get; set; } - public override string ToString() { return string.Format("[{0}][{1}]", ItunesId, ArtistName.NullSafe()); @@ -88,18 +72,11 @@ namespace NzbDrone.Core.Music ArtistFolder = otherArtist.ArtistFolder; AddOptions = otherArtist.AddOptions; - - //TODO: Implement - ItunesId = otherArtist.ItunesId; - Albums = otherArtist.Albums; Path = otherArtist.Path; ProfileId = otherArtist.ProfileId; - AlbumFolder = otherArtist.AlbumFolder; Monitored = otherArtist.Monitored; - - //SeriesType = otherArtist.SeriesType; RootFolderPath = otherArtist.RootFolderPath; Tags = otherArtist.Tags; AddOptions = otherArtist.AddOptions; diff --git a/src/NzbDrone.Core/Music/ArtistAddedHandler.cs b/src/NzbDrone.Core/Music/ArtistAddedHandler.cs new file mode 100644 index 000000000..b2da66db9 --- /dev/null +++ b/src/NzbDrone.Core/Music/ArtistAddedHandler.cs @@ -0,0 +1,26 @@ +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Music.Commands; +using NzbDrone.Core.Music.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Music +{ + public class ArtistAddedHandler : IHandle + { + private readonly IManageCommandQueue _commandQueueManager; + + public ArtistAddedHandler(IManageCommandQueue commandQueueManager) + { + _commandQueueManager = commandQueueManager; + } + + public void Handle(ArtistAddedEvent message) + { + _commandQueueManager.Push(new RefreshArtistCommand(message.Artist.Id)); + } + } +} diff --git a/src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs b/src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs new file mode 100644 index 000000000..fdf3e56d6 --- /dev/null +++ b/src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs @@ -0,0 +1,26 @@ +using NzbDrone.Core.Messaging.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Music.Commands +{ + public class RefreshArtistCommand : Command + { + public int? ArtistId { get; set; } + + public RefreshArtistCommand() + { + } + + public RefreshArtistCommand(int? artistId) + { + ArtistId = artistId; + } + + public override bool SendUpdatesToClient => true; + + public override bool UpdateScheduledTask => !ArtistId.HasValue; + } +} diff --git a/src/NzbDrone.Core/Music/Events/ArtistRefreshStartingEvent.cs b/src/NzbDrone.Core/Music/Events/ArtistRefreshStartingEvent.cs new file mode 100644 index 000000000..45d9a50c8 --- /dev/null +++ b/src/NzbDrone.Core/Music/Events/ArtistRefreshStartingEvent.cs @@ -0,0 +1,18 @@ +using NzbDrone.Common.Messaging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Music.Events +{ + public class ArtistRefreshStartingEvent : IEvent + { + public bool ManualTrigger { get; set; } + + public ArtistRefreshStartingEvent(bool manualTrigger) + { + ManualTrigger = manualTrigger; + } + } +} diff --git a/src/NzbDrone.Core/Music/Events/TrackInfoRefreshedEvent.cs b/src/NzbDrone.Core/Music/Events/TrackInfoRefreshedEvent.cs new file mode 100644 index 000000000..99661c480 --- /dev/null +++ b/src/NzbDrone.Core/Music/Events/TrackInfoRefreshedEvent.cs @@ -0,0 +1,23 @@ +using NzbDrone.Common.Messaging; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Music.Events +{ + public class TrackInfoRefreshedEvent : IEvent + { + public Artist Artist { get; set; } + public ReadOnlyCollection Added { get; private set; } + public ReadOnlyCollection Updated { get; private set; } + + public TrackInfoRefreshedEvent(Artist artist, IList added, IList updated) + { + Artist = artist; + Added = new ReadOnlyCollection(added); + Updated = new ReadOnlyCollection(updated); + } + } +} diff --git a/src/NzbDrone.Core/Music/RefreshArtistService.cs b/src/NzbDrone.Core/Music/RefreshArtistService.cs new file mode 100644 index 000000000..401753ef8 --- /dev/null +++ b/src/NzbDrone.Core/Music/RefreshArtistService.cs @@ -0,0 +1,173 @@ +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Core.Exceptions; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.MetadataSource.SkyHook; +using NzbDrone.Core.Music.Commands; +using NzbDrone.Core.Music.Events; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Music +{ + public class RefreshArtistService : IExecute + { + private readonly IProvideArtistInfo _artistInfo; + private readonly IArtistService _artistService; + private readonly IRefreshTrackService _refreshTrackService; + private readonly IEventAggregator _eventAggregator; + //private readonly IDailySeriesService _dailySeriesService; + private readonly IDiskScanService _diskScanService; + //private readonly ICheckIfArtistShouldBeRefreshed _checkIfArtistShouldBeRefreshed; + private readonly Logger _logger; + + public RefreshArtistService(IProvideArtistInfo artistInfo, + IArtistService artistService, + IRefreshTrackService refreshTrackService, + IEventAggregator eventAggregator, + IDiskScanService diskScanService, + //ICheckIfArtistShouldBeRefreshed checkIfArtistShouldBeRefreshed, + Logger logger) + { + _artistInfo = artistInfo; + _artistService = artistService; + _refreshTrackService = refreshTrackService; + _eventAggregator = eventAggregator; + _diskScanService = diskScanService; + //_checkIfArtistShouldBeRefreshed = checkIfArtistShouldBeRefreshed; + _logger = logger; + } + + private void RefreshArtistInfo(Artist artist) + { + _logger.ProgressInfo("Updating Info for {0}", artist.ArtistName); + + Tuple> tuple; + + try + { + tuple = _artistInfo.GetArtistInfo(artist.ItunesId); + } + catch (ArtistNotFoundException) + { + _logger.Error("Artist '{0}' (itunesid {1}) was not found, it may have been removed from iTunes.", artist.ArtistName, artist.ItunesId); + return; + } + + var artistInfo = tuple.Item1; + + if (artist.ItunesId != artistInfo.ItunesId) + { + _logger.Warn("Artist '{0}' (itunes {1}) was replaced with '{2}' (itunes {3}), because the original was a duplicate.", artist.ArtistName, artist.ItunesId, artistInfo.ArtistName, artistInfo.ItunesId); + artist.ItunesId = artistInfo.ItunesId; + } + + artist.ArtistName = artistInfo.ArtistName; + artist.ArtistSlug = artistInfo.ArtistSlug; + artist.Overview = artistInfo.Overview; + artist.Status = artistInfo.Status; + artist.CleanTitle = artistInfo.CleanTitle; + artist.LastInfoSync = DateTime.UtcNow; + artist.Images = artistInfo.Images; + //artist.Actors = artistInfo.Actors; + artist.Genres = artistInfo.Genres; + + try + { + artist.Path = new DirectoryInfo(artist.Path).FullName; + artist.Path = artist.Path.GetActualCasing(); + } + catch (Exception e) + { + _logger.Warn(e, "Couldn't update artist path for " + artist.Path); + } + + artist.Albums = UpdateAlbums(artist, artistInfo); + + _artistService.UpdateArtist(artist); + _refreshTrackService.RefreshTrackInfo(artist, tuple.Item2); + + _logger.Debug("Finished artist refresh for {0}", artist.ArtistName); + _eventAggregator.PublishEvent(new ArtistUpdatedEvent(artist)); + } + + private List UpdateAlbums(Artist artist, Artist artistInfo) + { + var albums = artistInfo.Albums.DistinctBy(s => s.AlbumId).ToList(); + + foreach (var album in albums) + { + var existingAlbum = artist.Albums.FirstOrDefault(s => s.AlbumId == album.AlbumId); + + //Todo: Should this should use the previous season's monitored state? + if (existingAlbum == null) + { + //if (album.SeasonNumber == 0) + //{ + // album.Monitored = false; + // continue; + //} + + _logger.Debug("New album ({0}) for artist: [{1}] {2}, setting monitored to true", album.Title, artist.ItunesId, artist.ArtistName); + album.Monitored = true; + } + + else + { + album.Monitored = existingAlbum.Monitored; + } + } + + return albums; + } + + public void Execute(RefreshArtistCommand message) + { + _eventAggregator.PublishEvent(new ArtistRefreshStartingEvent(message.Trigger == CommandTrigger.Manual)); + + if (message.ArtistId.HasValue) + { + var artist = _artistService.GetArtist(message.ArtistId.Value); + RefreshArtistInfo(artist); + } + else + { + var allArtists = _artistService.GetAllArtists().OrderBy(c => c.ArtistName).ToList(); + + foreach (var artist in allArtists) + { + if (message.Trigger == CommandTrigger.Manual /*|| _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)*/) + { + try + { + RefreshArtistInfo(artist); + } + catch (Exception e) + { + _logger.Error(e, "Couldn't refresh info for {0}", artist); + } + } + + else + { + try + { + _logger.Info("Skipping refresh of artist: {0}", artist.ArtistName); + //TODO: _diskScanService.Scan(artist); + } + catch (Exception e) + { + _logger.Error(e, "Couldn't rescan artist {0}", artist); + } + } + } + } + } + } +} diff --git a/src/NzbDrone.Core/Music/RefreshTrackService.cs b/src/NzbDrone.Core/Music/RefreshTrackService.cs new file mode 100644 index 000000000..76499545b --- /dev/null +++ b/src/NzbDrone.Core/Music/RefreshTrackService.cs @@ -0,0 +1,125 @@ +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Music.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Music +{ + public interface IRefreshTrackService + { + void RefreshTrackInfo(Artist artist, IEnumerable remoteTracks); + } + + public class RefreshTrackService : IRefreshTrackService + { + private readonly ITrackService _trackService; + private readonly IEventAggregator _eventAggregator; + private readonly Logger _logger; + + public RefreshTrackService(ITrackService trackService, IEventAggregator eventAggregator, Logger logger) + { + _trackService = trackService; + _eventAggregator = eventAggregator; + _logger = logger; + } + + public void RefreshTrackInfo(Artist artist, IEnumerable remoteTracks) + { + _logger.Info("Starting track info refresh for: {0}", artist); + var successCount = 0; + var failCount = 0; + + var existingTracks = _trackService.GetTrackByArtist(artist.ItunesId); + var albums = artist.Albums; + + var updateList = new List(); + var newList = new List(); + var dupeFreeRemoteTracks = remoteTracks.DistinctBy(m => new { m.AlbumId, m.TrackNumber }).ToList(); + + foreach (var track in OrderTracks(artist, dupeFreeRemoteTracks)) + { + try + { + var trackToUpdate = GetTrackToUpdate(artist, track, existingTracks); + + if (trackToUpdate != null) + { + existingTracks.Remove(trackToUpdate); + updateList.Add(trackToUpdate); + } + else + { + trackToUpdate = new Track(); + trackToUpdate.Monitored = GetMonitoredStatus(track, albums); + newList.Add(trackToUpdate); + } + trackToUpdate.ArtistId = artist.ItunesId; // TODO: Ensure LazyLoaded field gets updated. + trackToUpdate.TrackNumber = track.TrackNumber; + trackToUpdate.Title = track.Title ?? "Unknown"; + + // TODO: Implement rest of [RefreshTrackService] fields + + + + successCount++; + } + catch (Exception e) + { + _logger.Fatal(e, "An error has occurred while updating track info for artist {0}. {1}", artist, track); + failCount++; + } + } + + var allTracks = new List(); + allTracks.AddRange(newList); + allTracks.AddRange(updateList); + + // TODO: See if anything needs to be done here + //AdjustMultiEpisodeAirTime(artist, allTracks); + //AdjustDirectToDvdAirDate(artist, allTracks); + + _trackService.DeleteMany(existingTracks); + _trackService.UpdateMany(updateList); + _trackService.InsertMany(newList); + + _eventAggregator.PublishEvent(new TrackInfoRefreshedEvent(artist, newList, updateList)); + + if (failCount != 0) + { + _logger.Info("Finished track refresh for artist: {0}. Successful: {1} - Failed: {2} ", + artist.ArtistName, successCount, failCount); + } + else + { + _logger.Info("Finished track refresh for artist: {0}.", artist); + } + } + + private bool GetMonitoredStatus(Track track, IEnumerable albums) + { + if (track.TrackNumber == 0 /*&& track.AlbumId != 1*/) + { + return false; + } + + var album = albums.SingleOrDefault(c => c.AlbumId == track.AlbumId); + return album == null || album.Monitored; + } + + + private Track GetTrackToUpdate(Artist artist, Track track, List existingTracks) + { + return existingTracks.FirstOrDefault(e => e.AlbumId == track.AlbumId && e.TrackNumber == track.TrackNumber); + } + + private IEnumerable OrderTracks(Artist artist, List tracks) + { + return tracks.OrderBy(e => e.AlbumId).ThenBy(e => e.TrackNumber); + } + } +} + diff --git a/src/NzbDrone.Core/Music/Track.cs b/src/NzbDrone.Core/Music/Track.cs index 65e28231b..fb437193b 100644 --- a/src/NzbDrone.Core/Music/Track.cs +++ b/src/NzbDrone.Core/Music/Track.cs @@ -19,7 +19,8 @@ namespace NzbDrone.Core.Music public int ItunesTrackId { get; set; } public int AlbumId { get; set; } - public LazyLoaded ArtistsId { get; set; } + public LazyLoaded Artist { get; set; } + public int ArtistId { get; set; } public int CompilationId { get; set; } public bool Compilation { get; set; } public int TrackNumber { get; set; } @@ -28,11 +29,10 @@ namespace NzbDrone.Core.Music public bool Explict { get; set; } public string TrackExplicitName { get; set; } public string TrackCensoredName { get; set; } - public string Monitored { get; set; } - public int TrackFileId { get; set; } // JVM: Is this needed with TrackFile reference? + public bool Monitored { get; set; } + public int TrackFileId { get; set; } public DateTime? ReleaseDate { get; set; } - /*public int? SceneEpisodeNumber { get; set; } - public bool UnverifiedSceneNumbering { get; set; } + /* public Ratings Ratings { get; set; } // This might be aplicable as can be pulled from IDv3 tags public List Images { get; set; }*/ diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 678b2e8da..5dcadb8e0 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -851,14 +851,20 @@ + + + + + + diff --git a/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs b/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs index f562800fc..19da3e335 100644 --- a/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs +++ b/src/UI/Series/Index/Overview/SeriesOverviewItemViewTemplate.hbs @@ -8,7 +8,7 @@
@@ -50,6 +50,9 @@
{{> EpisodeProgressPartial }}
+
+ Path {{path}} +
From a09d5d0b6957e1981727a7e40461454a58d18c45 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Sun, 7 May 2017 14:32:13 -0500 Subject: [PATCH 10/11] Switched over to using Spotify API for meta data. This will require deleting DB to start using. --- src/NzbDrone.Api/Music/AlbumResource.cs | 2 +- src/NzbDrone.Api/Music/ArtistModule.cs | 2 +- src/NzbDrone.Api/Music/ArtistResource.cs | 18 +- .../Cloud/SonarrCloudRequestBuilder.cs | 2 +- .../Datastore/Migration/111_setup_music.cs | 8 +- src/NzbDrone.Core/Datastore/TableMapping.cs | 2 +- .../Exceptions/ArtistNotFoundException.cs | 16 +- .../MetadataSource/IProvideArtistInfo.cs | 2 +- .../SkyHook/Resource/AlbumInfoResource.cs | 23 ++ .../SkyHook/Resource/ArtistInfoResource.cs | 20 ++ .../SkyHook/Resource/ArtistResource.cs | 61 +--- .../SkyHook/Resource/ImageResource.cs | 4 + .../MetadataSource/SkyHook/SkyHookProxy.cs | 293 ++++++------------ src/NzbDrone.Core/Music/AddArtistService.cs | 10 +- src/NzbDrone.Core/Music/Album.cs | 2 +- src/NzbDrone.Core/Music/Artist.cs | 6 +- src/NzbDrone.Core/Music/ArtistRepository.cs | 6 +- src/NzbDrone.Core/Music/ArtistService.cs | 8 +- .../Music/RefreshArtistService.cs | 12 +- .../Music/RefreshTrackService.cs | 4 +- src/NzbDrone.Core/Music/Track.cs | 8 +- src/NzbDrone.Core/Music/TrackService.cs | 28 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 2 + src/NzbDrone.Core/Parser/Model/LocalTrack.cs | 4 +- .../Validation/Paths/ArtistExistsValidator.cs | 6 +- src/UI/AddSeries/SearchResultView.js | 4 +- 26 files changed, 229 insertions(+), 324 deletions(-) create mode 100644 src/NzbDrone.Core/MetadataSource/SkyHook/Resource/AlbumInfoResource.cs create mode 100644 src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistInfoResource.cs diff --git a/src/NzbDrone.Api/Music/AlbumResource.cs b/src/NzbDrone.Api/Music/AlbumResource.cs index a6d49d3bd..d3e243c66 100644 --- a/src/NzbDrone.Api/Music/AlbumResource.cs +++ b/src/NzbDrone.Api/Music/AlbumResource.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Api.Music { public class AlbumResource { - public int AlbumId { get; set; } + public string AlbumId { get; set; } public string AlbumName { get; set; } public bool Monitored { get; set; } public int Year { get; set; } diff --git a/src/NzbDrone.Api/Music/ArtistModule.cs b/src/NzbDrone.Api/Music/ArtistModule.cs index c598713d6..d7ef5fed3 100644 --- a/src/NzbDrone.Api/Music/ArtistModule.cs +++ b/src/NzbDrone.Api/Music/ArtistModule.cs @@ -71,7 +71,7 @@ namespace NzbDrone.Api.Music PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace()); - PostValidator.RuleFor(s => s.ItunesId).GreaterThan(0).SetValidator(artistExistsValidator); + PostValidator.RuleFor(s => s.SpotifyId).NotEqual("").SetValidator(artistExistsValidator); PutValidator.RuleFor(s => s.Path).IsValidPath(); } diff --git a/src/NzbDrone.Api/Music/ArtistResource.cs b/src/NzbDrone.Api/Music/ArtistResource.cs index 89bc764a3..71cc14b85 100644 --- a/src/NzbDrone.Api/Music/ArtistResource.cs +++ b/src/NzbDrone.Api/Music/ArtistResource.cs @@ -19,9 +19,7 @@ namespace NzbDrone.Api.Music //View Only public string ArtistName { get; set; } - public int ItunesId { get; set; } - //public List AlternateTitles { get; set; } - //public string SortTitle { get; set; } + public string SpotifyId { get; set; } public string Overview { get; set; } public int AlbumCount @@ -30,7 +28,7 @@ namespace NzbDrone.Api.Music { if (Albums == null) return 0; - return Albums.Where(s => s.AlbumId > 0).Count(); // TODO: CHeck this condition + return Albums.Where(s => s.AlbumId != "").Count(); // TODO: CHeck this condition } } @@ -107,7 +105,7 @@ namespace NzbDrone.Api.Music //FirstAired = resource.FirstAired, //LastInfoSync = resource.LastInfoSync, //SeriesType = resource.SeriesType, - ItunesId = model.ItunesId, + SpotifyId = model.SpotifyId, ArtistSlug = model.ArtistSlug, RootFolderPath = model.RootFolderPath, @@ -151,16 +149,8 @@ namespace NzbDrone.Api.Music ArtistFolder = resource.ArtistFolder, Monitored = resource.Monitored, - - //UseSceneNumbering = resource.UseSceneNumbering, - //Runtime = resource.Runtime, - //TvdbId = resource.TvdbId, - //TvRageId = resource.TvRageId, - //TvMazeId = resource.TvMazeId, - //FirstAired = resource.FirstAired, //LastInfoSync = resource.LastInfoSync, - //SeriesType = resource.SeriesType, - ItunesId = resource.ItunesId, + SpotifyId = resource.SpotifyId, ArtistSlug = resource.ArtistSlug, RootFolderPath = resource.RootFolderPath, diff --git a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs index fa734300a..0349dcf8f 100644 --- a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs +++ b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Common.Cloud Services = new HttpRequestBuilder("http://services.lidarr.tv/v1/") .CreateFactory(); - Search = new HttpRequestBuilder("https://itunes.apple.com/{route}/") + Search = new HttpRequestBuilder("https://api.spotify.com/v1/{route}/") // TODO: maybe use {version} .CreateFactory(); InternalSearch = new HttpRequestBuilder("https://itunes.apple.com/WebObjects/MZStore.woa/wa/{route}") //viewArtist or search diff --git a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs index 52067b23b..0e2da7d1b 100644 --- a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs +++ b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Datastore.Migration protected override void MainDbUpgrade() { Create.TableForModel("Artist") - .WithColumn("ItunesId").AsInt32().Unique() + .WithColumn("SpotifyId").AsString().Nullable().Unique() .WithColumn("ArtistName").AsString().Unique() .WithColumn("ArtistSlug").AsString().Nullable() //.Unique() .WithColumn("CleanTitle").AsString().Nullable() // Do we need this? @@ -37,8 +37,8 @@ namespace NzbDrone.Core.Datastore.Migration ; Create.TableForModel("Albums") - .WithColumn("AlbumId").AsInt32() - .WithColumn("ArtistId").AsInt32() + .WithColumn("AlbumId").AsString().Unique() + .WithColumn("ArtistId").AsInt32() // Should this be artistId (string) .WithColumn("Title").AsString() .WithColumn("Year").AsInt32() .WithColumn("Image").AsInt32() @@ -49,7 +49,7 @@ namespace NzbDrone.Core.Datastore.Migration Create.TableForModel("Tracks") .WithColumn("ItunesTrackId").AsInt32().Unique() - .WithColumn("AlbumId").AsInt32() + .WithColumn("AlbumId").AsString() .WithColumn("ArtistsId").AsString().Nullable() .WithColumn("TrackNumber").AsInt32() .WithColumn("Title").AsString().Nullable() diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 3d2594bef..09163a47c 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -102,7 +102,7 @@ namespace NzbDrone.Core.Datastore .Relationships.AutoMapICollectionOrComplexProperties() .For("Tracks") .LazyLoad(condition: parent => parent.Id > 0, - query: (db, parent) => db.Query().Where(c => c.ItunesTrackId == parent.Id).ToList()) + query: (db, parent) => db.Query().Where(c => c.SpotifyTrackId == parent.Id).ToList()) .HasOne(file => file.Artist, file => file.AlbumId); Mapper.Entity().RegisterModel("Tracks") diff --git a/src/NzbDrone.Core/Exceptions/ArtistNotFoundException.cs b/src/NzbDrone.Core/Exceptions/ArtistNotFoundException.cs index 60a05febd..58c7ea61f 100644 --- a/src/NzbDrone.Core/Exceptions/ArtistNotFoundException.cs +++ b/src/NzbDrone.Core/Exceptions/ArtistNotFoundException.cs @@ -8,24 +8,24 @@ namespace NzbDrone.Core.Exceptions { public class ArtistNotFoundException : NzbDroneException { - public int ItunesId { get; set; } + public string SpotifyId { get; set; } - public ArtistNotFoundException(int itunesId) - : base(string.Format("Series with iTunesId {0} was not found, it may have been removed from iTunes.", itunesId)) + public ArtistNotFoundException(string spotifyId) + : base(string.Format("Artist with SpotifyId {0} was not found, it may have been removed from Spotify.", spotifyId)) { - ItunesId = itunesId; + SpotifyId = spotifyId; } - public ArtistNotFoundException(int itunesId, string message, params object[] args) + public ArtistNotFoundException(string spotifyId, string message, params object[] args) : base(message, args) { - ItunesId = itunesId; + SpotifyId = spotifyId; } - public ArtistNotFoundException(int itunesId, string message) + public ArtistNotFoundException(string spotifyId, string message) : base(message) { - ItunesId = itunesId; + SpotifyId = spotifyId; } } } diff --git a/src/NzbDrone.Core/MetadataSource/IProvideArtistInfo.cs b/src/NzbDrone.Core/MetadataSource/IProvideArtistInfo.cs index 4ae5a3420..76810655a 100644 --- a/src/NzbDrone.Core/MetadataSource/IProvideArtistInfo.cs +++ b/src/NzbDrone.Core/MetadataSource/IProvideArtistInfo.cs @@ -6,6 +6,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook { public interface IProvideArtistInfo { - Tuple> GetArtistInfo(int itunesId); + Tuple> GetArtistInfo(string spotifyId); } } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/AlbumInfoResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/AlbumInfoResource.cs new file mode 100644 index 000000000..d442be7ec --- /dev/null +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/AlbumInfoResource.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MetadataSource.SkyHook.Resource +{ + public class AlbumInfoResource + { + public AlbumInfoResource() + { + + } + public string AlbumType { get; set; } // Might need to make this a separate class + public List Artists { get; set; } // Will always be length of 1 unless a compilation + public string Url { get; set; } // Link to the endpoint api to give full info for this object + public string Id { get; set; } // This is a unique Album ID. Needed for all future API calls + public List Images { get; set; } + public string Name { get; set; } // In case of a takedown, this may be empty + } + + +} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistInfoResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistInfoResource.cs new file mode 100644 index 000000000..35f969001 --- /dev/null +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistInfoResource.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MetadataSource.SkyHook.Resource +{ + public class ArtistInfoResource + { + public ArtistInfoResource() { } + + public List Genres { get; set; } + public string AristUrl { get; set; } + public string Id { get; set; } + public List Images { get; set; } + public string Name { get; set; } + + // We may need external_urls.spotify to external linking... + } +} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs index 5113d5394..2dd663383 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs @@ -5,61 +5,25 @@ using System.Text; namespace NzbDrone.Core.MetadataSource.SkyHook.Resource { - public class StorePlatformDataResource - { - public StorePlatformDataResource() { } - public ArtistInfoResource Artist { get; set; } - //public Lockup lockup { get; set; } - } - public class ArtistInfoResource + public class AristResultResource { - public ArtistInfoResource() { } - public Dictionary Results { get; set; } - - public bool HasArtistBio { get; set; } + public AristResultResource() + { - public string url { get; set; } - public string shortUrl { get; set; } - - public List artistContemporaries { get; set; } - public List genreNames { get; set; } - public bool hasSocialPosts { get; set; } - public string artistBio { get; set; } - public bool isGroup { get; set; } - public string id { get; set; } - public string bornOrFormed { get; set; } - public string name { get; set; } - public string latestAlbumContentId { get; set; } - public string nameRaw { get; set; } + } - //public string kind { get; set; } - //public List gallery { get; set; } - //public List genres { get; set; } - public List artistInfluencers { get; set; } - public List artistFollowers { get; set; } - //public string umcArtistImageUrl { get; set; } + public List Items { get; set; } } - public class AlbumResource - { - public AlbumResource() + public class AlbumResultResource + { + public AlbumResultResource() { } - public string ArtistName { get; set; } - public int ArtistId { get; set; } - public string CollectionName { get; set; } - public int CollectionId { get; set; } - public string PrimaryGenreName { get; set; } - public string ArtworkUrl100 { get; set; } - public string Country { get; set; } - public string CollectionExplicitness { get; set; } - public int TrackCount { get; set; } - public string Copyright { get; set; } - public DateTime ReleaseDate { get; set; } - + public List Items { get; set; } } public class ArtistResource @@ -69,10 +33,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource } - public int ResultCount { get; set; } - public List Results { get; set; } - //public string ArtistName { get; set; } - //public List Albums { get; set; } - public StorePlatformDataResource StorePlatformData { get; set; } + public AristResultResource Artists { get; set; } + public AristResultResource Albums { get; set; } } } diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ImageResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ImageResource.cs index 81a2f578e..518705757 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ImageResource.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ImageResource.cs @@ -3,6 +3,10 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public class ImageResource { public string CoverType { get; set; } + + // Spotify Mapping public string Url { get; set; } + public int Height { get; set; } + public int Width { get; set; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index a35e33835..0bb74c548 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -22,13 +22,11 @@ namespace NzbDrone.Core.MetadataSource.SkyHook private readonly Logger _logger; private readonly IHttpRequestBuilderFactory _requestBuilder; - private readonly IHttpRequestBuilderFactory _internalRequestBuilder; public SkyHookProxy(IHttpClient httpClient, ILidarrCloudRequestBuilder requestBuilder, Logger logger) { _httpClient = httpClient; _requestBuilder = requestBuilder.Search; - _internalRequestBuilder = requestBuilder.InternalSearch; _logger = logger; } @@ -67,153 +65,97 @@ namespace NzbDrone.Core.MetadataSource.SkyHook public List SearchForNewSeries(string title) { - try - { - var lowerTitle = title.ToLowerInvariant(); - Console.WriteLine("Searching for " + lowerTitle); - - //if (lowerTitle.StartsWith("tvdb:") || lowerTitle.StartsWith("tvdbid:")) - //{ - // var slug = lowerTitle.Split(':')[1].Trim(); - - // int tvdbId; - - // if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace) || !int.TryParse(slug, out tvdbId) || tvdbId <= 0) - // { - // return new List(); - // } - - // try - // { - // return new List { GetSeriesInfo(tvdbId).Item1 }; - // } - // catch (SeriesNotFoundException) - // { - // return new List(); - // } - //} - - // Majora: Temporarily, use iTunes to test. - var httpRequest = _requestBuilder.Create() - .AddQueryParam("entity", "album") - .AddQueryParam("term", title.ToLower().Trim()) - .Build(); - - - - Console.WriteLine("httpRequest: ", httpRequest); + // TODO: Remove this API + var tempList = new List(); + var tempSeries = new Series(); + tempSeries.Title = "AFI"; + tempList.Add(tempSeries); + return tempList; + } - var httpResponse = _httpClient.Get>(httpRequest); - //Console.WriteLine("Response: ", httpResponse.GetType()); - //_logger.Info("Response: ", httpResponse.Resource.ResultCount); + public Tuple> GetArtistInfo(string spotifyId) + { - //_logger.Info("HTTP Response: ", httpResponse.Resource.ResultCount); - var tempList = new List(); - var tempSeries = new Series(); - tempSeries.Title = "AFI"; - tempList.Add(tempSeries); - return tempList; - - return httpResponse.Resource.SelectList(MapSeries); - } - catch (HttpException) - { - throw new SkyHookException("Search for '{0}' failed. Unable to communicate with SkyHook.", title); - } - catch (Exception ex) - { - _logger.Warn(ex, ex.Message); - throw new SkyHookException("Search for '{0}' failed. Invalid response received from SkyHook.", title); - } - } + _logger.Debug("Getting Artist with SpotifyId of {0}", spotifyId); - //public Artist GetArtistInfo(int itunesId) - //{ - // Console.WriteLine("[GetArtistInfo] id:" + itunesId); - // //https://itunes.apple.com/lookup?id=909253 - // //var httpRequest = _requestBuilder.Create() - // // .SetSegment("route", "lookup") - // // .AddQueryParam("id", itunesId.ToString()) - // // .Build(); - - // // TODO: Add special header, add Overview to Artist model - // var httpRequest = _requestBuilder.Create() - // .SetSegment("route", "viewArtist") - // .AddQueryParam("id", itunesId.ToString()) - // .Build(); - // httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); - - // httpRequest.AllowAutoRedirect = true; - // httpRequest.SuppressHttpError = true; - - // var httpResponse = _httpClient.Get(httpRequest); - - // if (httpResponse.HasHttpError) - // { - // if (httpResponse.StatusCode == HttpStatusCode.NotFound) - // { - // throw new ArtistNotFoundException(itunesId); - // } - // else - // { - // throw new HttpException(httpRequest, httpResponse); - // } - // } - - // Console.WriteLine("GetArtistInfo, GetArtistInfo"); - // return MapArtists(httpResponse.Resource)[0]; - //} + ///v1/albums/{id} + // - public Tuple> GetArtistInfo(int itunesId) - { - // TODO: [GetArtistInfo]: This needs to return a set of tracks from iTunes. - // This call is expected to return information about an artist and the tracks that make up said artist. - // To do this, we need 2-3 API calls. 1st is to gather information about the artist and the albums the artist has. This is https://itunes.apple.com/search?entity=album&id=itunesId - // Next call is to populate the overview field and calls the internal API - // Finally, we need to, for each album, get all tracks, which means calling this N times: https://itunes.apple.com/search?entity=musicTrack&term=artistName (id will not work) - _logger.Debug("Getting Artist with iTunesID of {0}", itunesId); - var httpRequest1 = _requestBuilder.Create() - .SetSegment("route", "lookup") - .AddQueryParam("id", itunesId.ToString()) + // We need to perform a direct lookup of the artist + var httpRequest = _requestBuilder.Create() + .SetSegment("route", "artists/" + spotifyId) + //.SetSegment("route", "search") + //.AddQueryParam("type", "artist,album") + //.AddQueryParam("q", spotifyId.ToString()) .Build(); - var httpRequest2 = _internalRequestBuilder.Create() - .SetSegment("route", "viewArtist") - .AddQueryParam("id", itunesId.ToString()) - .Build(); - httpRequest2.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); - httpRequest2.Headers.ContentType = "application/json"; + - httpRequest1.AllowAutoRedirect = true; - httpRequest1.SuppressHttpError = true; + httpRequest.AllowAutoRedirect = true; + httpRequest.SuppressHttpError = true; - var httpResponse = _httpClient.Get(httpRequest1); + var httpResponse = _httpClient.Get(httpRequest); if (httpResponse.HasHttpError) { if (httpResponse.StatusCode == HttpStatusCode.NotFound) { - throw new ArtistNotFoundException(itunesId); + throw new ArtistNotFoundException(spotifyId); } else { - throw new HttpException(httpRequest1, httpResponse); + throw new HttpException(httpRequest, httpResponse); } } - List artists = MapArtists(httpResponse.Resource); - List newArtists = new List(artists.Count); - int count = 0; - foreach (var artist in artists) + Artist artist = new Artist(); + artist.ArtistName = httpResponse.Resource.Name; + artist.SpotifyId = httpResponse.Resource.Id; + artist.Genres = httpResponse.Resource.Genres; + //Artist artist = MapArtists(httpResponse.Resource)[0]; + + + artist = MapAlbums(artist); + + + // TODO: implement tracks api call + return new Tuple>(artist, new List()); + } + + private Artist MapAlbums(Artist artist) + { + + // Find all albums for the artist and all tracks for said album + ///v1/artists/{id}/albums + var httpRequest = _requestBuilder.Create() + .SetSegment("route", "artists/" + artist.SpotifyId + "/albums") + .Build(); + httpRequest.AllowAutoRedirect = true; + httpRequest.SuppressHttpError = true; + + var httpResponse = _httpClient.Get(httpRequest); + + if (httpResponse.HasHttpError) + { + throw new HttpException(httpRequest, httpResponse); + } + + List albums = new List(); + foreach(var albumResource in httpResponse.Resource.Items) { - newArtists.Add(AddOverview(artist)); - count++; + Album album = new Album(); + album.AlbumId = albumResource.Id; + album.Title = albumResource.Name; + album.ArtworkUrl = albumResource.Images[0].Url; + albums.Add(album); } - // I don't know how we are getting tracks from iTunes yet. - return new Tuple>(newArtists[0], new List()); + // TODO: We now need to get all tracks for each album + + artist.Albums = albums; + return artist; } public List SearchForNewArtist(string title) @@ -227,16 +169,14 @@ namespace NzbDrone.Core.MetadataSource.SkyHook { var slug = lowerTitle.Split(':')[1].Trim(); - int itunesId; - - if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace) || !int.TryParse(slug, out itunesId) || itunesId <= 0) + if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace)) { return new List(); } try { - return new List { GetArtistInfo(itunesId).Item1 }; + return new List { GetArtistInfo(slug).Item1 }; } catch (ArtistNotFoundException) { @@ -246,8 +186,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook var httpRequest = _requestBuilder.Create() .SetSegment("route", "search") - .AddQueryParam("entity", "album") - .AddQueryParam("term", title.ToLower().Trim()) + .AddQueryParam("type", "artist,album") + .AddQueryParam("q", title.ToLower().Trim()) .Build(); @@ -256,16 +196,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook List artists = MapArtists(httpResponse.Resource); - List newArtists = new List(artists.Count); - int count = 0; - foreach (var artist in artists) - { - newArtists.Add(AddOverview(artist)); - count++; - } - - return newArtists; + return artists; } catch (HttpException) { @@ -278,77 +210,52 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } } - private Artist AddOverview(Artist artist) - { - var httpRequest = _internalRequestBuilder.Create() - .SetSegment("route", "viewArtist") - .AddQueryParam("id", artist.ItunesId.ToString()) - .Build(); - httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3"); - httpRequest.Headers.ContentType = "application/json"; - var httpResponse = _httpClient.Get(httpRequest); - - if (!httpResponse.HasHttpError) - { - artist.Overview = httpResponse.Resource.StorePlatformData.Artist.Results[artist.ItunesId].artistBio; - } - - return artist; - } - private Artist MapArtistInfo(ArtistInfoResource resource) { // This expects ArtistInfoResource, thus just need to populate one artist Artist artist = new Artist(); - artist.Overview = resource.artistBio; - artist.ArtistName = resource.name; - foreach(var genre in resource.genreNames) - { - artist.Genres.Add(genre); - } + //artist.Overview = resource.artistBio; + //artist.ArtistName = resource.name; + //foreach(var genre in resource.genreNames) + //{ + // artist.Genres.Add(genre); + //} return artist; } private List MapArtists(ArtistResource resource) { - Album tempAlbum; + + List artists = new List(); - foreach (var album in resource.Results) + foreach(var artistResource in resource.Artists.Items) { - int index = artists.FindIndex(a => a.ItunesId == album.ArtistId); - tempAlbum = MapAlbum(album); + Artist artist = new Artist(); + artist.ArtistName = artistResource.Name; + artist.SpotifyId = artistResource.Id; + artist.Genres = artistResource.Genres; + //artist.ArtistSlug = a//TODO implement artistSlug mapping; + artists.Add(artist); + } - if (index >= 0) - { - artists[index].Albums.Add(tempAlbum); - } - else - { - Artist tempArtist = new Artist(); - tempArtist.ItunesId = album.ArtistId; - tempArtist.ArtistName = album.ArtistName; - tempArtist.Genres.Add(album.PrimaryGenreName); - tempArtist.Albums.Add(tempAlbum); - artists.Add(tempArtist); - } + // Maybe? Get all the albums for said artist - } return artists; } - private Album MapAlbum(AlbumResource albumQuery) - { - Album album = new Album(); - - album.AlbumId = albumQuery.CollectionId; - album.Title = albumQuery.CollectionName; - album.Year = albumQuery.ReleaseDate.Year; - album.ArtworkUrl = albumQuery.ArtworkUrl100; - album.Explicitness = albumQuery.CollectionExplicitness; - return album; - } + //private Album MapAlbum(AlbumResource albumQuery) + //{ + // Album album = new Album(); + + // album.AlbumId = albumQuery.CollectionId; + // album.Title = albumQuery.CollectionName; + // album.Year = albumQuery.ReleaseDate.Year; + // album.ArtworkUrl = albumQuery.ArtworkUrl100; + // album.Explicitness = albumQuery.CollectionExplicitness; + // return album; + //} private static Series MapSeries(ShowResource show) { diff --git a/src/NzbDrone.Core/Music/AddArtistService.cs b/src/NzbDrone.Core/Music/AddArtistService.cs index 3ca636d0b..07f995f69 100644 --- a/src/NzbDrone.Core/Music/AddArtistService.cs +++ b/src/NzbDrone.Core/Music/AddArtistService.cs @@ -48,7 +48,7 @@ namespace NzbDrone.Core.Music if (string.IsNullOrWhiteSpace(newArtist.Path)) { - var folderName = newArtist.ArtistName;// _fileNameBuilder.GetArtistFolder(newArtist); + var folderName = newArtist.ArtistName;// TODO: _fileNameBuilder.GetArtistFolder(newArtist); newArtist.Path = Path.Combine(newArtist.RootFolderPath, folderName); } @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Music throw new ValidationException(validationResult.Errors); } - _logger.Info("Adding Series {0} Path: [{1}]", newArtist, newArtist.Path); + _logger.Info("Adding Artist {0} Path: [{1}]", newArtist, newArtist.Path); _artistService.AddArtist(newArtist); return newArtist; @@ -75,15 +75,15 @@ namespace NzbDrone.Core.Music try { - tuple = _artistInfo.GetArtistInfo(newArtist.ItunesId); + tuple = _artistInfo.GetArtistInfo(newArtist.SpotifyId); } catch (SeriesNotFoundException) { - _logger.Error("iTunesId {1} was not found, it may have been removed from iTunes.", newArtist.ItunesId); + _logger.Error("SpotifyId {1} was not found, it may have been removed from Spotify.", newArtist.SpotifyId); throw new ValidationException(new List { - new ValidationFailure("iTunesId", "An artist with this ID was not found", newArtist.ItunesId) + new ValidationFailure("SpotifyId", "An artist with this ID was not found", newArtist.SpotifyId) }); } diff --git a/src/NzbDrone.Core/Music/Album.cs b/src/NzbDrone.Core/Music/Album.cs index c0c7fc19e..43aaecb14 100644 --- a/src/NzbDrone.Core/Music/Album.cs +++ b/src/NzbDrone.Core/Music/Album.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.Music Images = new List(); } - public int AlbumId { get; set; } + public string AlbumId { get; set; } public string Title { get; set; } // NOTE: This should be CollectionName in API public int Year { get; set; } public int TrackCount { get; set; } diff --git a/src/NzbDrone.Core/Music/Artist.cs b/src/NzbDrone.Core/Music/Artist.cs index ef89b2998..000aaf928 100644 --- a/src/NzbDrone.Core/Music/Artist.cs +++ b/src/NzbDrone.Core/Music/Artist.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Core.Music } - public int ItunesId { get; set; } + public string SpotifyId { get; set; } public string ArtistName { get; set; } public string ArtistSlug { get; set; } public string CleanTitle { get; set; } @@ -47,13 +47,13 @@ namespace NzbDrone.Core.Music public override string ToString() { - return string.Format("[{0}][{1}]", ItunesId, ArtistName.NullSafe()); + return string.Format("[{0}][{1}]", SpotifyId, ArtistName.NullSafe()); } public void ApplyChanges(Artist otherArtist) { - ItunesId = otherArtist.ItunesId; + SpotifyId = otherArtist.SpotifyId; ArtistName = otherArtist.ArtistName; ArtistSlug = otherArtist.ArtistSlug; CleanTitle = otherArtist.CleanTitle; diff --git a/src/NzbDrone.Core/Music/ArtistRepository.cs b/src/NzbDrone.Core/Music/ArtistRepository.cs index f9e7f9da4..0da04ad0d 100644 --- a/src/NzbDrone.Core/Music/ArtistRepository.cs +++ b/src/NzbDrone.Core/Music/ArtistRepository.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.Music { bool ArtistPathExists(string path); Artist FindByName(string cleanTitle); - Artist FindByItunesId(int iTunesId); + Artist FindById(string spotifyId); } public class ArtistRepository : BasicRepository, IArtistRepository @@ -24,9 +24,9 @@ namespace NzbDrone.Core.Music return Query.Where(c => c.Path == path).Any(); } - public Artist FindByItunesId(int iTunesId) + public Artist FindById(string spotifyId) { - return Query.Where(s => s.ItunesId == iTunesId).SingleOrDefault(); + return Query.Where(s => s.SpotifyId == spotifyId).SingleOrDefault(); } public Artist FindByName(string cleanName) diff --git a/src/NzbDrone.Core/Music/ArtistService.cs b/src/NzbDrone.Core/Music/ArtistService.cs index 1397fad5d..bedf41f74 100644 --- a/src/NzbDrone.Core/Music/ArtistService.cs +++ b/src/NzbDrone.Core/Music/ArtistService.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Music Artist GetArtist(int artistId); List GetArtists(IEnumerable artistIds); Artist AddArtist(Artist newArtist); - Artist FindByItunesId(int itunesId); + Artist FindById(string spotifyId); Artist FindByName(string title); Artist FindByTitleInexact(string title); void DeleteArtist(int artistId, bool deleteFiles); @@ -69,9 +69,9 @@ namespace NzbDrone.Core.Music _eventAggregator.PublishEvent(new ArtistDeletedEvent(artist, deleteFiles)); } - public Artist FindByItunesId(int itunesId) + public Artist FindById(string spotifyId) { - return _artistRepository.FindByItunesId(itunesId); + return _artistRepository.FindById(spotifyId); } public Artist FindByName(string title) @@ -114,7 +114,7 @@ namespace NzbDrone.Core.Music if (storedAlbum != null && album.Monitored != storedAlbum.Monitored) { - _trackService.SetTrackMonitoredByAlbum(artist.Id, album.AlbumId, album.Monitored); + _trackService.SetTrackMonitoredByAlbum(artist.SpotifyId, album.AlbumId, album.Monitored); } } diff --git a/src/NzbDrone.Core/Music/RefreshArtistService.cs b/src/NzbDrone.Core/Music/RefreshArtistService.cs index 401753ef8..af0717437 100644 --- a/src/NzbDrone.Core/Music/RefreshArtistService.cs +++ b/src/NzbDrone.Core/Music/RefreshArtistService.cs @@ -52,20 +52,20 @@ namespace NzbDrone.Core.Music try { - tuple = _artistInfo.GetArtistInfo(artist.ItunesId); + tuple = _artistInfo.GetArtistInfo(artist.SpotifyId); } catch (ArtistNotFoundException) { - _logger.Error("Artist '{0}' (itunesid {1}) was not found, it may have been removed from iTunes.", artist.ArtistName, artist.ItunesId); + _logger.Error("Artist '{0}' (SpotifyId {1}) was not found, it may have been removed from Spotify.", artist.ArtistName, artist.SpotifyId); return; } var artistInfo = tuple.Item1; - if (artist.ItunesId != artistInfo.ItunesId) + if (artist.SpotifyId != artistInfo.SpotifyId) { - _logger.Warn("Artist '{0}' (itunes {1}) was replaced with '{2}' (itunes {3}), because the original was a duplicate.", artist.ArtistName, artist.ItunesId, artistInfo.ArtistName, artistInfo.ItunesId); - artist.ItunesId = artistInfo.ItunesId; + _logger.Warn("Artist '{0}' (SpotifyId {1}) was replaced with '{2}' (SpotifyId {3}), because the original was a duplicate.", artist.ArtistName, artist.SpotifyId, artistInfo.ArtistName, artistInfo.SpotifyId); + artist.SpotifyId = artistInfo.SpotifyId; } artist.ArtistName = artistInfo.ArtistName; @@ -114,7 +114,7 @@ namespace NzbDrone.Core.Music // continue; //} - _logger.Debug("New album ({0}) for artist: [{1}] {2}, setting monitored to true", album.Title, artist.ItunesId, artist.ArtistName); + _logger.Debug("New album ({0}) for artist: [{1}] {2}, setting monitored to true", album.Title, artist.SpotifyId, artist.ArtistName); album.Monitored = true; } diff --git a/src/NzbDrone.Core/Music/RefreshTrackService.cs b/src/NzbDrone.Core/Music/RefreshTrackService.cs index 76499545b..bee0e8a5d 100644 --- a/src/NzbDrone.Core/Music/RefreshTrackService.cs +++ b/src/NzbDrone.Core/Music/RefreshTrackService.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Music var successCount = 0; var failCount = 0; - var existingTracks = _trackService.GetTrackByArtist(artist.ItunesId); + var existingTracks = _trackService.GetTrackByArtist(artist.SpotifyId); var albums = artist.Albums; var updateList = new List(); @@ -57,7 +57,7 @@ namespace NzbDrone.Core.Music trackToUpdate.Monitored = GetMonitoredStatus(track, albums); newList.Add(trackToUpdate); } - trackToUpdate.ArtistId = artist.ItunesId; // TODO: Ensure LazyLoaded field gets updated. + trackToUpdate.ArtistId = artist.SpotifyId; // TODO: Ensure LazyLoaded field gets updated. trackToUpdate.TrackNumber = track.TrackNumber; trackToUpdate.Title = track.Title ?? "Unknown"; diff --git a/src/NzbDrone.Core/Music/Track.cs b/src/NzbDrone.Core/Music/Track.cs index fb437193b..f1f563306 100644 --- a/src/NzbDrone.Core/Music/Track.cs +++ b/src/NzbDrone.Core/Music/Track.cs @@ -17,10 +17,10 @@ namespace NzbDrone.Core.Music public const string RELEASE_DATE_FORMAT = "yyyy-MM-dd"; - public int ItunesTrackId { get; set; } - public int AlbumId { get; set; } + public int SpotifyTrackId { get; set; } + public string AlbumId { get; set; } public LazyLoaded Artist { get; set; } - public int ArtistId { get; set; } + public string ArtistId { get; set; } public int CompilationId { get; set; } public bool Compilation { get; set; } public int TrackNumber { get; set; } @@ -46,7 +46,7 @@ namespace NzbDrone.Core.Music public override string ToString() { - return string.Format("[{0}]{1}", ItunesTrackId, Title.NullSafe()); + return string.Format("[{0}]{1}", SpotifyTrackId, Title.NullSafe()); } } } diff --git a/src/NzbDrone.Core/Music/TrackService.cs b/src/NzbDrone.Core/Music/TrackService.cs index 91bdeb5f7..b8bbbafa7 100644 --- a/src/NzbDrone.Core/Music/TrackService.cs +++ b/src/NzbDrone.Core/Music/TrackService.cs @@ -10,12 +10,12 @@ namespace NzbDrone.Core.Music { Track GetTrack(int id); List GetTracks(IEnumerable ids); - Track FindTrack(int artistId, int albumId, int trackNumber); - Track FindTrackByTitle(int artistId, int albumId, string releaseTitle); - List GetTrackByArtist(int artistId); - List GetTracksByAblum(int artistId, int albumId); - List GetTracksByAblumTitle(int artistId, string albumTitle); - List TracksWithFiles(int artistId); + Track FindTrack(string artistId, string albumId, int trackNumber); + Track FindTrackByTitle(string artistId, string albumId, string releaseTitle); + List GetTrackByArtist(string artistId); + List GetTracksByAlbum(string artistId, string albumId); + List GetTracksByAlbumTitle(string artistId, string albumTitle); + List TracksWithFiles(string artistId); PagingSpec TracksWithoutFiles(PagingSpec pagingSpec); List GeTracksByFileId(int trackFileId); void UpdateTrack(Track track); @@ -24,7 +24,7 @@ namespace NzbDrone.Core.Music void InsertMany(List tracks); void UpdateMany(List tracks); void DeleteMany(List tracks); - void SetTrackMonitoredByAlbum(int artistId, int albumId, bool monitored); + void SetTrackMonitoredByAlbum(string artistId, string albumId, bool monitored); } public class TrackService : ITrackService @@ -34,12 +34,12 @@ namespace NzbDrone.Core.Music throw new NotImplementedException(); } - public Track FindTrack(int artistId, int albumId, int trackNumber) + public Track FindTrack(string artistId, string albumId, int trackNumber) { throw new NotImplementedException(); } - public Track FindTrackByTitle(int artistId, int albumId, string releaseTitle) + public Track FindTrackByTitle(string artistId, string albumId, string releaseTitle) { throw new NotImplementedException(); } @@ -54,7 +54,7 @@ namespace NzbDrone.Core.Music throw new NotImplementedException(); } - public List GetTrackByArtist(int artistId) + public List GetTrackByArtist(string artistId) { throw new NotImplementedException(); } @@ -64,12 +64,12 @@ namespace NzbDrone.Core.Music throw new NotImplementedException(); } - public List GetTracksByAblum(int artistId, int albumId) + public List GetTracksByAlbum(string artistId, string albumId) { throw new NotImplementedException(); } - public List GetTracksByAblumTitle(int artistId, string albumTitle) + public List GetTracksByAlbumTitle(string artistId, string albumTitle) { throw new NotImplementedException(); } @@ -84,12 +84,12 @@ namespace NzbDrone.Core.Music throw new NotImplementedException(); } - public void SetTrackMonitoredByAlbum(int artistId, int albumId, bool monitored) + public void SetTrackMonitoredByAlbum(string artistId, string albumId, bool monitored) { throw new NotImplementedException(); } - public List TracksWithFiles(int artistId) + public List TracksWithFiles(string artistId) { throw new NotImplementedException(); } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 5dcadb8e0..7f5a97c01 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -815,6 +815,8 @@ + + diff --git a/src/NzbDrone.Core/Parser/Model/LocalTrack.cs b/src/NzbDrone.Core/Parser/Model/LocalTrack.cs index e3577527d..2f8b35588 100644 --- a/src/NzbDrone.Core/Parser/Model/LocalTrack.cs +++ b/src/NzbDrone.Core/Parser/Model/LocalTrack.cs @@ -24,7 +24,7 @@ namespace NzbDrone.Core.Parser.Model public MediaInfoModel MediaInfo { get; set; } public bool ExistingFile { get; set; } - public int Album + public string Album { get { @@ -32,7 +32,7 @@ namespace NzbDrone.Core.Parser.Model } } - public bool IsSpecial => Album == 0; + public bool IsSpecial => Album != ""; public override string ToString() { diff --git a/src/NzbDrone.Core/Validation/Paths/ArtistExistsValidator.cs b/src/NzbDrone.Core/Validation/Paths/ArtistExistsValidator.cs index 4a56bd072..3260895c5 100644 --- a/src/NzbDrone.Core/Validation/Paths/ArtistExistsValidator.cs +++ b/src/NzbDrone.Core/Validation/Paths/ArtistExistsValidator.cs @@ -12,7 +12,7 @@ namespace NzbDrone.Core.Validation.Paths private readonly IArtistService _artistService; public ArtistExistsValidator(IArtistService artistService) - : base("This artist has already been added") + : base("This artist has already been added.") { _artistService = artistService; } @@ -21,9 +21,7 @@ namespace NzbDrone.Core.Validation.Paths { if (context.PropertyValue == null) return true; - var itunesId = Convert.ToInt32(context.PropertyValue.ToString()); - - return (!_artistService.GetAllArtists().Exists(s => s.ItunesId == itunesId)); + return (!_artistService.GetAllArtists().Exists(s => s.SpotifyId == context.PropertyValue.ToString())); } } } diff --git a/src/UI/AddSeries/SearchResultView.js b/src/UI/AddSeries/SearchResultView.js index aaef92a1f..8c4c70e7e 100644 --- a/src/UI/AddSeries/SearchResultView.js +++ b/src/UI/AddSeries/SearchResultView.js @@ -223,12 +223,12 @@ var view = Marionette.ItemView.extend({ self.close(); Messenger.show({ - message : 'Added: ' + self.model.get('title'), + message : 'Added: ' + self.model.get('artistName'), actions : { goToSeries : { label : 'Go to Artist', action : function() { - Backbone.history.navigate('/artist/' + self.model.get('titleSlug'), { trigger : true }); + Backbone.history.navigate('/artist/' + self.model.get('artistSlug'), { trigger : true }); } } }, From acb7d33d09307986bca94c1ec79da5e4b435f758 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Sun, 7 May 2017 14:55:49 -0500 Subject: [PATCH 11/11] Implemented track lookup into skyhook. --- .../Cloud/SonarrCloudRequestBuilder.cs | 3 +- .../SkyHook/Resource/ArtistResource.cs | 9 ++++ .../SkyHook/Resource/TrackInfoResource.cs | 25 +++++++++++ .../MetadataSource/SkyHook/SkyHookProxy.cs | 41 ++++++++++++++++++- src/NzbDrone.Core/Music/Album.cs | 1 + src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + 6 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TrackInfoResource.cs diff --git a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs index 0349dcf8f..9efdda47c 100644 --- a/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs +++ b/src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs @@ -17,7 +17,8 @@ namespace NzbDrone.Common.Cloud Services = new HttpRequestBuilder("http://services.lidarr.tv/v1/") .CreateFactory(); - Search = new HttpRequestBuilder("https://api.spotify.com/v1/{route}/") // TODO: maybe use {version} + Search = new HttpRequestBuilder("https://api.spotify.com/{version}/{route}/") // TODO: maybe use {version} + .SetSegment("version", "v1") .CreateFactory(); InternalSearch = new HttpRequestBuilder("https://itunes.apple.com/WebObjects/MZStore.woa/wa/{route}") //viewArtist or search diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs index 2dd663383..8d98d6890 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/ArtistResource.cs @@ -26,6 +26,15 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource public List Items { get; set; } } + public class TrackResultResource + { + public TrackResultResource() + { + + } + + public List Items { get; set; } + } public class ArtistResource { public ArtistResource() diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TrackInfoResource.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TrackInfoResource.cs new file mode 100644 index 000000000..2f905637d --- /dev/null +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/Resource/TrackInfoResource.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MetadataSource.SkyHook.Resource +{ + public class TrackInfoResource + { + public TrackInfoResource() + { + + } + + public int DiscNumber { get; set; } + public int DurationMs { get; set; } + public string Href { get; set; } + public string Id { get; set; } + public string Name { get; set; } + public int TrackNumber { get; set; } + public bool Explicit { get; set; } + public List Artists { get; set; } + + } +} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 0bb74c548..bf47348b1 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -114,7 +114,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook artist.ArtistName = httpResponse.Resource.Name; artist.SpotifyId = httpResponse.Resource.Id; artist.Genres = httpResponse.Resource.Genres; - //Artist artist = MapArtists(httpResponse.Resource)[0]; artist = MapAlbums(artist); @@ -149,6 +148,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook album.AlbumId = albumResource.Id; album.Title = albumResource.Name; album.ArtworkUrl = albumResource.Images[0].Url; + album.Tracks = MapTracksToAlbum(album); albums.Add(album); } @@ -157,7 +157,44 @@ namespace NzbDrone.Core.MetadataSource.SkyHook artist.Albums = albums; return artist; } - + + private List MapTracksToAlbum(Album album) + { + var httpRequest = _requestBuilder.Create() + .SetSegment("route", "albums/" + album.AlbumId + "/tracks") + .Build(); + + httpRequest.AllowAutoRedirect = true; + httpRequest.SuppressHttpError = true; + + var httpResponse = _httpClient.Get(httpRequest); + + if (httpResponse.HasHttpError) + { + throw new HttpException(httpRequest, httpResponse); + } + + List tracks = new List(); + foreach(var trackResource in httpResponse.Resource.Items) + { + Track track = new Track(); + track.AlbumId = album.AlbumId; + //track.Album = album; // This will cause infinite loop when trying to serialize. + // TODO: Implement more track mapping + //track.Artist = trackResource.Artists + //track.ArtistId = album. + track.Explict = trackResource.Explicit; + track.Compilation = trackResource.Artists.Count > 1; + track.TrackNumber = trackResource.TrackNumber; + track.TrackExplicitName = trackResource.Name; + track.TrackCensoredName = trackResource.Name; + tracks.Add(track); + } + + return tracks; + } + + public List SearchForNewArtist(string title) { try diff --git a/src/NzbDrone.Core/Music/Album.cs b/src/NzbDrone.Core/Music/Album.cs index 43aaecb14..6d18a766b 100644 --- a/src/NzbDrone.Core/Music/Album.cs +++ b/src/NzbDrone.Core/Music/Album.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.Music public string Title { get; set; } // NOTE: This should be CollectionName in API public int Year { get; set; } public int TrackCount { get; set; } + public List Tracks { get; set; } public int DiscCount { get; set; } public bool Monitored { get; set; } public List Images { get; set; } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 7f5a97c01..d263b8013 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -824,6 +824,7 @@ +