New DB Schema

Rearrange DB Schema for Album Centric Plan
pull/6/head
Qstick 7 years ago
parent db62c15c17
commit 0f3c355381

@ -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.SpotifyId).NotEqual("").SetValidator(artistExistsValidator);
PostValidator.RuleFor(s => s.ForeignArtistId).NotEqual("").SetValidator(artistExistsValidator);
PutValidator.RuleFor(s => s.Path).IsValidPath();
}

@ -18,8 +18,8 @@ namespace NzbDrone.Api.Music
//View Only
public string ArtistName { get; set; }
public string SpotifyId { get; set; }
public string Name { get; set; }
public string ForeignArtistId { get; set; }
public string Overview { get; set; }
public int AlbumCount
@ -59,7 +59,7 @@ namespace NzbDrone.Api.Music
public DateTime Added { get; set; }
public AddSeriesOptions AddOptions { get; set; }
public Ratings Ratings { get; set; }
public string ArtistSlug { get; internal set; }
public string NameSlug { get; set; }
}
public static class ArtistResourceMapper
@ -72,7 +72,7 @@ namespace NzbDrone.Api.Music
{
Id = model.Id,
ArtistName = model.ArtistName,
Name = model.Name,
//AlternateTitles
//SortTitle = resource.SortTitle,
@ -94,7 +94,6 @@ namespace NzbDrone.Api.Music
Path = model.Path,
ProfileId = model.ProfileId,
ArtistFolder = model.ArtistFolder,
Monitored = model.Monitored,
//UseSceneNumbering = resource.UseSceneNumbering,
@ -105,8 +104,8 @@ namespace NzbDrone.Api.Music
//FirstAired = resource.FirstAired,
//LastInfoSync = resource.LastInfoSync,
//SeriesType = resource.SeriesType,
SpotifyId = model.SpotifyId,
ArtistSlug = model.ArtistSlug,
ForeignArtistId = model.ForeignArtistId,
NameSlug = model.NameSlug,
RootFolderPath = model.RootFolderPath,
Genres = model.Genres,
@ -125,7 +124,7 @@ namespace NzbDrone.Api.Music
{
Id = resource.Id,
ArtistName = resource.ArtistName,
Name = resource.Name,
//AlternateTitles
//SortTitle = resource.SortTitle,
@ -147,11 +146,10 @@ namespace NzbDrone.Api.Music
Path = resource.Path,
ProfileId = resource.ProfileId,
ArtistFolder = resource.ArtistFolder,
Monitored = resource.Monitored,
//LastInfoSync = resource.LastInfoSync,
SpotifyId = resource.SpotifyId,
ArtistSlug = resource.ArtistSlug,
ForeignArtistId = resource.ForeignArtistId,
NameSlug = resource.NameSlug,
RootFolderPath = resource.RootFolderPath,
Genres = resource.Genres,

@ -12,64 +12,91 @@ namespace NzbDrone.Core.Datastore.Migration
{
protected override void MainDbUpgrade()
{
Create.TableForModel("Artist")
.WithColumn("SpotifyId").AsString().Nullable().Unique()
.WithColumn("ArtistName").AsString().Unique()
.WithColumn("ArtistSlug").AsString().Nullable() //.Unique()
.WithColumn("CleanTitle").AsString().Nullable() // Do we need this?
.WithColumn("Monitored").AsBoolean()
Create.TableForModel("Artists")
.WithColumn("ForeignArtistId").AsString().Unique()
.WithColumn("MBId").AsString().Nullable()
.WithColumn("AMId").AsString().Nullable()
.WithColumn("TADBId").AsInt32().Nullable()
.WithColumn("DiscogsId").AsInt32().Nullable()
.WithColumn("Name").AsString()
.WithColumn("NameSlug").AsString().Nullable().Unique()
.WithColumn("CleanName").AsString().Indexed()
.WithColumn("Status").AsInt32()
.WithColumn("Overview").AsString().Nullable()
.WithColumn("AlbumFolder").AsBoolean().Nullable()
.WithColumn("ArtistFolder").AsBoolean().Nullable()
.WithColumn("Images").AsString()
.WithColumn("Path").AsString().Indexed()
.WithColumn("Monitored").AsBoolean()
.WithColumn("AlbumFolder").AsBoolean()
.WithColumn("LastInfoSync").AsDateTime().Nullable()
.WithColumn("LastDiskSync").AsDateTime().Nullable()
.WithColumn("Status").AsInt32().Nullable()
.WithColumn("Path").AsString()
.WithColumn("Images").AsString().Nullable()
.WithColumn("QualityProfileId").AsInt32().Nullable()
.WithColumn("RootFolderPath").AsString().Nullable()
.WithColumn("Added").AsDateTime().Nullable()
.WithColumn("ProfileId").AsInt32().Nullable() // This is either ProfileId or Profile
.WithColumn("DateFormed").AsDateTime().Nullable()
.WithColumn("Members").AsString().Nullable()
.WithColumn("Ratings").AsString().Nullable()
.WithColumn("Genres").AsString().Nullable()
.WithColumn("Albums").AsString().Nullable()
.WithColumn("SortName").AsString().Nullable()
.WithColumn("ProfileId").AsInt32().Nullable()
.WithColumn("Tags").AsString().Nullable()
.WithColumn("AddOptions").AsString().Nullable()
;
.WithColumn("Added").AsDateTime().Nullable()
.WithColumn("AddOptions").AsString().Nullable();
Create.TableForModel("Albums")
.WithColumn("AlbumId").AsString().Unique()
.WithColumn("ArtistId").AsInt32() // Should this be artistId (string)
.WithColumn("ForeignArtistId").AsString().Unique()
.WithColumn("ArtistId").AsInt32()
.WithColumn("MBId").AsString().Indexed()
.WithColumn("AMId").AsString().Nullable()
.WithColumn("TADBId").AsInt32().Indexed()
.WithColumn("DiscogsId").AsInt32().Nullable()
.WithColumn("Title").AsString()
.WithColumn("Year").AsInt32()
.WithColumn("Image").AsInt32()
.WithColumn("TrackCount").AsInt32()
.WithColumn("DiscCount").AsInt32()
.WithColumn("TitleSlug").AsString().Nullable().Unique()
.WithColumn("CleanTitle").AsString().Indexed()
.WithColumn("Status").AsInt32()
.WithColumn("Overview").AsString().Nullable()
.WithColumn("Images").AsString()
.WithColumn("Path").AsString().Indexed()
.WithColumn("Monitored").AsBoolean()
.WithColumn("Overview").AsString();
.WithColumn("LastInfoSync").AsDateTime().Nullable()
.WithColumn("LastDiskSync").AsDateTime().Nullable()
.WithColumn("ReleaseDate").AsDateTime().Nullable()
.WithColumn("Ratings").AsString().Nullable()
.WithColumn("Genres").AsString().Nullable()
.WithColumn("Label").AsString().Nullable()
.WithColumn("SortTitle").AsString().Nullable()
.WithColumn("ProfileId").AsInt32().Nullable()
.WithColumn("Tags").AsString().Nullable()
.WithColumn("Added").AsDateTime().Nullable()
.WithColumn("AlbumType").AsString()
.WithColumn("AddOptions").AsString().Nullable();
Create.TableForModel("Tracks")
.WithColumn("SpotifyTrackId").AsString().Nullable() // This shouldn't be nullable, but TrackRepository won't behave. Someone please fix this.
.WithColumn("AlbumId").AsString()
.WithColumn("ArtistId").AsString() // This may be a list of Ids in future for compilations
.WithColumn("ArtistSpotifyId").AsString()
.WithColumn("Compilation").AsBoolean()
.WithColumn("ArtistId").AsInt32().Indexed()
.WithColumn("AlbumId").AsInt32()
.WithColumn("MBId").AsString().Indexed()
.WithColumn("TrackNumber").AsInt32()
.WithColumn("Title").AsString().Nullable()
.WithColumn("Ignored").AsBoolean().Nullable()
.WithColumn("Explict").AsBoolean()
.WithColumn("Explicit").AsBoolean()
.WithColumn("DiscNumber").AsInt32().Nullable()
.WithColumn("TrackFileId").AsInt32().Nullable().Indexed()
.WithColumn("Monitored").AsBoolean()
.WithColumn("TrackFileId").AsInt32().Nullable()
.WithColumn("ReleaseDate").AsDateTime().Nullable();
.WithColumn("Ratings").AsString().Nullable();
Create.Index().OnTable("Tracks").OnColumn("ArtistId").Ascending()
.OnColumn("AlbumId").Ascending()
.OnColumn("TrackNumber").Ascending();
Create.TableForModel("TrackFiles")
.WithColumn("ArtistId").AsInt32()
.WithColumn("Path").AsString().Unique()
.WithColumn("ArtistId").AsInt32().Indexed()
.WithColumn("AlbumId").AsInt32().Indexed()
.WithColumn("Quality").AsString()
.WithColumn("Size").AsInt64()
.WithColumn("DateAdded").AsDateTime()
.WithColumn("AlbumId").AsInt32(); // How does this impact stand alone tracks?
.WithColumn("SceneName").AsString().Nullable()
.WithColumn("ReleaseGroup").AsString().Nullable()
.WithColumn("MediaInfo").AsString().Nullable()
.WithColumn("RelativePath").AsString().Nullable();
Alter.Table("NamingConfig")
.AddColumn("ArtistFolderFormat").AsString().Nullable()
.AddColumn("AlbumFolderFormat").AsString().Nullable();
}
}

@ -1,15 +0,0 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(112)]
public class add_music_fields_to_namingconfig : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("NamingConfig").AddColumn("ArtistFolderFormat").AsAnsiString().Nullable();
Alter.Table("NamingConfig").AddColumn("AlbumFolderFormat").AsAnsiString().Nullable();
}
}
}

@ -92,11 +92,13 @@ namespace NzbDrone.Core.Datastore
.Relationship()
.HasOne(episode => episode.EpisodeFile, episode => episode.EpisodeFileId);
Mapper.Entity<Artist>().RegisterModel("Artist")
Mapper.Entity<Artist>().RegisterModel("Artists")
.Ignore(s => s.RootFolderPath)
.Relationship()
.HasOne(a => a.Profile, a => a.ProfileId);
Mapper.Entity<Album>().RegisterModel("Album");
Mapper.Entity<TrackFile>().RegisterModel("TrackFiles")
.Ignore(f => f.Path)
.Relationships.AutoMapICollectionOrComplexProperties()

@ -0,0 +1,17 @@
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Download
{
public class AlbumGrabbedEvent : IEvent
{
public RemoteAlbum Album { get; private set; }
public string DownloadClient { get; set; }
public string DownloadId { get; set; }
public AlbumGrabbedEvent(RemoteAlbum album)
{
Album = album;
}
}
}

@ -5,9 +5,9 @@ using System.Text;
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{
public class AlbumInfoResource
public class AlbumResource
{
public AlbumInfoResource()
public AlbumResource()
{
}

@ -5,47 +5,20 @@ using System.Text;
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{
public class AristResultResource
{
public AristResultResource()
{
}
public List<ArtistInfoResource> Items { get; set; }
public int Count { get; set; }
}
public class AlbumResultResource
{
public AlbumResultResource()
{
}
public List<AlbumInfoResource> Items { get; set; }
public int Count { get; set; }
}
public class TrackResultResource
{
public TrackResultResource()
{
}
public List<TrackInfoResource> Items { get; set; }
public int Count { get; set; }
}
public class ArtistResource
{
public ArtistResource()
{
public ArtistResource() {
Albums = new List<AlbumResource>();
Tracks = new List<TrackResource>();
}
public AristResultResource Artists { get; set; }
public AristResultResource Albums { get; set; }
public List<string> Genres { get; set; }
public string AristUrl { get; set; }
public string Overview { get; set; }
public string Id { get; set; }
public List<ImageResource> Images { get; set; }
public string ArtistName { get; set; }
public List<AlbumResource> Albums { get; set; }
public List<TrackResource> Tracks { get; set; }
}
}

@ -5,9 +5,9 @@ using System.Text;
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{
public class TrackInfoResource
public class TrackResource
{
public TrackInfoResource()
public TrackResource()
{
}

@ -108,92 +108,96 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
}
// It is safe to assume an id will only return one Artist back
Artist artist = new Artist();
artist.ArtistName = httpResponse.Resource.Artists.Items[0].ArtistName;
artist.SpotifyId = httpResponse.Resource.Artists.Items[0].Id;
artist.Genres = httpResponse.Resource.Artists.Items[0].Genres;
var albumRet = MapAlbums(artist);
artist = albumRet.Item1;
return new Tuple<Artist, List<Track>>(albumRet.Item1, albumRet.Item2);
}
private Tuple<Artist, List<Track>> 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 albums = httpResponse.Resource.Albums.Select(MapAlbum);
var tracks = httpResponse.Resource.Tracks.Select(MapTrack);
var artist = MapArtist(httpResponse.Resource);
var httpResponse = _httpClient.Get<AlbumResultResource>(httpRequest);
if (httpResponse.HasHttpError)
{
throw new HttpException(httpRequest, httpResponse);
}
List<Track> masterTracks = 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.AlbumName;
album.ArtworkUrl = albumResource.Images.Count > 0 ? albumResource.Images[0].Url : "";
album.Tracks = MapTracksToAlbum(album);
masterTracks.InsertRange(masterTracks.Count, album.Tracks);
albums.Add(album);
}
//artist.Name = httpResponse.Resource.Artists.Items[0].ArtistName;
//artist.ForeignArtistId = httpResponse.Resource.Artists.Items[0].Id;
//artist.Genres = httpResponse.Resource.Artists.Items[0].Genres;
// TODO: We now need to get all tracks for each album
//var albumRet = MapAlbums(artist);
//artist = albumRet.Item1;
artist.Albums = albums;
return new Tuple<Artist, List<Track>>(artist, masterTracks);
return new Tuple<Artist, List<Track>>(artist, tracks.ToList());
}
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);
}
//private Tuple<Artist, List<Track>> MapAlbums(Artist artist)
//{
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.SpotifyTrackId = trackResource.Id;
track.ArtistSpotifyId = trackResource.Artists.Count > 0 ? trackResource.Artists[0].Id : null;
track.Explict = trackResource.Explicit;
track.Compilation = trackResource.Artists.Count > 1;
track.TrackNumber = trackResource.TrackNumber;
track.Title = trackResource.TrackName;
tracks.Add(track);
}
// // Find all albums for the artist and all tracks for said album
// ///v1/artists/{id}/albums
// var httpRequest = _requestBuilder.Create()
// .SetSegment("route", "artists/" + artist.ForeignArtistId + "/albums")
// .Build();
// httpRequest.AllowAutoRedirect = true;
// httpRequest.SuppressHttpError = true;
// var httpResponse = _httpClient.Get<AlbumResultResource>(httpRequest);
// if (httpResponse.HasHttpError)
// {
// throw new HttpException(httpRequest, httpResponse);
// }
// List<Track> masterTracks = 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.AlbumName;
// album.ArtworkUrl = albumResource.Images.Count > 0 ? albumResource.Images[0].Url : "";
// album.Tracks = MapTracksToAlbum(album);
// masterTracks.InsertRange(masterTracks.Count, album.Tracks);
// albums.Add(album);
// }
// // TODO: We now need to get all tracks for each album
// artist.Albums = albums;
// return new Tuple<Artist, List<Track>>(artist, masterTracks);
//}
return tracks;
}
//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.SpotifyTrackId = trackResource.Id;
// track.ArtistSpotifyId = trackResource.Artists.Count > 0 ? trackResource.Artists[0].Id : null;
// track.Explict = trackResource.Explicit;
// track.Compilation = trackResource.Artists.Count > 1;
// track.TrackNumber = trackResource.TrackNumber;
// track.Title = trackResource.TrackName;
// tracks.Add(track);
// }
// return tracks;
//}
public List<Artist> SearchForNewArtist(string title)
@ -230,12 +234,26 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
var httpResponse = _httpClient.Get<ArtistResource>(httpRequest);
var httpResponse = _httpClient.Get<List<ArtistResource>>(httpRequest);
return httpResponse.Resource.SelectList(MapArtist);
//List<Artist> artists = MapArtists(httpResponse.Resource);
//List<Artist> artists = new List<Artist>();
//foreach (var artistResource in httpResponse.Resource.Artists.Items)
//{
// Artist artist = new Artist();
// artist.Name = artistResource.ArtistName;
// artist.ForeignArtistId = artistResource.Id; // TODO: Rename spotifyId to LidarrId
// artist.Genres = artistResource.Genres;
// artist.NameSlug = Parser.Parser.CleanArtistTitle(artist.Name);
// artist.CleanName = Parser.Parser.CleanArtistTitle(artist.Name);
//artist.Images = artistResource.Images;
// artists.Add(artist);
//}
List<Artist> artists = MapArtists(httpResponse.Resource);
return artists;
//return artists;
}
catch (HttpException)
{
@ -248,27 +266,37 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
}
}
private List<Artist> MapArtists(ArtistResource resource)
private static Album MapAlbum(AlbumResource resource)
{
Album album = new Album();
return album;
}
private static Track MapTrack(TrackResource resource)
{
Track track = new Track();
return track;
}
private static Artist MapArtist(ArtistResource resource)
{
Artist artist = new Artist();
artist.Name = resource.ArtistName;
artist.ForeignArtistId = resource.Id; // TODO: Rename spotifyId to LidarrId
artist.Genres = resource.Genres;
artist.Overview = resource.Overview;
artist.NameSlug = Parser.Parser.CleanArtistTitle(artist.Name);
artist.CleanName = Parser.Parser.CleanArtistTitle(artist.Name);
//artist.Images = resource.Artists.Items[0].Images;
List<Artist> artists = new List<Artist>();
foreach(var artistResource in resource.Artists.Items)
{
Artist artist = new Artist();
artist.ArtistName = artistResource.ArtistName;
artist.SpotifyId = artistResource.Id; // TODO: Rename spotifyId to LidarrId
artist.Genres = artistResource.Genres;
artist.ArtistSlug = Parser.Parser.CleanArtistTitle(artist.ArtistName);
//artist.Images = artistResource.Images;
artists.Add(artist);
}
// Maybe? Get all the albums for said artist
return artists;
return artist;
}
//private Album MapAlbum(AlbumResource albumQuery)

@ -48,11 +48,11 @@ namespace NzbDrone.Core.Music
if (string.IsNullOrWhiteSpace(newArtist.Path))
{
var folderName = newArtist.ArtistName;// TODO: _fileNameBuilder.GetArtistFolder(newArtist);
var folderName = newArtist.Name;// TODO: _fileNameBuilder.GetArtistFolder(newArtist);
newArtist.Path = Path.Combine(newArtist.RootFolderPath, folderName);
}
newArtist.CleanTitle = newArtist.ArtistName.CleanSeriesTitle();
newArtist.CleanName = newArtist.Name.CleanSeriesTitle();
//newArtist.SortTitle = ArtistNameNormalizer.Normalize(newArtist.ArtistName, newArtist.ItunesId); // There is no Sort Title
newArtist.Added = DateTime.UtcNow;
@ -75,15 +75,15 @@ namespace NzbDrone.Core.Music
try
{
tuple = _artistInfo.GetArtistInfo(newArtist.SpotifyId);
tuple = _artistInfo.GetArtistInfo(newArtist.ForeignArtistId);
}
catch (ArtistNotFoundException)
{
_logger.Error("SpotifyId {1} was not found, it may have been removed from Spotify.", newArtist.SpotifyId);
_logger.Error("SpotifyId {1} was not found, it may have been removed from Spotify.", newArtist.ForeignArtistId);
throw new ValidationException(new List<ValidationFailure>
{
new ValidationFailure("SpotifyId", "An artist with this ID was not found", newArtist.SpotifyId)
new ValidationFailure("SpotifyId", "An artist with this ID was not found", newArtist.ForeignArtistId)
});
}

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Music
.SetValidator(droneFactoryValidator)
.SetValidator(seriesAncestorValidator);
RuleFor(c => c.ArtistSlug).SetValidator(artistTitleSlugValidator);// TODO: Check if we are going to use a slug or artistName
RuleFor(c => c.NameSlug).SetValidator(artistTitleSlugValidator);// TODO: Check if we are going to use a slug or artistName
}
}
}

@ -7,7 +7,7 @@ using System.Text;
namespace NzbDrone.Core.Music
{
public class Album : IEmbeddedDocument
public class Album : ModelBase
{
public Album()
{

@ -22,21 +22,19 @@ namespace NzbDrone.Core.Music
}
public string SpotifyId { get; set; }
public string ArtistName { get; set; }
public string ArtistSlug { get; set; }
public string CleanTitle { get; set; }
public string ForeignArtistId { get; set; }
public string Name { get; set; }
public string NameSlug { get; set; }
public string CleanName { get; set; }
public string Overview { get; set; }
public bool Monitored { get; set; }
public bool AlbumFolder { get; set; }
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; }
@ -47,16 +45,16 @@ namespace NzbDrone.Core.Music
public override string ToString()
{
return string.Format("[{0}][{1}]", SpotifyId, ArtistName.NullSafe());
return string.Format("[{0}][{1}]", ForeignArtistId, Name.NullSafe());
}
public void ApplyChanges(Artist otherArtist)
{
SpotifyId = otherArtist.SpotifyId;
ArtistName = otherArtist.ArtistName;
ArtistSlug = otherArtist.ArtistSlug;
CleanTitle = otherArtist.CleanTitle;
ForeignArtistId = otherArtist.ForeignArtistId;
Name = otherArtist.Name;
NameSlug = otherArtist.NameSlug;
CleanName = otherArtist.CleanName;
Monitored = otherArtist.Monitored;
AlbumFolder = otherArtist.AlbumFolder;
LastInfoSync = otherArtist.LastInfoSync;
@ -69,7 +67,6 @@ namespace NzbDrone.Core.Music
ProfileId = otherArtist.ProfileId;
Albums = otherArtist.Albums;
Tags = otherArtist.Tags;
ArtistFolder = otherArtist.ArtistFolder;
AddOptions = otherArtist.AddOptions;
Albums = otherArtist.Albums;

@ -24,16 +24,16 @@ namespace NzbDrone.Core.Music
return Query.Where(c => c.Path == path).Any();
}
public Artist FindById(string spotifyId)
public Artist FindById(string foreignArtistId)
{
return Query.Where(s => s.SpotifyId == spotifyId).SingleOrDefault();
return Query.Where(s => s.ForeignArtistId == foreignArtistId).SingleOrDefault();
}
public Artist FindByName(string cleanName)
{
cleanName = cleanName.ToLowerInvariant();
return Query.Where(s => s.CleanTitle == cleanName)
return Query.Where(s => s.CleanName == cleanName)
.SingleOrDefault();
}
}

@ -114,7 +114,7 @@ namespace NzbDrone.Core.Music
if (storedAlbum != null && album.Monitored != storedAlbum.Monitored)
{
_trackService.SetTrackMonitoredByAlbum(artist.SpotifyId, album.AlbumId, album.Monitored);
_trackService.SetTrackMonitoredByAlbum(artist.ForeignArtistId, album.AlbumId, album.Monitored);
}
}
@ -129,17 +129,17 @@ namespace NzbDrone.Core.Music
_logger.Debug("Updating {0} artist", artist.Count);
foreach (var s in artist)
{
_logger.Trace("Updating: {0}", s.ArtistName);
_logger.Trace("Updating: {0}", s.Name);
if (!s.RootFolderPath.IsNullOrWhiteSpace())
{
var folderName = new DirectoryInfo(s.Path).Name;
s.Path = Path.Combine(s.RootFolderPath, folderName);
_logger.Trace("Changing path for {0} to {1}", s.ArtistName, s.Path);
_logger.Trace("Changing path for {0} to {1}", s.Name, s.Path);
}
else
{
_logger.Trace("Not changing path for: {0}", s.ArtistName);
_logger.Trace("Not changing path for: {0}", s.Name);
}
}

@ -23,7 +23,7 @@ namespace NzbDrone.Core.Music
dynamic instance = context.ParentContext.InstanceToValidate;
var instanceId = (int)instance.Id;
return !_artistService.GetAllArtists().Exists(s => s.ArtistSlug.Equals(context.PropertyValue.ToString()) && s.Id != instanceId);
return !_artistService.GetAllArtists().Exists(s => s.NameSlug.Equals(context.PropertyValue.ToString()) && s.Id != instanceId);
}
}
}

@ -45,33 +45,33 @@ namespace NzbDrone.Core.Music
private void RefreshArtistInfo(Artist artist)
{
_logger.ProgressInfo("Updating Info for {0}", artist.ArtistName);
_logger.ProgressInfo("Updating Info for {0}", artist.Name);
Tuple<Artist, List<Track>> tuple;
try
{
tuple = _artistInfo.GetArtistInfo(artist.SpotifyId);
tuple = _artistInfo.GetArtistInfo(artist.ForeignArtistId);
}
catch (ArtistNotFoundException)
{
_logger.Error("Artist '{0}' (SpotifyId {1}) was not found, it may have been removed from Spotify.", artist.ArtistName, artist.SpotifyId);
_logger.Error("Artist '{0}' (SpotifyId {1}) was not found, it may have been removed from Spotify.", artist.Name, artist.ForeignArtistId);
return;
}
var artistInfo = tuple.Item1;
if (artist.SpotifyId != artistInfo.SpotifyId)
if (artist.ForeignArtistId != artistInfo.ForeignArtistId)
{
_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;
_logger.Warn("Artist '{0}' (SpotifyId {1}) was replaced with '{2}' (SpotifyId {3}), because the original was a duplicate.", artist.Name, artist.ForeignArtistId, artistInfo.Name, artistInfo.ForeignArtistId);
artist.ForeignArtistId = artistInfo.ForeignArtistId;
}
artist.ArtistName = artistInfo.ArtistName;
artist.ArtistSlug = artistInfo.ArtistSlug;
artist.Name = artistInfo.Name;
artist.NameSlug = artistInfo.NameSlug;
artist.Overview = artistInfo.Overview;
artist.Status = artistInfo.Status;
artist.CleanTitle = artistInfo.CleanTitle;
artist.CleanName = artistInfo.CleanName;
artist.LastInfoSync = DateTime.UtcNow;
artist.Images = artistInfo.Images;
artist.Genres = artistInfo.Genres;
@ -91,7 +91,7 @@ namespace NzbDrone.Core.Music
_artistService.UpdateArtist(artist);
_refreshTrackService.RefreshTrackInfo(artist, tuple.Item2);
_logger.Debug("Finished artist refresh for {0}", artist.ArtistName);
_logger.Debug("Finished artist refresh for {0}", artist.Name);
_eventAggregator.PublishEvent(new ArtistUpdatedEvent(artist));
}
@ -112,7 +112,7 @@ namespace NzbDrone.Core.Music
// continue;
//}
_logger.Debug("New album ({0}) for artist: [{1}] {2}, setting monitored to true", album.Title, artist.SpotifyId, artist.ArtistName);
_logger.Debug("New album ({0}) for artist: [{1}] {2}, setting monitored to true", album.Title, artist.ForeignArtistId, artist.Name);
album.Monitored = true;
}
@ -136,7 +136,7 @@ namespace NzbDrone.Core.Music
}
else
{
var allArtists = _artistService.GetAllArtists().OrderBy(c => c.ArtistName).ToList();
var allArtists = _artistService.GetAllArtists().OrderBy(c => c.Name).ToList();
foreach (var artist in allArtists)
{
@ -156,7 +156,7 @@ namespace NzbDrone.Core.Music
{
try
{
_logger.Info("Skipping refresh of artist: {0}", artist.ArtistName);
_logger.Info("Skipping refresh of artist: {0}", artist.Name);
//TODO: _diskScanService.Scan(artist);
}
catch (Exception e)

@ -33,7 +33,7 @@ namespace NzbDrone.Core.Music
var successCount = 0;
var failCount = 0;
var existingTracks = _trackService.GetTracksByArtist(artist.SpotifyId);
var existingTracks = _trackService.GetTracksByArtist(artist.ForeignArtistId);
var albums = artist.Albums;
var updateList = new List<Track>();
@ -66,7 +66,7 @@ namespace NzbDrone.Core.Music
trackToUpdate.Explict = track.Explict;
if (track.ArtistSpotifyId.IsNullOrWhiteSpace())
{
trackToUpdate.ArtistSpotifyId = artist.SpotifyId;
trackToUpdate.ArtistSpotifyId = artist.ForeignArtistId;
} else
{
trackToUpdate.ArtistSpotifyId = track.ArtistSpotifyId;
@ -104,7 +104,7 @@ namespace NzbDrone.Core.Music
if (failCount != 0)
{
_logger.Info("Finished track refresh for artist: {0}. Successful: {1} - Failed: {2} ",
artist.ArtistName, successCount, failCount);
artist.Name, successCount, failCount);
}
else
{

@ -26,13 +26,13 @@ namespace NzbDrone.Core.Music
{
if (artist.LastInfoSync < DateTime.UtcNow.AddDays(-30))
{
_logger.Trace("Artist {0} last updated more than 30 days ago, should refresh.", artist.ArtistName);
_logger.Trace("Artist {0} last updated more than 30 days ago, should refresh.", artist.Name);
return true;
}
if (artist.LastInfoSync >= DateTime.UtcNow.AddHours(-6))
{
_logger.Trace("Artist {0} last updated less than 6 hours ago, should not be refreshed.", artist.ArtistName);
_logger.Trace("Artist {0} last updated less than 6 hours ago, should not be refreshed.", artist.Name);
return false;
}

@ -118,7 +118,7 @@ namespace NzbDrone.Core.Music
private SortBuilder<Track> GetMissingEpisodesQuery(PagingSpec<Track> pagingSpec, DateTime currentTime)
{
return Query.Join<Track, Artist>(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistSpotifyId == s.SpotifyId)
return Query.Join<Track, Artist>(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistSpotifyId == s.ForeignArtistId)
.Where(pagingSpec.FilterExpression)
.AndWhere(e => e.TrackFileId == 0)
.AndWhere(BuildAirDateUtcCutoffWhereClause(currentTime))
@ -130,7 +130,7 @@ namespace NzbDrone.Core.Music
private SortBuilder<Track> EpisodesWhereCutoffUnmetQuery(PagingSpec<Track> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
{
return Query.Join<Track, Artist>(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistSpotifyId == s.SpotifyId)
return Query.Join<Track, Artist>(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistSpotifyId == s.ForeignArtistId)
.Join<Track, TrackFile>(JoinType.Left, e => e.TrackFile, (e, s) => e.TrackFileId == s.Id)
.Where(pagingSpec.FilterExpression)
.AndWhere(e => e.TrackFileId != 0)

@ -154,7 +154,7 @@ namespace NzbDrone.Core.Music
public void HandleAsync(ArtistDeletedEvent message)
{
var tracks = GetTracksByArtist(message.Artist.SpotifyId);
var tracks = GetTracksByArtist(message.Artist.ForeignArtistId);
_trackRepository.DeleteMany(tracks);
}

@ -288,7 +288,6 @@
</Compile>
<Compile Include="Datastore\Migration\105_rename_torrent_downloadstation.cs" />
<Compile Include="Datastore\Migration\111_setup_music.cs" />
<Compile Include="Datastore\Migration\112_add_music_fields_to_namingconfig.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" />
@ -816,7 +815,7 @@
<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\AlbumResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ArtistInfoResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ArtistResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\EpisodeResource.cs" />
@ -825,7 +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\Resource\TrackResource.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />

@ -284,8 +284,8 @@ namespace NzbDrone.Core.Organizer
private void AddArtistTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Artist artist)
{
tokenHandlers["{Artist Name}"] = m => artist.ArtistName;
tokenHandlers["{Artist CleanTitle}"] = m => CleanTitle(artist.ArtistName);
tokenHandlers["{Artist Name}"] = m => artist.Name;
tokenHandlers["{Artist CleanTitle}"] = m => CleanTitle(artist.Name);
}
private string AddSeasonEpisodeNumberingTokens(string pattern, Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Episode> episodes, NamingConfig namingConfig)

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Music;
namespace NzbDrone.Core.Parser.Model
{
public class RemoteAlbum
{
public ReleaseInfo Release { get; set; }
public ParsedTrackInfo ParsedTrackInfo { get; set; }
public Artist Artist { get; set; }
public List<Album> Albums { get; set; }
public bool DownloadAllowed { get; set; }
public bool IsRecentAlbum()
{
return Albums.Any(e => e.ReleaseDate >= DateTime.UtcNow.Date.AddDays(-14));
}
public override string ToString()
{
return Release.Title;
}
}
}

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Validation.Paths
{
if (context.PropertyValue == null) return true;
return (!_artistService.GetAllArtists().Exists(s => s.SpotifyId == context.PropertyValue.ToString()));
return (!_artistService.GetAllArtists().Exists(s => s.ForeignArtistId == context.PropertyValue.ToString()));
}
}
}

@ -1,3 +1,3 @@
<div class="text-center hint col-md-12">
<span>You can also search by Spotify using the spotify: prefixes.</span>
<span>You can also search by MusicBrianzID using the MBID: prefixes.</span>
</div>

@ -97,7 +97,7 @@ var view = Marionette.ItemView.extend({
},
_configureTemplateHelpers : function() {
var existingArtist = ArtistCollection.where({ spotifyId : this.model.get('spotifyId') });
var existingArtist = ArtistCollection.where({ foreignArtistId : this.model.get('foreignArtistId') });
if (existingArtist.length > 0) {
this.templateHelpers.existing = existingArtist[0].toJSON();

@ -5,7 +5,7 @@
<div class="col-md-12">
<h2 class="artist-title">
<!--{{titleWithYear}}-->
{{artistName}}
{{name}}
<!--<span class="labels">
<span class="label label-default">{{network}}</span>
@ -68,16 +68,16 @@
</div>
<div class="row">
{{#unless existing}}
{{#if artistName}}
{{#if name}}
<div class="form-group col-md-2 col-md-offset-10">
<!--Uncomment if we need to add even more controls to add artist-->
<!--<label style="visibility: hidden">Add</label>-->
<div class="btn-group">
<button class="btn btn-success add x-add" title="Add" data-artist="{{artistName}}">
<button class="btn btn-success add x-add" title="Add" data-artist="{{name}}">
<i class="icon-lidarr-add"></i>
</button>
<button class="btn btn-success add x-add-search" title="Add and Search for missing tracks" data-artist="{{artistName}}">
<button class="btn btn-success add x-add-search" title="Add and Search for missing tracks" data-artist="{{name}}">
<i class="icon-lidarr-search"></i>
</button>
</div>

@ -77,7 +77,7 @@ var Collection = PageableCollection.extend({
},
artistName: {
sortKey : 'artistName'
sortKey : 'name'
},
nextAiring : {

@ -21,12 +21,12 @@ module.exports = NzbDroneController.extend({
},
artistDetails : function(query) {
var artists = ArtistCollection.where({ artistSlug : query });
var artists = ArtistCollection.where({ nameSlug : query });
console.log('artistDetails, artists: ', artists);
if (artists.length !== 0) {
var targetArtist = artists[0];
console.log("[ArtistController] targetArtist: ", targetArtist);
this.setTitle(targetArtist.get('artistName')); // TODO: Update NzbDroneController
this.setTitle(targetArtist.get('name')); // TODO: Update NzbDroneController
//this.setArtistName(targetSeries.get('artistName'));
this.showMainRegion(new ArtistDetailsLayout({ model : targetArtist }));
} else {

@ -63,7 +63,7 @@ module.exports = Marionette.Layout.extend({
onShow : function() {
this._showBackdrop();
this._showSeasons();
this._showAlbums();
this._setMonitoredState();
this._showInfo();
},

@ -1,35 +1,35 @@
<div class="row series-page-header">
<div class="row artist-page-header">
<div class="visible-lg col-lg-2 poster">
{{poster}}
</div>
<div class="col-md-12 col-lg-10">
<div>
<h1 class="header-text">
<i class="x-monitored" title="Toggle monitored state for entire series"/>
{{title}}
<div class="series-actions pull-right">
<i class="x-monitored" title="Toggle monitored state for entire artist"/>
{{name}}
<div class="artist-actions pull-right">
<div class="x-episode-file-editor">
<i class="icon-lidarr-episode-file" title="Modify episode files for series"/>
<i class="icon-lidarr-episode-file" title="Modify episode files for artist"/>
</div>
<div class="x-refresh">
<i class="icon-lidarr-refresh icon-can-spin" title="Update series info and scan disk"/>
<i class="icon-lidarr-refresh icon-can-spin" title="Update artist info and scan disk"/>
</div>
<div class="x-rename">
<i class="icon-lidarr-rename" title="Preview rename for all episodes"/>
</div>
<div class="x-search">
<i class="icon-lidarr-search" title="Search for monitored episodes in this series"/>
<i class="icon-lidarr-search" title="Search for monitored episodes in this artist"/>
</div>
<div class="x-edit">
<i class="icon-lidarr-edit" title="Edit series"/>
<i class="icon-lidarr-edit" title="Edit artist"/>
</div>
</div>
</h1>
</div>
<div class="series-detail-overview">
<div class="artist-detail-overview">
{{overview}}
</div>
<div id="info" class="series-info"></div>
<div id="info" class="artist-info"></div>
</div>
</div>
<div id="seasons"></div>

@ -1,18 +1,18 @@
var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({
template : 'Series/Details/InfoViewTemplate',
template : 'Artist/Details/InfoViewTemplate',
initialize : function(options) {
this.episodeFileCollection = options.episodeFileCollection;
this.trackFileCollection = options.trackFileCollection;
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.episodeFileCollection, 'sync', this.render);
this.listenTo(this.trackFileCollection, 'sync', this.render);
},
templateHelpers : function() {
return {
fileCount : this.episodeFileCollection.length
fileCount : this.trackFileCollection.length
};
}
});

@ -28,7 +28,7 @@
{{/if_eq}}
</div>
<div class="col-md-3">
<span class="series-info-links">
<span class="artist-info-links">
<a href="{{traktUrl}}" class="label label-info">Trakt</a>
<a href="{{tvdbUrl}}" class="label label-info">The TVDB</a>

@ -45,7 +45,7 @@ module.exports = Marionette.Layout.extend({
cell : ArtistStatusCell
},
{
name : 'artistName',
name : 'name',
label : 'Artist',
cell : ArtistTitleCell,
cellValue : 'this'

@ -9,7 +9,7 @@
<div class="row">
<div class="col-md-10 col-xs-10">
<a href="{{route}}" target="_blank">
<h2>{{artistName}}</h2>
<h2>{{name}}</h2>
</a>
</div>
<div class="col-md-2 col-xs-2">

@ -40,7 +40,7 @@ Handlebars.registerHelper('tvMazeUrl', function() {
});
Handlebars.registerHelper('route', function() {
return StatusModel.get('urlBase') + '/artist/' + this.artistSlug;
return StatusModel.get('urlBase') + '/artist/' + this.nameSlug;
});
Handlebars.registerHelper('percentOfEpisodes', function() {

Loading…
Cancel
Save