New: Manually Edit/Override Album Release (#181)

* New: Manually Edit/Override Album Release

* !fixup for comments, loading all albums instead of only artist albums
* fixup! UI Cleanup lint issues
* fixup! Remove AddAlbum service for now, fix refresh override selected release
* fixup! Last one... to fix updating albums with custom release set

Closes #109 
Closes #129 
Closes #128
This commit is contained in:
Qstick
2018-01-17 21:28:47 -05:00
committed by GitHub
parent 74f433d4f0
commit 26ef43f302
96 changed files with 2928 additions and 408 deletions

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Nancy;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Music;
using NzbDrone.SignalR;
@@ -21,7 +22,7 @@ namespace Lidarr.Api.V1.Albums
: base(albumService, artistStatisticsService, artistService, upgradableSpecification, signalRBroadcaster)
{
GetResourceAll = GetAlbums;
Put[@"/(?<id>[\d]{1,10})"] = x => SetAlbumMonitored(x.Id);
UpdateResource = UpdateAlbum;
Put["/monitor"] = x => SetAlbumsMonitored();
}
@@ -29,8 +30,9 @@ namespace Lidarr.Api.V1.Albums
{
var artistIdQuery = Request.Query.ArtistId;
var albumIdsQuery = Request.Query.AlbumIds;
var foreignIdQuery = Request.Query.ForeignAlbumId;
if (!Request.Query.ArtistId.HasValue && !albumIdsQuery.HasValue)
if (!Request.Query.ArtistId.HasValue && !albumIdsQuery.HasValue && !foreignIdQuery.HasValue)
{
return MapToResource(_albumService.GetAllAlbums(), false);
}
@@ -42,6 +44,13 @@ namespace Lidarr.Api.V1.Albums
return MapToResource(_albumService.GetAlbumsByArtist(artistId), false);
}
if (foreignIdQuery.HasValue)
{
int artistId = _albumService.FindById(foreignIdQuery.Value).ArtistId;
return MapToResource(_albumService.GetAlbumsByArtist(artistId), false);
}
string albumIdsValue = albumIdsQuery.Value.ToString();
var albumIds = albumIdsValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
@@ -51,12 +60,15 @@ namespace Lidarr.Api.V1.Albums
return MapToResource(_albumService.GetAlbums(albumIds), false);
}
private Response SetAlbumMonitored(int id)
private void UpdateAlbum(AlbumResource albumResource)
{
var resource = Request.Body.FromJson<AlbumResource>();
_albumService.SetAlbumMonitored(id, resource.Monitored);
var album = _albumService.GetAlbum(albumResource.Id);
return MapToResource(_albumService.GetAlbum(id), false).AsResponse(HttpStatusCode.Accepted);
var model = albumResource.ToModel(album);
_albumService.UpdateAlbum(model);
BroadcastResourceChange(ModelAction.Updated, albumResource);
}
private Response SetAlbumsMonitored()

View File

@@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Common.Extensions;
using Lidarr.Api.V1.Artist;
using NzbDrone.Core.DecisionEngine;
@@ -9,10 +11,16 @@ using NzbDrone.Core.Music;
using NzbDrone.Core.ArtistStats;
using NzbDrone.SignalR;
using Lidarr.Http;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Music.Events;
namespace Lidarr.Api.V1.Albums
{
public abstract class AlbumModuleWithSignalR : LidarrRestModuleWithSignalR<AlbumResource, Album>
public abstract class AlbumModuleWithSignalR : LidarrRestModuleWithSignalR<AlbumResource, Album>,
IHandle<AlbumGrabbedEvent>,
IHandle<AlbumEditedEvent>,
IHandle<TrackImportedEvent>
{
protected readonly IAlbumService _albumService;
protected readonly IArtistStatisticsService _artistStatisticsService;
@@ -130,18 +138,26 @@ namespace Lidarr.Api.V1.Albums
}
}
//TODO: Implement Track or Album Grabbed/Dowloaded Events
public void Handle(AlbumGrabbedEvent message)
{
foreach (var album in message.Album.Albums)
{
var resource = album.ToResource();
resource.Grabbed = true;
//public void Handle(TrackGrabbedEvent message)
//{
// foreach (var track in message.Track.Tracks)
// {
// var resource = track.ToResource();
// resource.Grabbed = true;
BroadcastResourceChange(ModelAction.Updated, resource);
}
}
public void Handle(AlbumEditedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Album.Id);
}
// BroadcastResourceChange(ModelAction.Updated, resource);
// }
//}
public void Handle(TrackImportedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.ImportedTrack.AlbumId);
}
//public void Handle(TrackDownloadedEvent message)
//{

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Music;
namespace Lidarr.Api.V1.Albums
{
public class AlbumReleaseResource
{
public string Id { get; set; }
public DateTime? ReleaseDate { get; set; }
public int TrackCount { get; set; }
public int MediaCount { get; set; }
public string Disambiguation { get; set; }
public List<string> Country { get; set; }
public List<string> Label { get; set; }
public string Format { get; set; }
}
public static class AlbumReleaseResourceMapper
{
public static AlbumReleaseResource ToResource(this AlbumRelease model)
{
if (model == null)
{
return null;
}
return new AlbumReleaseResource
{
Id = model.Id,
ReleaseDate = model.ReleaseDate,
TrackCount = model.TrackCount,
MediaCount = model.MediaCount,
Disambiguation = model.Disambiguation,
Country = model.Country,
Label = model.Label,
Format = model.Format
};
}
public static AlbumRelease ToModel(this AlbumReleaseResource resource)
{
if (resource == null)
{
return null;
}
return new AlbumRelease
{
Id = resource.Id,
ReleaseDate = resource.ReleaseDate,
TrackCount = resource.TrackCount,
MediaCount = resource.MediaCount,
Disambiguation = resource.Disambiguation,
Country = resource.Country,
Label = resource.Label,
Format = resource.Format
};
}
public static List<AlbumReleaseResource> ToResource(this IEnumerable<AlbumRelease> models)
{
return models.Select(ToResource).ToList();
}
public static List<AlbumRelease> ToModel(this IEnumerable<AlbumReleaseResource> resources)
{
return resources.Select(ToModel).ToList();
}
}
}

View File

@@ -19,6 +19,7 @@ namespace Lidarr.Api.V1.Albums
public int ProfileId { get; set; }
public int Duration { get; set; }
public string AlbumType { get; set; }
public List<string> SecondaryTypes { get; set; }
public int MediumCount
{
get
@@ -33,6 +34,8 @@ namespace Lidarr.Api.V1.Albums
}
public Ratings Ratings { get; set; }
public DateTime? ReleaseDate { get; set; }
public AlbumRelease CurrentRelease { get; set; }
public List<AlbumReleaseResource> Releases { get; set; }
public List<string> Genres { get; set; }
public List<MediumResource> Media { get; set; }
public ArtistResource Artist { get; set; }
@@ -65,15 +68,45 @@ namespace Lidarr.Api.V1.Albums
Ratings = model.Ratings,
Duration = model.Duration,
AlbumType = model.AlbumType,
SecondaryTypes = model.SecondaryTypes.Select(s => s.Name).ToList(),
Media = model.Media.ToResource(),
CurrentRelease = model.CurrentRelease,
Releases = model.Releases.ToResource(),
};
}
public static Album ToModel(this AlbumResource resource)
{
if (resource == null) return null;
return new Album
{
Id = resource.Id,
ForeignAlbumId = resource.ForeignAlbumId,
Title = resource.Title,
Images = resource.Images,
Monitored = resource.Monitored,
CurrentRelease = resource.CurrentRelease
};
}
public static Album ToModel(this AlbumResource resource, Album album)
{
var updatedAlbum = resource.ToModel();
album.ApplyChanges(updatedAlbum);
return album;
}
public static List<AlbumResource> ToResource(this IEnumerable<Album> models)
{
if (models == null) return null;
return models?.Select(ToResource).ToList();
}
return models.Select(ToResource).ToList();
public static List<Album> ToModel(this IEnumerable<AlbumResource> resources)
{
return resources.Select(ToModel).ToList();
}
}
}

View File

@@ -11,7 +11,7 @@ namespace Lidarr.Api.V1.Albums
public string MediumFormat { get; set; }
}
public static class SeasonResourceMapper
public static class MediumResourceMapper
{
public static MediumResource ToResource(this Medium model)
{

View File

@@ -34,6 +34,7 @@ namespace Lidarr.Api.V1.Artist
{
private readonly IArtistService _artistService;
private readonly IAlbumService _albumService;
private readonly IAddArtistService _addArtistService;
private readonly IArtistStatisticsService _artistStatisticsService;
private readonly IMapCoversToLocal _coverMapper;
@@ -41,6 +42,7 @@ namespace Lidarr.Api.V1.Artist
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
IArtistService artistService,
IAlbumService albumService,
IAddArtistService addArtistService,
IArtistStatisticsService artistStatisticsService,
IMapCoversToLocal coverMapper,
@@ -57,6 +59,7 @@ namespace Lidarr.Api.V1.Artist
: base(signalRBroadcaster)
{
_artistService = artistService;
_albumService = albumService;
_addArtistService = addArtistService;
_artistStatisticsService = artistStatisticsService;
@@ -105,6 +108,7 @@ namespace Lidarr.Api.V1.Artist
var resource = artist.ToResource();
MapCoversToLocal(resource);
FetchAndLinkArtistStatistics(resource);
LinkNextPreviousAlbums(resource);
//PopulateAlternateTitles(resource);
return resource;
@@ -116,7 +120,7 @@ namespace Lidarr.Api.V1.Artist
var artistsResources = _artistService.GetAllArtists().ToResource();
MapCoversToLocal(artistsResources.ToArray());
//MapAlbums(artistsResources.ToArray());
LinkNextPreviousAlbums(artistsResources.ToArray());
LinkArtistStatistics(artistsResources, artistStats);
//PopulateAlternateTitles(seriesResources);
@@ -171,6 +175,16 @@ namespace Lidarr.Api.V1.Artist
}
}
private void LinkNextPreviousAlbums(params ArtistResource[] artists)
{
foreach (var artistResource in artists)
{
var artistAlbums = _albumService.GetAlbumsByArtist(artistResource.Id).OrderBy(s=>s.ReleaseDate);
artistResource.NextAlbum = artistAlbums.Where(s => s.ReleaseDate >= DateTime.UtcNow && s.Monitored).FirstOrDefault();
artistResource.LastAlbum = artistAlbums.Where(s => s.ReleaseDate <= DateTime.UtcNow && s.Monitored).LastOrDefault();
}
}
private void FetchAndLinkArtistStatistics(ArtistResource resource)
{
LinkArtistStatistics(resource, _artistStatisticsService.ArtistStatistics(resource.Id));
@@ -195,13 +209,6 @@ namespace Lidarr.Api.V1.Artist
resource.SizeOnDisk = artistStatistics.SizeOnDisk;
resource.AlbumCount = artistStatistics.AlbumCount;
if (artistStatistics.AlbumStatistics != null)
{
foreach (var album in resource.Albums)
{
album.Statistics = artistStatistics.AlbumStatistics.SingleOrDefault(s => s.AlbumId == album.Id).ToResource();
}
}
}
//private void PopulateAlternateTitles(List<ArtistResource> resources)

View File

@@ -36,13 +36,13 @@ namespace Lidarr.Api.V1.Artist
public int? TrackCount { get; set; }
public int? TrackFileCount { get; set; }
public long? SizeOnDisk { get; set; }
//public SeriesStatusType Status { get; set; }
public Album NextAlbum { get; set; }
public Album LastAlbum { get; set; }
public List<MediaCover> Images { get; set; }
public List<Member> Members { get; set; }
public string RemotePoster { get; set; }
public List<AlbumResource> Albums { get; set; }
//View & Edit
@@ -89,8 +89,6 @@ namespace Lidarr.Api.V1.Artist
Images = model.Images,
Albums = model.Albums.ToResource(),
Path = model.Path,
QualityProfileId = model.ProfileId,
LanguageProfileId = model.LanguageProfileId,
@@ -128,8 +126,6 @@ namespace Lidarr.Api.V1.Artist
Status = resource.Status,
Overview = resource.Overview,
//NextAiring
//PreviousAiring
Images = resource.Images,

View File

@@ -82,6 +82,7 @@
</Compile>
<Compile Include="Albums\AlbumModule.cs" />
<Compile Include="Albums\AlbumModuleWithSignalR.cs" />
<Compile Include="Albums\AlbumReleaseResource.cs" />
<Compile Include="Albums\AlbumResource.cs" />
<Compile Include="Albums\AlbumsMonitoredResource.cs" />
<Compile Include="Albums\AlbumStatisticsResource.cs" />

View File

@@ -12,7 +12,7 @@ namespace Lidarr.Api.V1.TrackFiles
public static class MediaInfoResourceMapper
{
public static MediaInfoResource ToResource(this MediaInfoModel model, string sceneName)
public static MediaInfoResource ToResource(this MediaInfoModel model)
{
if (model == null)
{

View File

@@ -80,8 +80,9 @@ namespace Lidarr.Api.V1.TrackFiles
{
int albumId = Convert.ToInt32(albumIdQuery.Value);
var album = _albumService.GetAlbum(albumId);
var albumArtist = _artistService.GetArtist(album.ArtistId);
return _mediaFileService.GetFilesByAlbum(album.ArtistId, album.Id).ConvertAll(f => f.ToResource(album.Artist, _upgradableSpecification));
return _mediaFileService.GetFilesByAlbum(album.Id).ConvertAll(f => f.ToResource(albumArtist, _upgradableSpecification));
}
else

View File

@@ -45,7 +45,7 @@ namespace Lidarr.Api.V1.TrackFiles
// SceneName = model.SceneName,
Language = model.Language,
Quality = model.Quality,
MediaInfo = model.MediaInfo.ToResource(model.SceneName)
MediaInfo = model.MediaInfo.ToResource()
//QualityCutoffNotMet
};
@@ -68,7 +68,7 @@ namespace Lidarr.Api.V1.TrackFiles
//SceneName = model.SceneName,
Language = model.Language,
Quality = model.Quality,
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
MediaInfo = model.MediaInfo.ToResource(),
QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(artist.Profile.Value, model.Quality),
LanguageCutoffNotMet = upgradableSpecification.LanguageCutoffNotMet(artist.LanguageProfile.Value, model.Language)

View File

@@ -2,20 +2,20 @@ using System.Collections.Generic;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Music;
using NzbDrone.Core.Music.Events;
using NzbDrone.SignalR;
using Lidarr.Api.V1.TrackFiles;
using Lidarr.Api.V1.Artist;
using Lidarr.Http;
using NzbDrone.Core.MediaFiles.Events;
namespace Lidarr.Api.V1.Tracks
{
public abstract class TrackModuleWithSignalR : LidarrRestModuleWithSignalR<TrackResource, Track>
//IHandle<EpisodeGrabbedEvent>,
//IHandle<EpisodeImportedEvent>
public abstract class TrackModuleWithSignalR : LidarrRestModuleWithSignalR<TrackResource, Track>,
IHandle<TrackInfoRefreshedEvent>,
IHandle<TrackImportedEvent>
{
protected readonly ITrackService _trackService;
protected readonly IArtistService _artistService;
@@ -31,24 +31,24 @@ namespace Lidarr.Api.V1.Tracks
_artistService = artistService;
_upgradableSpecification = upgradableSpecification;
GetResourceById = GetEpisode;
GetResourceById = GetTrack;
}
protected TrackModuleWithSignalR(ITrackService episodeService,
IArtistService seriesService,
protected TrackModuleWithSignalR(ITrackService trackService,
IArtistService artistService,
IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster,
string resource)
: base(signalRBroadcaster, resource)
{
_trackService = episodeService;
_artistService = seriesService;
_trackService = trackService;
_artistService = artistService;
_upgradableSpecification = upgradableSpecification;
GetResourceById = GetEpisode;
GetResourceById = GetTrack;
}
protected TrackResource GetEpisode(int id)
protected TrackResource GetTrack(int id)
{
var episode = _trackService.GetTrack(id);
var resource = MapToResource(episode, true, true);
@@ -76,28 +76,28 @@ namespace Lidarr.Api.V1.Tracks
return resource;
}
protected List<TrackResource> MapToResource(List<Track> episodes, bool includeSeries, bool includeEpisodeFile)
protected List<TrackResource> MapToResource(List<Track> tracks, bool includeArtist, bool includeTrackFile)
{
var result = episodes.ToResource();
var result = tracks.ToResource();
if (includeSeries || includeEpisodeFile)
if (includeArtist || includeTrackFile)
{
var seriesDict = new Dictionary<int, NzbDrone.Core.Music.Artist>();
for (var i = 0; i < episodes.Count; i++)
var artistDict = new Dictionary<int, NzbDrone.Core.Music.Artist>();
for (var i = 0; i < tracks.Count; i++)
{
var episode = episodes[i];
var track = tracks[i];
var resource = result[i];
var series = episode.Artist ?? seriesDict.GetValueOrDefault(episodes[i].ArtistId) ?? _artistService.GetArtist(episodes[i].ArtistId);
seriesDict[series.Id] = series;
var series = track.Artist ?? artistDict.GetValueOrDefault(tracks[i].ArtistId) ?? _artistService.GetArtist(tracks[i].ArtistId);
artistDict[series.Id] = series;
if (includeSeries)
if (includeArtist)
{
resource.Artist = series.ToResource();
}
if (includeEpisodeFile && episodes[i].TrackFileId != 0)
if (includeTrackFile && tracks[i].TrackFileId != 0)
{
resource.TrackFile = episodes[i].TrackFile.Value.ToResource(series, _upgradableSpecification);
resource.TrackFile = tracks[i].TrackFile.Value.ToResource(series, _upgradableSpecification);
}
}
}
@@ -105,28 +105,31 @@ namespace Lidarr.Api.V1.Tracks
return result;
}
//public void Handle(EpisodeGrabbedEvent message)
//{
// foreach (var episode in message.Episode.Episodes)
// {
// var resource = episode.ToResource();
// resource.Grabbed = true;
public void Handle(TrackInfoRefreshedEvent message)
{
foreach (var track in message.Removed)
{
BroadcastResourceChange(ModelAction.Deleted, track.ToResource());
}
// BroadcastResourceChange(ModelAction.Updated, resource);
// }
//}
foreach (var track in message.Added)
{
BroadcastResourceChange(ModelAction.Updated, track.ToResource());
}
//public void Handle(EpisodeImportedEvent message)
//{
// if (!message.NewDownload)
// {
// return;
// }
foreach (var track in message.Updated)
{
BroadcastResourceChange(ModelAction.Updated, track.Id);
}
}
public void Handle(TrackImportedEvent message)
{
foreach (var track in message.TrackInfo.Tracks)
{
BroadcastResourceChange(ModelAction.Updated, track.Id);
}
}
// foreach (var episode in message.EpisodeInfo.Episodes)
// {
// BroadcastResourceChange(ModelAction.Updated, episode.Id);
// }
//}
}
}

View File

@@ -16,7 +16,7 @@ namespace NzbDrone.Common.Cloud
Services = new HttpRequestBuilder("https://services.lidarr.audio/v1/")
.CreateFactory();
Search = new HttpRequestBuilder("https://api.lidarr.audio/api/v0/{route}/") // TODO: Add {version} once LidarrAPI.Metadata is released.
Search = new HttpRequestBuilder("https://api.lidarr.audio/api/v0.3/{route}")
.CreateFactory();
}

View File

@@ -71,7 +71,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_remoteAlbum.Albums = Builder<Album>.CreateListOfSize(1).Build().ToList();
Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesByAlbum(It.IsAny<int>(), It.IsAny<int>()))
.Setup(s => s.GetFilesByAlbum(It.IsAny<int>()))
.Returns(new List<TrackFile> { });
Mocker.GetMock<IDelayProfileService>()
@@ -86,7 +86,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
private void GivenExistingFile(QualityModel quality, Language language)
{
Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesByAlbum(It.IsAny<int>(), It.IsAny<int>()))
.Setup(s => s.GetFilesByAlbum(It.IsAny<int>()))
.Returns(new List<TrackFile> { new TrackFile {
Quality = quality,
Language = language

View File

@@ -91,7 +91,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
private void SetupMediaFile(List<TrackFile> files)
{
Mocker.GetMock<IMediaFileService>()
.Setup(v => v.GetFilesByAlbum(It.IsAny<int>(), It.IsAny<int>()))
.Setup(v => v.GetFilesByAlbum(It.IsAny<int>()))
.Returns(files);
}

View File

@@ -44,7 +44,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
.Build();
Mocker.GetMock<IMediaFileService>()
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>(), It.IsAny<int>()))
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>()))
.Returns(new List<TrackFile> { _firstFile, _secondFile });
_parseResultMulti = new RemoteAlbum

View File

@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Build();
Mocker.GetMock<IMediaFileService>()
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>(), It.IsAny<int>()))
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>()))
.Returns(new List<TrackFile> { _firstFile, _secondFile });
_parseResultMulti = new RemoteAlbum
@@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_return_true_if_album_has_no_existing_file()
{
Mocker.GetMock<IMediaFileService>()
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>(), It.IsAny<int>()))
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>()))
.Returns(new List<TrackFile> { });
Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue();

View File

@@ -0,0 +1,15 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(9)]
public class album_releases : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Albums").AddColumn("Releases").AsString().WithDefaultValue("").Nullable();
Alter.Table("Albums").AddColumn("CurrentRelease").AsString().WithDefaultValue("").Nullable();
}
}
}

View File

@@ -30,7 +30,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
foreach (var album in subject.Albums)
{
var trackFiles = _mediaFileService.GetFilesByAlbum(album.ArtistId, album.Id);
var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);
if (trackFiles.Any())
{

View File

@@ -61,7 +61,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
{
foreach (var album in subject.Albums)
{
var trackFiles = _mediaFileService.GetFilesByAlbum(album.ArtistId, album.Id);
var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);
if (trackFiles.Any())
{

View File

@@ -46,7 +46,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
}
var missingTrackFiles = subject.Albums
.SelectMany(v => _albumService.GetFilesByAlbum(v.ArtistId, v.Id))
.SelectMany(v => _albumService.GetFilesByAlbum(v.Id))
.DistinctBy(v => v.Id)
.Where(v => IsTrackFileMissing(subject.Artist, v))
.ToArray();

View File

@@ -35,7 +35,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
foreach (var album in subject.Albums)
{
var trackFiles = _mediaFileService.GetFilesByAlbum(album.ArtistId, album.Id);
var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);
if (trackFiles.Any())
{

View File

@@ -28,7 +28,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
foreach (var album in subject.Albums)
{
var trackFiles = _mediaFileService.GetFilesByAlbum(album.ArtistId, album.Id);
var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);
if (trackFiles.Any())
{

View File

@@ -0,0 +1,31 @@
using NzbDrone.Common.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Exceptions
{
public class AlbumNotFoundException : NzbDroneException
{
public string MusicBrainzId { get; set; }
public AlbumNotFoundException(string musicbrainzId)
: base(string.Format("Album with MusicBrainz {0} was not found, it may have been removed from MusicBrainz.", musicbrainzId))
{
MusicBrainzId = musicbrainzId;
}
public AlbumNotFoundException(string musicbrainzId, string message, params object[] args)
: base(message, args)
{
MusicBrainzId = musicbrainzId;
}
public AlbumNotFoundException(string musicbrainzId, string message)
: base(message)
{
MusicBrainzId = musicbrainzId;
}
}
}

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Core.MediaFiles
void Update(List<TrackFile> trackFile);
void Delete(TrackFile trackFile, DeleteMediaFileReason reason);
List<TrackFile> GetFilesByArtist(int artistId);
List<TrackFile> GetFilesByAlbum(int artistId, int albumId);
List<TrackFile> GetFilesByAlbum(int albumId);
List<TrackFile> GetFiles(IEnumerable<int> ids);
List<TrackFile> GetFilesWithoutMediaInfo();
List<string> FilterExistingFiles(List<string> files, Artist artist);
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.MediaFiles
}
public class MediaFileService : IMediaFileService, IHandleAsync<ArtistDeletedEvent>
public class MediaFileService : IMediaFileService, IHandleAsync<ArtistDeletedEvent>, IHandleAsync<AlbumDeletedEvent>
{
private readonly IEventAggregator _eventAggregator;
private readonly IMediaFileRepository _mediaFileRepository;
@@ -104,12 +104,18 @@ namespace NzbDrone.Core.MediaFiles
_mediaFileRepository.DeleteMany(files);
}
public void HandleAsync(AlbumDeletedEvent message)
{
var files = GetFilesByAlbum(message.Album.Id);
_mediaFileRepository.DeleteMany(files);
}
public List<TrackFile> GetFilesByArtist(int artistId)
{
return _mediaFileRepository.GetFilesByArtist(artistId);
}
public List<TrackFile> GetFilesByAlbum(int artistId, int albumId)
public List<TrackFile> GetFilesByAlbum(int albumId)
{
return _mediaFileRepository.GetFilesByAlbum(albumId);
}

View File

@@ -72,7 +72,7 @@ namespace NzbDrone.Core.MediaFiles
var artist = _artistService.GetArtist(artistId);
var tracks = _trackService.GetTracksByAlbum(albumId);
var files = _mediaFileService.GetFilesByAlbum(artistId, albumId);
var files = _mediaFileService.GetFilesByAlbum(albumId);
return GetPreviews(artist, tracks, files)
.OrderByDescending(e => e.TrackNumbers.First()).ToList();

View File

@@ -0,0 +1,11 @@
using NzbDrone.Core.Music;
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.MetadataSource
{
public interface IProvideAlbumInfo
{
Tuple<Album, List<Track>> GetAlbumInfo(string lidarrId, string releaseId);
}
}

View File

@@ -25,6 +25,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public List<string> SecondaryTypes { get; set; }
public List<MediumResource> Media { get; set; }
public List<TrackResource> Tracks { get; set; }
public List<ReleaseResource> Releases { get; set; }
public string SelectedRelease { get; set; }
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{
public class ReleaseResource
{
public string Id { get; set; }
public DateTime ReleaseDate { get; set; }
public int MediaCount { get; set; }
public int TrackCount { get; set; }
public string Disambiguation { get; set; }
public List<string> Label {get; set;}
public List<string> Country { get; set; }
public string Format { get; set; }
}
}

View File

@@ -9,16 +9,13 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
using Newtonsoft.Json.Linq;
using NzbDrone.Core.Music;
using Newtonsoft.Json;
using NzbDrone.Core.Configuration;
using System.Text.RegularExpressions;
using NzbDrone.Core.Profiles.Metadata;
namespace NzbDrone.Core.MetadataSource.SkyHook
{
public class SkyHookProxy : IProvideArtistInfo, ISearchForNewArtist
public class SkyHookProxy : IProvideArtistInfo, ISearchForNewArtist, IProvideAlbumInfo
{
private readonly IHttpClient _httpClient;
private readonly Logger _logger;
@@ -28,9 +25,13 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
private readonly IConfigService _configService;
private readonly IMetadataProfileService _metadataProfileService;
private IHttpRequestBuilderFactory customerRequestBuilder;
private IHttpRequestBuilderFactory _customerRequestBuilder;
public SkyHookProxy(IHttpClient httpClient, ILidarrCloudRequestBuilder requestBuilder, IArtistService artistService, Logger logger, IConfigService configService, IMetadataProfileService metadataProfileService)
public SkyHookProxy(IHttpClient httpClient,
ILidarrCloudRequestBuilder requestBuilder,
IArtistService artistService, Logger logger,
IConfigService configService,
IMetadataProfileService metadataProfileService)
{
_httpClient = httpClient;
_configService = configService;
@@ -52,8 +53,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
var primaryTypes = metadataProfile.PrimaryAlbumTypes.Where(s => s.Allowed).Select(s => s.PrimaryAlbumType.Name);
var secondaryTypes = metadataProfile.SecondaryAlbumTypes.Where(s => s.Allowed).Select(s => s.SecondaryAlbumType.Name);
var httpRequest = customerRequestBuilder.Create()
.SetSegment("route", "artists/" + foreignArtistId)
var httpRequest = _customerRequestBuilder.Create()
.SetSegment("route", "artist/" + foreignArtistId)
.AddQueryParam("primTypes", string.Join("|", primaryTypes))
.AddQueryParam("secTypes", string.Join("|", secondaryTypes))
.Build();
@@ -86,16 +87,55 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
return new Tuple<Artist, List<Album>>(artist, albums.ToList());
}
public Tuple<Album, List<Track>> GetAlbumInfo(string foreignAlbumId, string releaseId)
{
_logger.Debug("Getting Album with LidarrAPI.MetadataID of {0}", foreignAlbumId);
SetCustomProvider();
var httpRequest = _customerRequestBuilder.Create()
.SetSegment("route", "album/" + foreignAlbumId)
.AddQueryParam("release", releaseId ?? string.Empty)
.Build();
httpRequest.AllowAutoRedirect = true;
httpRequest.SuppressHttpError = true;
var httpResponse = _httpClient.Get<AlbumResource>(httpRequest);
if (httpResponse.HasHttpError)
{
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{
throw new AlbumNotFoundException(foreignAlbumId);
}
else if (httpResponse.StatusCode == HttpStatusCode.BadRequest)
{
throw new BadRequestException(foreignAlbumId);
}
else
{
throw new HttpException(httpRequest, httpResponse);
}
}
var tracks = httpResponse.Resource.Tracks.Select(MapTrack);
var album = MapAlbum(httpResponse.Resource);
return new Tuple<Album, List<Track>>(album, tracks.ToList());
}
public List<Artist> SearchForNewArtist(string title)
{
try
{
var lowerTitle = title.ToLowerInvariant();
Console.WriteLine("Searching for " + lowerTitle);
if (lowerTitle.StartsWith("lidarr:") || lowerTitle.StartsWith("lidarrid:"))
{
var slug = lowerTitle.Split(':')[1].Trim();
Guid searchGuid;
bool isValid = Guid.TryParse(slug, out searchGuid);
@@ -118,10 +158,12 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
SetCustomProvider();
var httpRequest = customerRequestBuilder.Create()
var httpRequest = _customerRequestBuilder.Create()
.SetSegment("route", "search")
.AddQueryParam("type", "artist")
.AddQueryParam("query", title.ToLower().Trim())
//.AddQueryParam("images","false") // Should pass these on import search to avoid looking to fanart and wiki
//.AddQueryParam("overview","false")
.Build();
@@ -143,12 +185,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
private Artist MapSearhResult(ArtistResource resource)
{
var artist = _artistService.FindById(resource.Id);
if (artist == null)
{
artist = MapArtist(resource);
}
var artist = _artistService.FindById(resource.Id) ?? MapArtist(resource);
return artist;
}
@@ -161,37 +198,66 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
album.ReleaseDate = resource.ReleaseDate;
album.CleanTitle = Parser.Parser.CleanArtistName(album.Title);
album.AlbumType = resource.Type;
album.Images = resource.Images.Select(MapImage).ToList();
album.Label = resource.Labels;
if (resource.Images != null)
{
album.Images = resource.Images.Select(MapImage).ToList();
}
album.Label = resource.Labels;
album.Media = resource.Media.Select(MapMedium).ToList();
album.Tracks = resource.Tracks.Select(MapTrack).ToList();
album.SecondaryTypes = resource.SecondaryTypes.Select(MapSecondaryTypes).ToList();
if (resource.Releases != null)
{
album.Releases = resource.Releases.Select(MapAlbumRelease).ToList();
album.CurrentRelease = album.Releases.FirstOrDefault(s => s.Id == resource.SelectedRelease);
}
return album;
}
private static Medium MapMedium(MediumResource resource)
{
Medium medium = new Medium();
medium.Name = resource.Name;
medium.Number = resource.Position;
medium.Format = resource.Format;
Medium medium = new Medium
{
Name = resource.Name,
Number = resource.Position,
Format = resource.Format
};
return medium;
}
private static AlbumRelease MapAlbumRelease(ReleaseResource resource)
{
AlbumRelease albumRelease = new AlbumRelease
{
Id = resource.Id,
ReleaseDate = resource.ReleaseDate,
TrackCount = resource.TrackCount,
Format = resource.Format,
MediaCount = resource.MediaCount,
Country = resource.Country,
Disambiguation = resource.Disambiguation,
Label = resource.Label
};
return albumRelease;
}
private static Track MapTrack(TrackResource resource)
{
Track track = new Track();
track.Title = resource.TrackName;
track.ForeignTrackId = resource.Id;
track.TrackNumber = resource.TrackNumber;
track.AbsoluteTrackNumber = resource.TrackPosition;
track.Duration = resource.DurationMs;
track.MediumNumber = resource.MediumNumber;
Track track = new Track
{
Title = resource.TrackName,
ForeignTrackId = resource.Id,
TrackNumber = resource.TrackNumber,
AbsoluteTrackNumber = resource.TrackPosition,
Duration = resource.DurationMs,
MediumNumber = resource.MediumNumber
};
return track;
}
@@ -213,7 +279,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
artist.Status = MapArtistStatus(resource.Status);
artist.Ratings = MapRatings(resource.Rating);
artist.Links = resource.Links.Select(MapLink).ToList();
return artist;
}
@@ -251,14 +316,14 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
return ArtistStatusType.Continuing;
}
private static Music.Ratings MapRatings(RatingResource rating)
private static Ratings MapRatings(RatingResource rating)
{
if (rating == null)
{
return new Music.Ratings();
return new Ratings();
}
return new Music.Ratings
return new Ratings
{
Votes = rating.Count,
Value = rating.Value
@@ -274,9 +339,9 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
};
}
private static Music.Links MapLink(LinkResource arg)
private static Links MapLink(LinkResource arg)
{
return new Music.Links
return new Links
{
Url = arg.Target,
Name = arg.Type
@@ -337,11 +402,11 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
{
if (_configService.MetadataSource.IsNotNullOrWhiteSpace())
{
customerRequestBuilder = new HttpRequestBuilder(_configService.MetadataSource.TrimEnd("/") + "/{route}/").CreateFactory();
_customerRequestBuilder = new HttpRequestBuilder(_configService.MetadataSource.TrimEnd("/") + "/{route}").CreateFactory();
}
else
{
customerRequestBuilder = _requestBuilder;
_customerRequestBuilder = _requestBuilder;
}
}
}

View File

@@ -39,10 +39,26 @@ namespace NzbDrone.Core.Music
public AddArtistOptions AddOptions { get; set; }
public Artist Artist { get; set; }
public Ratings Ratings { get; set; }
public List<AlbumRelease> Releases { get; set; }
public AlbumRelease CurrentRelease { get; set; }
public override string ToString()
{
return string.Format("[{0}][{1}]", ForeignAlbumId, Title.NullSafe());
}
public void ApplyChanges(Album otherAlbum)
{
ForeignAlbumId = otherAlbum.ForeignAlbumId;
Tracks = otherAlbum.Tracks;
ProfileId = otherAlbum.ProfileId;
AddOptions = otherAlbum.AddOptions;
Monitored = otherAlbum.Monitored;
CurrentRelease = otherAlbum.CurrentRelease;
}
}
}

View File

@@ -0,0 +1,25 @@
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Music.Commands;
using NzbDrone.Core.Music.Events;
namespace NzbDrone.Core.Music
{
public class AlbumEditedService : IHandle<AlbumEditedEvent>
{
private readonly IManageCommandQueue _commandQueueManager;
public AlbumEditedService(IManageCommandQueue commandQueueManager)
{
_commandQueueManager = commandQueueManager;
}
public void Handle(AlbumEditedEvent message)
{
if (message.Album.CurrentRelease.Id != message.OldAlbum.CurrentRelease.Id)
{
_commandQueueManager.Push(new RefreshAlbumCommand(message.Album.Id));
}
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Music
{
public class AlbumRelease : IEmbeddedDocument
{
public string Id { get; set; }
public DateTime? ReleaseDate { get; set; }
public int TrackCount { get; set; }
public int MediaCount { get; set; }
public string Disambiguation { get; set; }
public List<string> Country { get; set; }
public string Format { get; set; }
public List<string> Label { get; set; }
}
}

View File

@@ -19,6 +19,7 @@ namespace NzbDrone.Core.Music
List<Album> GetAlbums(IEnumerable<int> albumIds);
List<Album> GetAlbumsByArtist(int artistId);
Album AddAlbum(Album newAlbum);
List<Album> AddAlbums(List<Album> newAlbums);
Album FindById(string spotifyId);
Album FindByTitle(int artistId, string title);
Album FindByTitleInexact(string title);
@@ -61,11 +62,19 @@ namespace NzbDrone.Core.Music
public Album AddAlbum(Album newAlbum)
{
_albumRepository.Insert(newAlbum);
_eventAggregator.PublishEvent(new AlbumAddedEvent(GetAlbum(newAlbum.Id)));
//_eventAggregator.PublishEvent(new AlbumAddedEvent(GetAlbum(newAlbum.Id)));
return newAlbum;
}
public List<Album> AddAlbums(List<Album> newAlbums)
{
_albumRepository.InsertMany(newAlbums);
//_eventAggregator.PublishEvent(new AlbumsAddedEvent(newAlbums.Select(s => s.Id).ToList()));
return newAlbums;
}
public void DeleteAlbum(int albumId, bool deleteFiles)
{
var album = _albumRepository.Get(albumId);
@@ -140,11 +149,16 @@ namespace NzbDrone.Core.Music
public void DeleteMany(List<Album> albums)
{
_albumRepository.DeleteMany(albums);
foreach (var album in albums)
{
_eventAggregator.PublishEvent(new AlbumDeletedEvent(album, false));
}
}
public Album UpdateAlbum(Album album)
{
var storedAlbum = GetAlbum(album.Id); // Is it Id or iTunesId?
var storedAlbum = GetAlbum(album.Id);
var updatedAlbum = _albumRepository.Update(album);
_eventAggregator.PublishEvent(new AlbumEditedEvent(updatedAlbum, storedAlbum));

View File

@@ -0,0 +1,22 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Music.Commands
{
public class RefreshAlbumCommand : Command
{
public int? AlbumId { get; set; }
public RefreshAlbumCommand()
{
}
public RefreshAlbumCommand(int? albumId)
{
AlbumId = albumId;
}
public override bool SendUpdatesToClient => true;
public override bool UpdateScheduledTask => !AlbumId.HasValue;
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Music.Events
{
public class AlbumsAddedEvent : IEvent
{
public List<int> AlbumIds { get; private set; }
public AlbumsAddedEvent(List<int> albumIds)
{
AlbumIds = albumIds;
}
}
}

View File

@@ -1,4 +1,4 @@
using NzbDrone.Common.Messaging;
using NzbDrone.Common.Messaging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -12,12 +12,14 @@ namespace NzbDrone.Core.Music.Events
public Album Album { get; set; }
public ReadOnlyCollection<Track> Added { get; private set; }
public ReadOnlyCollection<Track> Updated { get; private set; }
public ReadOnlyCollection<Track> Removed { get; private set; }
public TrackInfoRefreshedEvent(Album album, IList<Track> added, IList<Track> updated)
public TrackInfoRefreshedEvent(Album album, IList<Track> added, IList<Track> updated, IList<Track> removed)
{
Album = album;
Added = new ReadOnlyCollection<Track>(added);
Updated = new ReadOnlyCollection<Track>(updated);
Removed = new ReadOnlyCollection<Track>(removed);
}
}
}

View File

@@ -7,31 +7,104 @@ using System.Collections.Generic;
using NzbDrone.Core.Organizer;
using System.Linq;
using System.Text;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Music.Commands;
namespace NzbDrone.Core.Music
{
public interface IRefreshAlbumService
{
void RefreshAlbumInfo(Artist artist, IEnumerable<Album> remoteAlbums);
void RefreshAlbumInfo(Album album);
}
public class RefreshAlbumService : IRefreshAlbumService
public class RefreshAlbumService : IRefreshAlbumService, IExecute<RefreshAlbumCommand>
{
private readonly IAlbumService _albumService;
private readonly IArtistService _artistService;
private readonly IProvideAlbumInfo _albumInfo;
private readonly IRefreshTrackService _refreshTrackService;
private readonly ITrackService _trackService;
private readonly IBuildFileNames _fileNameBuilder;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
public RefreshAlbumService(IAlbumService albumService, IBuildFileNames fileNameBuilder, IRefreshTrackService refreshTrackService, IEventAggregator eventAggregator, Logger logger)
public RefreshAlbumService(IAlbumService albumService,
IArtistService artistService,
IProvideAlbumInfo albumInfo,
IRefreshTrackService refreshTrackService,
ITrackService trackService,
IBuildFileNames fileNameBuilder,
IEventAggregator eventAggregator,
Logger logger)
{
_albumService = albumService;
_fileNameBuilder = fileNameBuilder;
_artistService = artistService;
_albumInfo = albumInfo;
_refreshTrackService = refreshTrackService;
_trackService = trackService;
_fileNameBuilder = fileNameBuilder;
_eventAggregator = eventAggregator;
_logger = logger;
}
public void RefreshAlbumInfo(Album album)
{
_logger.ProgressInfo("Updating Info for {0}", album.Title);
Tuple<Album, List<Track>> tuple;
try
{
tuple = _albumInfo.GetAlbumInfo(album.ForeignAlbumId, album.CurrentRelease.Id);
}
catch (AlbumNotFoundException)
{
_logger.Error(
"Album '{0}' (LidarrAPI {1}) was not found, it may have been removed from Metadata sources.",
album.Title, album.ForeignAlbumId);
return;
}
var albumInfo = tuple.Item1;
if (album.ForeignAlbumId != albumInfo.ForeignAlbumId)
{
_logger.Warn(
"Album '{0}' (Album {1}) was replaced with '{2}' (LidarrAPI {3}), because the original was a duplicate.",
album.Title, album.ForeignAlbumId, albumInfo.Title, albumInfo.ForeignAlbumId);
album.ForeignAlbumId = albumInfo.ForeignAlbumId;
}
album.LastInfoSync = DateTime.UtcNow;
album.CleanTitle = albumInfo.CleanTitle;
album.Title = albumInfo.Title ?? "Unknown";
album.CleanTitle = Parser.Parser.CleanArtistName(album.Title);
album.AlbumType = albumInfo.AlbumType;
album.SecondaryTypes = albumInfo.SecondaryTypes;
album.Genres = albumInfo.Genres;
album.Media = albumInfo.Media;
album.Label = albumInfo.Label;
album.Images = albumInfo.Images;
album.ReleaseDate = albumInfo.ReleaseDate;
album.Duration = tuple.Item2.Sum(track => track.Duration);
album.Releases = albumInfo.Releases;
_refreshTrackService.RefreshTrackInfo(album, tuple.Item2);
_albumService.UpdateAlbum(album);
}
public void RefreshAlbumInfo(Artist artist, IEnumerable<Album> remoteAlbums)
{
_logger.Info("Starting album info refresh for: {0}", artist);
@@ -42,67 +115,80 @@ namespace NzbDrone.Core.Music
var updateList = new List<Album>();
var newList = new List<Album>();
var dupeFreeRemoteAlbums = remoteAlbums.DistinctBy(m => new { m.ForeignAlbumId, m.ReleaseDate }).ToList();
var dupeFreeRemoteAlbums = remoteAlbums.DistinctBy(m => new {m.ForeignAlbumId, m.ReleaseDate}).ToList();
foreach (var album in OrderAlbums(artist, dupeFreeRemoteAlbums))
{
try
{
var albumToUpdate = GetAlbumToUpdate(artist, album, existingAlbums);
Tuple<Album, List<Track>> tuple;
var albumInfo = new Album();
if (albumToUpdate != null)
{
tuple = _albumInfo.GetAlbumInfo(album.ForeignAlbumId, albumToUpdate.CurrentRelease.Id);
albumInfo = tuple.Item1;
existingAlbums.Remove(albumToUpdate);
updateList.Add(albumToUpdate);
}
else
{
albumToUpdate = new Album();
albumToUpdate.Monitored = artist.Monitored;
albumToUpdate.ProfileId = artist.ProfileId;
albumToUpdate.Added = DateTime.UtcNow;
tuple = _albumInfo.GetAlbumInfo(album.ForeignAlbumId, null);
albumInfo = tuple.Item1;
albumToUpdate = new Album
{
Monitored = artist.Monitored,
ProfileId = artist.ProfileId,
Added = DateTime.UtcNow
};
albumToUpdate.ArtistId = artist.Id;
albumToUpdate.CleanTitle = albumInfo.CleanTitle;
albumToUpdate.ForeignAlbumId = albumInfo.ForeignAlbumId;
albumToUpdate.Title = albumInfo.Title ?? "Unknown";
albumToUpdate.AlbumType = albumInfo.AlbumType;
_albumService.AddAlbum(albumToUpdate);
newList.Add(albumToUpdate);
}
albumToUpdate.ForeignAlbumId = album.ForeignAlbumId;
albumToUpdate.LastInfoSync = DateTime.UtcNow;
albumToUpdate.CleanTitle = album.CleanTitle;
albumToUpdate.Title = album.Title ?? "Unknown";
albumToUpdate.CleanTitle = albumInfo.CleanTitle;
albumToUpdate.Title = albumInfo.Title ?? "Unknown";
albumToUpdate.CleanTitle = Parser.Parser.CleanArtistName(albumToUpdate.Title);
albumToUpdate.ArtistId = artist.Id;
albumToUpdate.AlbumType = album.AlbumType;
albumToUpdate.SecondaryTypes = album.SecondaryTypes;
albumToUpdate.Genres = album.Genres;
albumToUpdate.Media = album.Media;
albumToUpdate.Label = album.Label;
albumToUpdate.Images = album.Images;
albumToUpdate.ReleaseDate = album.ReleaseDate;
albumToUpdate.Duration = album.Tracks.Sum(track => track.Duration);
albumToUpdate.AlbumType = albumInfo.AlbumType;
albumToUpdate.SecondaryTypes = albumInfo.SecondaryTypes;
albumToUpdate.Genres = albumInfo.Genres;
albumToUpdate.Media = albumInfo.Media;
albumToUpdate.Label = albumInfo.Label;
albumToUpdate.Images = albumInfo.Images;
albumToUpdate.ReleaseDate = albumInfo.ReleaseDate;
albumToUpdate.Duration = tuple.Item2.Sum(track => track.Duration);
albumToUpdate.Releases = albumInfo.Releases;
albumToUpdate.CurrentRelease = albumInfo.CurrentRelease;
_refreshTrackService.RefreshTrackInfo(albumToUpdate, tuple.Item2);
successCount++;
}
catch (Exception e)
{
_logger.Fatal(e, "An error has occurred while updating track info for artist {0}. {1}", artist, album);
_logger.Fatal(e, "An error has occurred while updating album info for artist {0}. {1}", artist,
album);
failCount++;
}
}
var allAlbums = new List<Album>();
allAlbums.AddRange(newList);
allAlbums.AddRange(updateList);
// TODO: See if anything needs to be done here
//AdjustMultiEpisodeAirTime(artist, allTracks);
//AdjustDirectToDvdAirDate(artist, allTracks);
_albumService.DeleteMany(existingAlbums);
_albumService.UpdateMany(updateList);
_albumService.InsertMany(newList);
_albumService.UpdateMany(newList);
_eventAggregator.PublishEvent(new AlbumInfoRefreshedEvent(artist, newList, updateList));
if (failCount != 0)
@@ -125,13 +211,26 @@ namespace NzbDrone.Core.Music
private Album GetAlbumToUpdate(Artist artist, Album album, List<Album> existingAlbums)
{
return existingAlbums.FirstOrDefault(e => e.ForeignAlbumId == album.ForeignAlbumId/* && e.ReleaseDate == album.ReleaseDate*/);
return existingAlbums.FirstOrDefault(
e => e.ForeignAlbumId == album.ForeignAlbumId /* && e.ReleaseDate == album.ReleaseDate*/);
}
private IEnumerable<Album> OrderAlbums(Artist artist, List<Album> albums)
{
return albums.OrderBy(e => e.ForeignAlbumId).ThenBy(e => e.ReleaseDate);
}
public void Execute(RefreshAlbumCommand message)
{
if (message.AlbumId.HasValue)
{
var album = _albumService.GetAlbum(message.AlbumId.Value);
var artist = _artistService.GetArtist(album.ArtistId);
RefreshAlbumInfo(album);
_eventAggregator.PublishEvent(new ArtistUpdatedEvent(artist));
}
}
}
}

View File

@@ -20,6 +20,7 @@ namespace NzbDrone.Core.Music
{
private readonly IProvideArtistInfo _artistInfo;
private readonly IArtistService _artistService;
private readonly IAlbumService _albumService;
private readonly IRefreshAlbumService _refreshAlbumService;
private readonly IRefreshTrackService _refreshTrackService;
private readonly IEventAggregator _eventAggregator;
@@ -29,6 +30,7 @@ namespace NzbDrone.Core.Music
public RefreshArtistService(IProvideArtistInfo artistInfo,
IArtistService artistService,
IAlbumService albumService,
IRefreshAlbumService refreshAlbumService,
IRefreshTrackService refreshTrackService,
IEventAggregator eventAggregator,
@@ -38,6 +40,7 @@ namespace NzbDrone.Core.Music
{
_artistInfo = artistInfo;
_artistService = artistService;
_albumService = albumService;
_refreshAlbumService = refreshAlbumService;
_refreshTrackService = refreshTrackService;
_eventAggregator = eventAggregator;
@@ -92,52 +95,13 @@ namespace NzbDrone.Core.Music
{
_logger.Warn(e, "Couldn't update artist path for " + artist.Path);
}
//artist.Albums = UpdateAlbums(artist, artistInfo); # We don't need this since we don't store albums in artist table.
_artistService.UpdateArtist(artist);
_refreshAlbumService.RefreshAlbumInfo(artist, tuple.Item2);
foreach (var album in tuple.Item2)
{
_refreshTrackService.RefreshTrackInfo(album, album.Tracks);
}
_refreshAlbumService.RefreshAlbumInfo(artist, tuple.Item2);
_logger.Debug("Finished artist refresh for {0}", artist.Name);
_eventAggregator.PublishEvent(new ArtistUpdatedEvent(artist));
}
private List<Album> UpdateAlbums(Artist artist, Artist artistInfo)
{
var albums = artistInfo.Albums.DistinctBy(s => s.ForeignAlbumId).ToList();
foreach (var album in albums)
{
var existingAlbum = artist.Albums.FirstOrDefault(s => s.ForeignAlbumId == album.ForeignAlbumId);
//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.ForeignArtistId, artist.Name);
album.Monitored = true;
}
else
{
album.Monitored = existingAlbum.Monitored;
}
}
return albums;
}
public void Execute(RefreshArtistCommand message)
{
_eventAggregator.PublishEvent(new ArtistRefreshStartingEvent(message.Trigger == CommandTrigger.Manual));

View File

@@ -68,6 +68,7 @@ namespace NzbDrone.Core.Music
trackToUpdate.AbsoluteTrackNumber = track.AbsoluteTrackNumber;
trackToUpdate.Title = track.Title ?? "Unknown";
trackToUpdate.AlbumId = album.Id;
trackToUpdate.ArtistId = album.ArtistId;
trackToUpdate.Album = track.Album ?? album;
trackToUpdate.Explicit = track.Explicit;
trackToUpdate.ArtistId = album.ArtistId;
@@ -89,15 +90,11 @@ namespace NzbDrone.Core.Music
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(album, newList, updateList));
_eventAggregator.PublishEvent(new TrackInfoRefreshedEvent(album, newList, updateList, existingTracks));
if (failCount != 0)
{

View File

@@ -35,6 +35,7 @@ namespace NzbDrone.Core.Music
public class TrackService : ITrackService,
IHandleAsync<ArtistDeletedEvent>,
IHandleAsync<AlbumDeletedEvent>,
IHandle<TrackFileDeletedEvent>,
IHandle<TrackFileAddedEvent>
{
@@ -163,6 +164,12 @@ namespace NzbDrone.Core.Music
_trackRepository.DeleteMany(tracks);
}
public void HandleAsync(AlbumDeletedEvent message)
{
var tracks = GetTracksByAlbum(message.Album.Id);
_trackRepository.DeleteMany(tracks);
}
public void Handle(TrackFileDeletedEvent message)
{
foreach (var track in GetTracksByFileId(message.TrackFile.Id))

View File

@@ -181,6 +181,7 @@
<Compile Include="Datastore\Migration\003_add_medium_support.cs" />
<Compile Include="Datastore\Migration\006_separate_automatic_and_interactive_search.cs" />
<Compile Include="Datastore\Migration\007_change_album_path_to_relative.cs" />
<Compile Include="Datastore\Migration\009_album_releases.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" />
@@ -424,6 +425,7 @@
<Compile Include="Download\ProcessDownloadDecisions.cs" />
<Compile Include="Download\ProcessedDecisions.cs" />
<Compile Include="Download\RedownloadFailedDownloadService.cs" />
<Compile Include="Exceptions\AlbumNotFoundException.cs" />
<Compile Include="Exceptions\ArtistNotFoundException.cs" />
<Compile Include="Exceptions\BadRequestException.cs" />
<Compile Include="Exceptions\DownstreamException.cs" />
@@ -730,6 +732,7 @@
<Compile Include="Messaging\IProcessMessage.cs" />
<Compile Include="MetadataSource\IProvideArtistInfo.cs" />
<Compile Include="MetadataSource\ISearchForNewArtist.cs" />
<Compile Include="MetadataSource\IProvideAlbumInfo.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\MediumResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\MemberResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\AlbumResource.cs" />
@@ -737,6 +740,7 @@
<Compile Include="MetadataSource\SkyHook\Resource\LinkResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ImageResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\RatingResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\ReleaseResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
<Compile Include="MetadataSource\SkyHook\Resource\TrackResource.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
@@ -761,9 +765,13 @@
<Compile Include="Extras\Metadata\MetadataRepository.cs" />
<Compile Include="Extras\Metadata\MetadataService.cs" />
<Compile Include="Extras\Metadata\MetadataType.cs" />
<Compile Include="Music\AlbumEditedService.cs" />
<Compile Include="Music\AlbumRelease.cs" />
<Compile Include="Music\ArtistStatusType.cs" />
<Compile Include="Music\AlbumCutoffService.cs" />
<Compile Include="Music\Commands\BulkMoveArtistCommand.cs" />
<Compile Include="Music\Commands\RefreshAlbumCommand.cs" />
<Compile Include="Music\Events\AlbumsAddedEvent.cs" />
<Compile Include="Music\Events\ArtistsImportedEvent.cs" />
<Compile Include="Music\SecondaryAlbumType.cs" />
<Compile Include="Music\PrimaryAlbumType.cs" />

View File

@@ -237,15 +237,6 @@ namespace NzbDrone.Integration.Test
changed = true;
}
result.Albums.ForEach(season =>
{
if (season.Monitored != monitored.Value)
{
season.Monitored = monitored.Value;
changed = true;
}
});
if (changed)
{
Artist.Put(result);