2017-09-04 02:20:56 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using FluentValidation;
|
|
|
|
using NzbDrone.Common.Extensions;
|
|
|
|
using NzbDrone.Core.Datastore.Events;
|
|
|
|
using NzbDrone.Core.MediaCover;
|
|
|
|
using NzbDrone.Core.MediaFiles;
|
|
|
|
using NzbDrone.Core.MediaFiles.Events;
|
2017-12-30 03:23:04 +00:00
|
|
|
using NzbDrone.Core.Messaging.Commands;
|
2017-09-04 02:20:56 +00:00
|
|
|
using NzbDrone.Core.Messaging.Events;
|
2018-08-08 00:57:15 +00:00
|
|
|
using NzbDrone.Core.RootFolders;
|
2017-09-04 02:20:56 +00:00
|
|
|
using NzbDrone.Core.ArtistStats;
|
|
|
|
using NzbDrone.Core.Music;
|
2017-12-30 03:23:04 +00:00
|
|
|
using NzbDrone.Core.Music.Commands;
|
2017-09-04 02:20:56 +00:00
|
|
|
using NzbDrone.Core.Music.Events;
|
|
|
|
using NzbDrone.Core.Validation;
|
|
|
|
using NzbDrone.Core.Validation.Paths;
|
2017-10-31 01:28:29 +00:00
|
|
|
using Lidarr.Api.V1.Albums;
|
2017-09-04 02:20:56 +00:00
|
|
|
using NzbDrone.SignalR;
|
|
|
|
using Lidarr.Http;
|
|
|
|
using Lidarr.Http.Extensions;
|
|
|
|
|
2017-10-31 01:28:29 +00:00
|
|
|
namespace Lidarr.Api.V1.Artist
|
2017-09-04 02:20:56 +00:00
|
|
|
{
|
|
|
|
public class ArtistModule : LidarrRestModuleWithSignalR<ArtistResource, NzbDrone.Core.Music.Artist>,
|
2019-02-16 14:49:24 +00:00
|
|
|
IHandle<AlbumImportedEvent>,
|
2019-03-15 21:48:50 +00:00
|
|
|
IHandle<AlbumEditedEvent>,
|
2017-09-04 02:20:56 +00:00
|
|
|
IHandle<TrackFileDeletedEvent>,
|
|
|
|
IHandle<ArtistUpdatedEvent>,
|
|
|
|
IHandle<ArtistEditedEvent>,
|
|
|
|
IHandle<ArtistDeletedEvent>,
|
2017-09-17 03:26:56 +00:00
|
|
|
IHandle<ArtistRenamedEvent>,
|
|
|
|
IHandle<MediaCoversUpdatedEvent>
|
2017-09-04 02:20:56 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
private readonly IArtistService _artistService;
|
2018-01-18 02:28:47 +00:00
|
|
|
private readonly IAlbumService _albumService;
|
2017-09-04 02:20:56 +00:00
|
|
|
private readonly IAddArtistService _addArtistService;
|
|
|
|
private readonly IArtistStatisticsService _artistStatisticsService;
|
|
|
|
private readonly IMapCoversToLocal _coverMapper;
|
2017-12-30 03:23:04 +00:00
|
|
|
private readonly IManageCommandQueue _commandQueueManager;
|
2018-08-08 00:57:15 +00:00
|
|
|
private readonly IRootFolderService _rootFolderService;
|
2017-09-04 02:20:56 +00:00
|
|
|
|
|
|
|
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
|
|
|
|
IArtistService artistService,
|
2018-01-18 02:28:47 +00:00
|
|
|
IAlbumService albumService,
|
2017-09-04 02:20:56 +00:00
|
|
|
IAddArtistService addArtistService,
|
|
|
|
IArtistStatisticsService artistStatisticsService,
|
|
|
|
IMapCoversToLocal coverMapper,
|
2017-12-30 03:23:04 +00:00
|
|
|
IManageCommandQueue commandQueueManager,
|
2018-08-08 00:57:15 +00:00
|
|
|
IRootFolderService rootFolderService,
|
2017-09-04 02:20:56 +00:00
|
|
|
RootFolderValidator rootFolderValidator,
|
2018-09-05 02:38:48 +00:00
|
|
|
MappedNetworkDriveValidator mappedNetworkDriveValidator,
|
2017-09-04 02:20:56 +00:00
|
|
|
ArtistPathValidator artistPathValidator,
|
|
|
|
ArtistExistsValidator artistExistsValidator,
|
2017-10-08 19:09:46 +00:00
|
|
|
ArtistAncestorValidator artistAncestorValidator,
|
2017-12-23 02:14:15 +00:00
|
|
|
SystemFolderValidator systemFolderValidator,
|
2017-09-04 02:20:56 +00:00
|
|
|
ProfileExistsValidator profileExistsValidator,
|
2017-11-27 02:43:17 +00:00
|
|
|
MetadataProfileExistsValidator metadataProfileExistsValidator
|
2017-09-04 02:20:56 +00:00
|
|
|
)
|
|
|
|
: base(signalRBroadcaster)
|
|
|
|
{
|
|
|
|
_artistService = artistService;
|
2018-01-18 02:28:47 +00:00
|
|
|
_albumService = albumService;
|
2017-09-04 02:20:56 +00:00
|
|
|
_addArtistService = addArtistService;
|
|
|
|
_artistStatisticsService = artistStatisticsService;
|
|
|
|
|
|
|
|
_coverMapper = coverMapper;
|
2017-12-30 03:23:04 +00:00
|
|
|
_commandQueueManager = commandQueueManager;
|
2018-08-08 00:57:15 +00:00
|
|
|
_rootFolderService = rootFolderService;
|
2017-09-04 02:20:56 +00:00
|
|
|
|
|
|
|
GetResourceAll = AllArtists;
|
|
|
|
GetResourceById = GetArtist;
|
|
|
|
CreateResource = AddArtist;
|
|
|
|
UpdateResource = UpdateArtist;
|
|
|
|
DeleteResource = DeleteArtist;
|
|
|
|
|
|
|
|
Http.Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.QualityProfileId));
|
2018-04-09 02:57:09 +00:00
|
|
|
Http.Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.MetadataProfileId));
|
2017-09-04 02:20:56 +00:00
|
|
|
|
|
|
|
SharedValidator.RuleFor(s => s.Path)
|
|
|
|
.Cascade(CascadeMode.StopOnFirstFailure)
|
|
|
|
.IsValidPath()
|
|
|
|
.SetValidator(rootFolderValidator)
|
2018-09-05 02:38:48 +00:00
|
|
|
.SetValidator(mappedNetworkDriveValidator)
|
2017-09-04 02:20:56 +00:00
|
|
|
.SetValidator(artistPathValidator)
|
2017-10-08 19:09:46 +00:00
|
|
|
.SetValidator(artistAncestorValidator)
|
2017-12-23 02:14:15 +00:00
|
|
|
.SetValidator(systemFolderValidator)
|
2017-09-04 02:20:56 +00:00
|
|
|
.When(s => !s.Path.IsNullOrWhiteSpace());
|
|
|
|
|
|
|
|
SharedValidator.RuleFor(s => s.QualityProfileId).SetValidator(profileExistsValidator);
|
2017-11-27 02:43:17 +00:00
|
|
|
SharedValidator.RuleFor(s => s.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
|
2017-09-04 02:20:56 +00:00
|
|
|
|
|
|
|
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.ArtistName).NotEmpty();
|
|
|
|
PostValidator.RuleFor(s => s.ForeignArtistId).NotEmpty().SetValidator(artistExistsValidator);
|
|
|
|
|
|
|
|
PutValidator.RuleFor(s => s.Path).IsValidPath();
|
|
|
|
}
|
|
|
|
|
|
|
|
private ArtistResource GetArtist(int id)
|
|
|
|
{
|
|
|
|
var artist = _artistService.GetArtist(id);
|
|
|
|
return GetArtistResource(artist);
|
|
|
|
}
|
|
|
|
|
|
|
|
private ArtistResource GetArtistResource(NzbDrone.Core.Music.Artist artist)
|
|
|
|
{
|
|
|
|
if (artist == null) return null;
|
|
|
|
|
|
|
|
var resource = artist.ToResource();
|
|
|
|
MapCoversToLocal(resource);
|
|
|
|
FetchAndLinkArtistStatistics(resource);
|
2018-01-18 02:28:47 +00:00
|
|
|
LinkNextPreviousAlbums(resource);
|
2017-09-04 02:20:56 +00:00
|
|
|
//PopulateAlternateTitles(resource);
|
2018-08-08 00:57:15 +00:00
|
|
|
LinkRootFolderPath(resource);
|
2017-09-04 02:20:56 +00:00
|
|
|
|
|
|
|
return resource;
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<ArtistResource> AllArtists()
|
|
|
|
{
|
|
|
|
var artistStats = _artistStatisticsService.ArtistStatistics();
|
|
|
|
var artistsResources = _artistService.GetAllArtists().ToResource();
|
|
|
|
|
|
|
|
MapCoversToLocal(artistsResources.ToArray());
|
2018-01-18 02:28:47 +00:00
|
|
|
LinkNextPreviousAlbums(artistsResources.ToArray());
|
2017-09-04 02:20:56 +00:00
|
|
|
LinkArtistStatistics(artistsResources, artistStats);
|
|
|
|
//PopulateAlternateTitles(seriesResources);
|
|
|
|
|
|
|
|
return artistsResources;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int AddArtist(ArtistResource artistResource)
|
|
|
|
{
|
|
|
|
var artist = _addArtistService.AddArtist(artistResource.ToModel());
|
|
|
|
|
|
|
|
return artist.Id;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void UpdateArtist(ArtistResource artistResource)
|
|
|
|
{
|
2017-12-30 03:23:04 +00:00
|
|
|
var moveFiles = Request.GetBooleanQueryParameter("moveFiles");
|
|
|
|
var artist = _artistService.GetArtist(artistResource.Id);
|
|
|
|
|
|
|
|
if (moveFiles)
|
|
|
|
{
|
|
|
|
var sourcePath = artist.Path;
|
|
|
|
var destinationPath = artistResource.Path;
|
|
|
|
|
|
|
|
_commandQueueManager.Push(new MoveArtistCommand
|
|
|
|
{
|
|
|
|
ArtistId = artist.Id,
|
|
|
|
SourcePath = sourcePath,
|
|
|
|
DestinationPath = destinationPath,
|
|
|
|
Trigger = CommandTrigger.Manual
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
var model = artistResource.ToModel(artist);
|
2017-09-04 02:20:56 +00:00
|
|
|
|
|
|
|
_artistService.UpdateArtist(model);
|
|
|
|
|
|
|
|
BroadcastResourceChange(ModelAction.Updated, artistResource);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void DeleteArtist(int id)
|
|
|
|
{
|
|
|
|
var deleteFiles = Request.GetBooleanQueryParameter("deleteFiles");
|
2019-03-01 22:26:36 +00:00
|
|
|
var addImportListExclusion = Request.GetBooleanQueryParameter("addImportListExclusion");
|
2017-09-04 02:20:56 +00:00
|
|
|
|
2019-03-01 22:26:36 +00:00
|
|
|
_artistService.DeleteArtist(id, deleteFiles, addImportListExclusion);
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void MapCoversToLocal(params ArtistResource[] artists)
|
|
|
|
{
|
|
|
|
foreach (var artistResource in artists)
|
|
|
|
{
|
2019-05-07 23:10:09 +00:00
|
|
|
_coverMapper.ConvertToLocalUrls(artistResource.Id, MediaCoverEntity.Artist, artistResource.Images);
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-18 02:28:47 +00:00
|
|
|
private void LinkNextPreviousAlbums(params ArtistResource[] artists)
|
|
|
|
{
|
2019-08-17 07:04:59 +00:00
|
|
|
var nextAlbums = _albumService.GetNextAlbumsByArtistMetadataId(artists.Select(x => x.ArtistMetadataId));
|
|
|
|
var lastAlbums = _albumService.GetLastAlbumsByArtistMetadataId(artists.Select(x => x.ArtistMetadataId));
|
|
|
|
|
2018-01-18 02:28:47 +00:00
|
|
|
foreach (var artistResource in artists)
|
|
|
|
{
|
2019-08-17 07:04:59 +00:00
|
|
|
artistResource.NextAlbum = nextAlbums.FirstOrDefault(x => x.ArtistMetadataId == artistResource.ArtistMetadataId);
|
|
|
|
artistResource.LastAlbum = lastAlbums.FirstOrDefault(x => x.ArtistMetadataId == artistResource.ArtistMetadataId);
|
2018-01-18 02:28:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-04 02:20:56 +00:00
|
|
|
private void FetchAndLinkArtistStatistics(ArtistResource resource)
|
|
|
|
{
|
|
|
|
LinkArtistStatistics(resource, _artistStatisticsService.ArtistStatistics(resource.Id));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void LinkArtistStatistics(List<ArtistResource> resources, List<ArtistStatistics> artistStatistics)
|
|
|
|
{
|
|
|
|
foreach (var artist in resources)
|
|
|
|
{
|
|
|
|
var stats = artistStatistics.SingleOrDefault(ss => ss.ArtistId == artist.Id);
|
|
|
|
if (stats == null) continue;
|
|
|
|
|
|
|
|
LinkArtistStatistics(artist, stats);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void LinkArtistStatistics(ArtistResource resource, ArtistStatistics artistStatistics)
|
|
|
|
{
|
2018-03-15 01:28:46 +00:00
|
|
|
resource.Statistics = artistStatistics.ToResource();
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//private void PopulateAlternateTitles(List<ArtistResource> resources)
|
|
|
|
//{
|
|
|
|
// foreach (var resource in resources)
|
|
|
|
// {
|
|
|
|
// PopulateAlternateTitles(resource);
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
|
|
|
|
//private void PopulateAlternateTitles(ArtistResource resource)
|
|
|
|
//{
|
|
|
|
// var mappings = _sceneMappingService.FindByTvdbId(resource.TvdbId);
|
|
|
|
|
|
|
|
// if (mappings == null) return;
|
|
|
|
|
|
|
|
// resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList();
|
|
|
|
//}
|
|
|
|
|
2018-08-08 00:57:15 +00:00
|
|
|
private void LinkRootFolderPath(ArtistResource resource)
|
|
|
|
{
|
|
|
|
resource.RootFolderPath = _rootFolderService.GetBestRootFolderPath(resource.Path);
|
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
public void Handle(AlbumImportedEvent message)
|
2017-09-04 02:20:56 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Artist));
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
|
2019-03-15 21:48:50 +00:00
|
|
|
public void Handle(AlbumEditedEvent message)
|
|
|
|
{
|
|
|
|
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Album.Artist.Value));
|
|
|
|
}
|
|
|
|
|
2017-09-04 02:20:56 +00:00
|
|
|
public void Handle(TrackFileDeletedEvent message)
|
|
|
|
{
|
|
|
|
if (message.Reason == DeleteMediaFileReason.Upgrade) return;
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.TrackFile.Artist.Value));
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Handle(ArtistUpdatedEvent message)
|
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Artist));
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Handle(ArtistEditedEvent message)
|
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Artist));
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Handle(ArtistDeletedEvent message)
|
|
|
|
{
|
|
|
|
BroadcastResourceChange(ModelAction.Deleted, message.Artist.ToResource());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Handle(ArtistRenamedEvent message)
|
|
|
|
{
|
|
|
|
BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
|
|
|
|
}
|
|
|
|
|
2017-09-17 03:26:56 +00:00
|
|
|
public void Handle(MediaCoversUpdatedEvent message)
|
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Artist));
|
2017-09-17 03:26:56 +00:00
|
|
|
}
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
}
|