Merge branch 'develop' of https://github.com/mattman86/Lidarr into runraid

This commit is contained in:
2017-05-07 19:07:44 -07:00
commit 67714561ea
39 changed files with 1270 additions and 354 deletions

View file

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

View file

@ -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();
}

View file

@ -19,9 +19,7 @@ namespace NzbDrone.Api.Music
//View Only
public string ArtistName { get; set; }
public int ItunesId { get; set; }
//public List<AlternateTitleResource> 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,

View file

@ -17,7 +17,8 @@ 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/{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

View file

@ -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,12 +49,13 @@ 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()
.WithColumn("Ignored").AsBoolean().Nullable()
.WithColumn("Explict").AsBoolean()
.WithColumn("Monitored").AsBoolean()
.WithColumn("TrackExplicitName").AsString().Nullable()
.WithColumn("TrackCensoredName").AsString().Nullable()
.WithColumn("TrackFileId").AsInt32().Nullable()

View file

@ -102,7 +102,7 @@ namespace NzbDrone.Core.Datastore
.Relationships.AutoMapICollectionOrComplexProperties()
.For("Tracks")
.LazyLoad(condition: parent => parent.Id > 0,
query: (db, parent) => db.Query<Track>().Where(c => c.ItunesTrackId == parent.Id).ToList())
query: (db, parent) => db.Query<Track>().Where(c => c.SpotifyTrackId == parent.Id).ToList())
.HasOne(file => file.Artist, file => file.AlbumId);
Mapper.Entity<Track>().RegisterModel("Tracks")

View file

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

View file

@ -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},

View file

@ -6,6 +6,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
{
public interface IProvideArtistInfo
{
Tuple<Artist, List<Track>> GetArtistInfo(int itunesId);
Tuple<Artist, List<Track>> GetArtistInfo(string spotifyId);
}
}

View file

@ -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<ArtistInfoResource> 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<ImageResource> Images { get; set; }
public string Name { get; set; } // In case of a takedown, this may be empty
}
}

View file

@ -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<string> Genres { get; set; }
public string AristUrl { get; set; }
public string Id { get; set; }
public List<ImageResource> Images { get; set; }
public string Name { get; set; }
// We may need external_urls.spotify to external linking...
}
}

View file

@ -5,63 +5,36 @@ using System.Text;
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{
public class StorePlatformDataResource
public class AristResultResource
{
public StorePlatformDataResource() { }
public ArtistInfoResource Artist { get; set; }
//public Lockup lockup { get; set; }
}
public class ArtistInfoResource
{
public ArtistInfoResource() { }
public Dictionary<int, ArtistInfoResource> Results { get; set; }
public bool HasArtistBio { get; set; }
public string url { get; set; }
public string shortUrl { get; set; }
public List<string> artistContemporaries { get; set; }
public List<string> 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> gallery { get; set; }
//public List<Genre> genres { get; set; }
public List<object> artistInfluencers { get; set; }
public List<object> artistFollowers { get; set; }
//public string umcArtistImageUrl { get; set; }
}
public class AlbumResource
{
public AlbumResource()
public AristResultResource()
{
}
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<ArtistInfoResource> Items { get; set; }
}
public class AlbumResultResource
{
public AlbumResultResource()
{
}
public List<AlbumInfoResource> Items { get; set; }
}
public class TrackResultResource
{
public TrackResultResource()
{
}
public List<TrackInfoResource> Items { get; set; }
}
public class ArtistResource
{
public ArtistResource()
@ -69,10 +42,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
}
public int ResultCount { get; set; }
public List<AlbumResource> Results { get; set; }
//public string ArtistName { get; set; }
//public List<AlbumResource> Albums { get; set; }
public StorePlatformDataResource StorePlatformData { get; set; }
public AristResultResource Artists { get; set; }
public AristResultResource Albums { get; set; }
}
}

View file

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

View file

@ -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<ArtistInfoResource> Artists { get; set; }
}
}

View file

@ -22,16 +22,16 @@ 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;
}
public Tuple<Series, List<Episode>> GetSeriesInfo(int tvdbSeriesId)
{
Console.WriteLine("[GetSeriesInfo] id:" + tvdbSeriesId);
@ -65,150 +65,136 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
public List<Series> 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<Series>();
// }
// try
// {
// return new List<Series> { GetSeriesInfo(tvdbId).Item1 };
// }
// catch (SeriesNotFoundException)
// {
// return new List<Series>();
// }
//}
// Majora: Temporarily, use iTunes to test.
var httpRequest = _requestBuilder.Create()
.AddQueryParam("entity", "album")
.AddQueryParam("term", title.ToLower().Trim())
.Build();
Console.WriteLine("httpRequest: ", httpRequest);
var httpResponse = _httpClient.Get<List<ShowResource>>(httpRequest);
//Console.WriteLine("Response: ", httpResponse.GetType());
//_logger.Info("Response: ", httpResponse.Resource.ResultCount);
//_logger.Info("HTTP Response: ", httpResponse.Resource.ResultCount);
var tempList = new List<Series>();
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);
}
// TODO: Remove this API
var tempList = new List<Series>();
var tempSeries = new Series();
tempSeries.Title = "AFI";
tempList.Add(tempSeries);
return tempList;
}
//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<ArtistResource>(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<Artist, List<Track>> GetArtistInfo(int itunesId)
public Tuple<Artist, List<Track>> GetArtistInfo(string spotifyId)
{
_logger.Debug("Getting Artist with iTunesID of {0}", itunesId);
var httpRequest1 = _requestBuilder.Create()
.SetSegment("route", "lookup")
.AddQueryParam("id", itunesId.ToString())
_logger.Debug("Getting Artist with SpotifyId of {0}", spotifyId);
///v1/albums/{id}
//
// 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<ArtistResource>(httpRequest1);
var httpResponse = _httpClient.Get<ArtistInfoResource>(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<Artist> artists = MapArtists(httpResponse.Resource);
List<Artist> newArtists = new List<Artist>(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 = MapAlbums(artist);
// TODO: implement tracks api call
return new Tuple<Artist, List<Track>>(artist, new List<Track>());
}
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<AlbumResultResource>(httpRequest);
if (httpResponse.HasHttpError)
{
newArtists.Add(AddOverview(artist));
count++;
throw new HttpException(httpRequest, httpResponse);
}
// I don't know how we are getting tracks from iTunes yet.
return new Tuple<Artist, List<Track>>(newArtists[0], new List<Track>());
List<Album> albums = new List<Album>();
foreach(var albumResource in httpResponse.Resource.Items)
{
Album album = new Album();
album.AlbumId = albumResource.Id;
album.Title = albumResource.Name;
album.ArtworkUrl = albumResource.Images[0].Url;
album.Tracks = MapTracksToAlbum(album);
albums.Add(album);
}
// TODO: We now need to get all tracks for each album
artist.Albums = albums;
return artist;
}
private List<Track> MapTracksToAlbum(Album album)
{
var httpRequest = _requestBuilder.Create()
.SetSegment("route", "albums/" + album.AlbumId + "/tracks")
.Build();
httpRequest.AllowAutoRedirect = true;
httpRequest.SuppressHttpError = true;
var httpResponse = _httpClient.Get<TrackResultResource>(httpRequest);
if (httpResponse.HasHttpError)
{
throw new HttpException(httpRequest, httpResponse);
}
List<Track> tracks = new List<Track>();
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<Artist> SearchForNewArtist(string title)
{
try
@ -220,16 +206,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<Artist>();
}
try
{
return new List<Artist> { GetArtistInfo(itunesId).Item1 };
return new List<Artist> { GetArtistInfo(slug).Item1 };
}
catch (ArtistNotFoundException)
{
@ -239,8 +223,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();
@ -249,16 +233,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
List<Artist> artists = MapArtists(httpResponse.Resource);
List<Artist> newArtists = new List<Artist>(artists.Count);
int count = 0;
foreach (var artist in artists)
{
newArtists.Add(AddOverview(artist));
count++;
}
return newArtists;
return artists;
}
catch (HttpException)
{
@ -271,77 +247,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<ArtistResource>(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<Artist> MapArtists(ArtistResource resource)
{
Album tempAlbum;
List<Artist> artists = new List<Artist>();
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);
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);
}
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);
}
// Maybe? Get all the albums for said artist
return artists;
}
private Album MapAlbum(AlbumResource albumQuery)
{
Album album = new 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;
}
// 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)
{

View file

@ -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<ValidationFailure>
{
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)
});
}

View file

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

View file

@ -14,10 +14,11 @@ namespace NzbDrone.Core.Music
Images = new List<MediaCover.MediaCover>();
}
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; }
public List<Track> Tracks { get; set; }
public int DiscCount { get; set; }
public bool Monitored { get; set; }
public List<MediaCover.MediaCover> Images { get; set; }

View file

@ -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; }
@ -32,44 +32,28 @@ 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<MediaCover.MediaCover> Images { get; set; }
public List<string> Genres { get; set; }
public int QualityProfileId { get; set; }
public string RootFolderPath { get; set; }
public DateTime Added { get; set; }
public LazyLoaded<Profile> Profile { get; set; }
public int ProfileId { get; set; }
public List<Album> Albums { get; set; }
public HashSet<int> 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<Actor> 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());
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;
@ -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;

View file

@ -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<ArtistAddedEvent>
{
private readonly IManageCommandQueue _commandQueueManager;
public ArtistAddedHandler(IManageCommandQueue commandQueueManager)
{
_commandQueueManager = commandQueueManager;
}
public void Handle(ArtistAddedEvent message)
{
_commandQueueManager.Push(new RefreshArtistCommand(message.Artist.Id));
}
}
}

View file

@ -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<Artist>, 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)

View file

@ -17,7 +17,7 @@ namespace NzbDrone.Core.Music
Artist GetArtist(int artistId);
List<Artist> GetArtists(IEnumerable<int> 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);
}
}

View file

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

View file

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

View file

@ -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<Track> Added { get; private set; }
public ReadOnlyCollection<Track> Updated { get; private set; }
public TrackInfoRefreshedEvent(Artist artist, IList<Track> added, IList<Track> updated)
{
Artist = artist;
Added = new ReadOnlyCollection<Track>(added);
Updated = new ReadOnlyCollection<Track>(updated);
}
}
}

View file

@ -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<RefreshArtistCommand>
{
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<Artist, List<Track>> tuple;
try
{
tuple = _artistInfo.GetArtistInfo(artist.SpotifyId);
}
catch (ArtistNotFoundException)
{
_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.SpotifyId != artistInfo.SpotifyId)
{
_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;
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<Album> 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.SpotifyId, 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);
}
}
}
}
}
}
}

View file

@ -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<Track> 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<Track> remoteTracks)
{
_logger.Info("Starting track info refresh for: {0}", artist);
var successCount = 0;
var failCount = 0;
var existingTracks = _trackService.GetTrackByArtist(artist.SpotifyId);
var albums = artist.Albums;
var updateList = new List<Track>();
var newList = new List<Track>();
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.SpotifyId; // TODO: Ensure LazyLoaded<Artist> 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<Track>();
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<Album> 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<Track> existingTracks)
{
return existingTracks.FirstOrDefault(e => e.AlbumId == track.AlbumId && e.TrackNumber == track.TrackNumber);
}
private IEnumerable<Track> OrderTracks(Artist artist, List<Track> tracks)
{
return tracks.OrderBy(e => e.AlbumId).ThenBy(e => e.TrackNumber);
}
}
}

View file

@ -17,9 +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 LazyLoaded<Artist> ArtistsId { get; set; }
public int SpotifyTrackId { get; set; }
public string AlbumId { get; set; }
public LazyLoaded<Artist> Artist { get; set; }
public string 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<MediaCover.MediaCover> Images { 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());
}
}
}

View file

@ -10,12 +10,12 @@ namespace NzbDrone.Core.Music
{
Track GetTrack(int id);
List<Track> GetTracks(IEnumerable<int> ids);
Track FindTrack(int artistId, int albumId, int trackNumber);
Track FindTrackByTitle(int artistId, int albumId, string releaseTitle);
List<Track> GetTrackByArtist(int artistId);
List<Track> GetTracksByAblum(int artistId, int albumId);
List<Track> GetTracksByAblumTitle(int artistId, string albumTitle);
List<Track> TracksWithFiles(int artistId);
Track FindTrack(string artistId, string albumId, int trackNumber);
Track FindTrackByTitle(string artistId, string albumId, string releaseTitle);
List<Track> GetTrackByArtist(string artistId);
List<Track> GetTracksByAlbum(string artistId, string albumId);
List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle);
List<Track> TracksWithFiles(string artistId);
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
List<Track> GeTracksByFileId(int trackFileId);
void UpdateTrack(Track track);
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Music
void InsertMany(List<Track> tracks);
void UpdateMany(List<Track> tracks);
void DeleteMany(List<Track> 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<Track> GetTrackByArtist(int artistId)
public List<Track> GetTrackByArtist(string artistId)
{
throw new NotImplementedException();
}
@ -64,12 +64,12 @@ namespace NzbDrone.Core.Music
throw new NotImplementedException();
}
public List<Track> GetTracksByAblum(int artistId, int albumId)
public List<Track> GetTracksByAlbum(string artistId, string albumId)
{
throw new NotImplementedException();
}
public List<Track> GetTracksByAblumTitle(int artistId, string albumTitle)
public List<Track> 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<Track> TracksWithFiles(int artistId)
public List<Track> TracksWithFiles(string artistId)
{
throw new NotImplementedException();
}

View file

@ -815,6 +815,8 @@
<Compile Include="Messaging\IProcessMessage.cs" />
<Compile Include="MetadataSource\IProvideArtistInfo.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ActorResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\AlbumInfoResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ArtistInfoResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ArtistResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\EpisodeResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ImageResource.cs" />
@ -822,6 +824,7 @@
<Compile Include="MetadataSource\SkyHook\Resource\SeasonResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ShowResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\TrackInfoResource.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />
@ -851,14 +854,20 @@
<Compile Include="Music\AddArtistValidator.cs" />
<Compile Include="Music\Album.cs" />
<Compile Include="Music\Artist.cs" />
<Compile Include="Music\ArtistAddedHandler.cs" />
<Compile Include="Music\ArtistNameNormalizer.cs" />
<Compile Include="Music\ArtistSlugValidator.cs" />
<Compile Include="Music\ArtistRepository.cs" />
<Compile Include="Music\ArtistService.cs" />
<Compile Include="Music\Commands\RefreshArtistCommand.cs" />
<Compile Include="Music\Events\ArtistAddedEvent.cs" />
<Compile Include="Music\Events\ArtistDeletedEvent.cs" />
<Compile Include="Music\Events\ArtistEditedEvent.cs" />
<Compile Include="Music\Events\ArtistRefreshStartingEvent.cs" />
<Compile Include="Music\Events\ArtistUpdatedEvent.cs" />
<Compile Include="Music\Events\TrackInfoRefreshedEvent.cs" />
<Compile Include="Music\RefreshArtistService.cs" />
<Compile Include="Music\RefreshTrackService.cs" />
<Compile Include="Music\Track.cs" />
<Compile Include="Music\TrackService.cs" />
<Compile Include="Notifications\Join\JoinAuthException.cs" />

View file

@ -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()
{

View file

@ -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()));
}
}
}

View file

@ -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 });
}
}
},

View file

@ -8,7 +8,7 @@
<div class="col-md-10 col-xs-9">
<div class="row">
<div class="col-md-10 col-xs-10">
<a href="artist/{{artistName}}" target="_blank">
<a href="artist/{{artistNameSlug}}" target="_blank">
<h2>{{artistName}}</h2>
</a>
</div>
@ -50,6 +50,9 @@
<div class="col-md-2 col-xs-4">
{{> EpisodeProgressPartial }}
</div>
<div class="col-md-8 col-xs-10">
Path {{path}}
</div>
</div>
</div>
</div>