From 07b37c1575946dab43496b6316c438c267078abe Mon Sep 17 00:00:00 2001 From: Qstick Date: Sat, 16 Sep 2017 23:26:56 -0400 Subject: [PATCH] Update MediaCover Module to Work with Artist and Album Images --- frontend/src/Artist/Details/ArtistDetails.css | 9 +- src/Lidarr.Api.V3/Artist/ArtistModule.cs | 12 +- src/NzbDrone.Api/Series/SeriesModule.cs | 34 ++-- .../MediaCoverServiceFixture.cs | 48 +++-- src/NzbDrone.Core/Extras/ExtraService.cs | 14 +- .../MediaCover/MediaCoverService.cs | 166 ++++++++++++++---- .../MediaCover/MediaCoversUpdatedEvent.cs | 15 +- 7 files changed, 213 insertions(+), 85 deletions(-) diff --git a/frontend/src/Artist/Details/ArtistDetails.css b/frontend/src/Artist/Details/ArtistDetails.css index 20b8d911f..a7dc2be23 100644 --- a/frontend/src/Artist/Details/ArtistDetails.css +++ b/frontend/src/Artist/Details/ArtistDetails.css @@ -32,11 +32,18 @@ color: $white; } +.logo { + flex-shrink: 0; + margin-right: 35px; + width: 250px; + height: 97px; +} + .poster { flex-shrink: 0; margin-right: 35px; width: 250px; - height: 368px; + height: 250px; } .info { diff --git a/src/Lidarr.Api.V3/Artist/ArtistModule.cs b/src/Lidarr.Api.V3/Artist/ArtistModule.cs index f39681ab1..17dee9810 100644 --- a/src/Lidarr.Api.V3/Artist/ArtistModule.cs +++ b/src/Lidarr.Api.V3/Artist/ArtistModule.cs @@ -28,8 +28,8 @@ namespace Lidarr.Api.V3.Artist IHandle, IHandle, IHandle, - IHandle - //IHandle + IHandle, + IHandle { private readonly IArtistService _artistService; @@ -240,9 +240,9 @@ namespace Lidarr.Api.V3.Artist BroadcastResourceChange(ModelAction.Updated, message.Artist.Id); } - //public void Handle(MediaCoversUpdatedEvent message) - //{ - // BroadcastResourceChange(ModelAction.Updated, message.Series.Id); - //} + public void Handle(MediaCoversUpdatedEvent message) + { + BroadcastResourceChange(ModelAction.Updated, message.Artist.Id); + } } } diff --git a/src/NzbDrone.Api/Series/SeriesModule.cs b/src/NzbDrone.Api/Series/SeriesModule.cs index c7f6cbc7d..9d9160732 100644 --- a/src/NzbDrone.Api/Series/SeriesModule.cs +++ b/src/NzbDrone.Api/Series/SeriesModule.cs @@ -26,21 +26,21 @@ namespace NzbDrone.Api.Series IHandle, IHandle, IHandle, - IHandle, - IHandle + IHandle + //IHandle { private readonly ISeriesService _seriesService; private readonly IAddSeriesService _addSeriesService; private readonly ISeriesStatisticsService _seriesStatisticsService; - // private readonly ISceneMappingService _sceneMappingService; + //private readonly ISceneMappingService _sceneMappingService; private readonly IMapCoversToLocal _coverMapper; public SeriesModule(IBroadcastSignalRMessage signalRBroadcaster, ISeriesService seriesService, IAddSeriesService addSeriesService, ISeriesStatisticsService seriesStatisticsService, - // ISceneMappingService sceneMappingService, + //ISceneMappingService sceneMappingService, IMapCoversToLocal coverMapper, RootFolderValidator rootFolderValidator, SeriesPathValidator seriesPathValidator, @@ -96,7 +96,7 @@ namespace NzbDrone.Api.Series if (series == null) return null; var resource = series.ToResource(); - MapCoversToLocal(resource); + //MapCoversToLocal(resource); FetchAndLinkSeriesStatistics(resource); PopulateAlternateTitles(resource); @@ -108,7 +108,7 @@ namespace NzbDrone.Api.Series var seriesStats = _seriesStatisticsService.SeriesStatistics(); var seriesResources = _seriesService.GetAllSeries().ToResource(); - MapCoversToLocal(seriesResources.ToArray()); + //MapCoversToLocal(seriesResources.ToArray()); LinkSeriesStatistics(seriesResources, seriesStats); PopulateAlternateTitles(seriesResources); @@ -144,13 +144,13 @@ namespace NzbDrone.Api.Series _seriesService.DeleteSeries(id, deleteFiles); } - private void MapCoversToLocal(params SeriesResource[] series) - { - foreach (var seriesResource in series) - { - _coverMapper.ConvertToLocalUrls(seriesResource.Id, seriesResource.Images); - } - } + //private void MapCoversToLocal(params SeriesResource[] series) + // { + // foreach (var seriesResource in series) + // { + // _coverMapper.ConvertToLocalUrls(seriesResource.Id, seriesResource.Images); + // } + //} private void FetchAndLinkSeriesStatistics(SeriesResource resource) { @@ -239,9 +239,9 @@ namespace NzbDrone.Api.Series BroadcastResourceChange(ModelAction.Updated, message.Series.Id); } - public void Handle(MediaCoversUpdatedEvent message) - { - BroadcastResourceChange(ModelAction.Updated, message.Series.Id); - } + //public void Handle(MediaCoversUpdatedEvent message) + //{ + // BroadcastResourceChange(ModelAction.Updated, message.Series.Id); + //} } } diff --git a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs index fdf2efb07..6de8ece98 100644 --- a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using FizzWare.NBuilder; @@ -9,25 +9,31 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.MediaCover; using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Tv.Events; +using NzbDrone.Core.Music; +using NzbDrone.Core.Music.Events; namespace NzbDrone.Core.Test.MediaCoverTests { [TestFixture] public class MediaCoverServiceFixture : CoreTest { - Series _series; + Artist _artist; + Album _album; [SetUp] public void Setup() { Mocker.SetConstant(new AppFolderInfo(Mocker.Resolve())); - _series = Builder.CreateNew() + _artist = Builder.CreateNew() .With(v => v.Id = 2) .With(v => v.Images = new List { new MediaCover.MediaCover(MediaCoverTypes.Poster, "") }) .Build(); + + _album = Builder.CreateNew() + .With(v => v.Id = 4) + .With(v => v.Images = new List { new MediaCover.MediaCover(MediaCoverTypes.Cover, "") }) + .Build(); } [Test] @@ -50,6 +56,26 @@ namespace NzbDrone.Core.Test.MediaCoverTests covers.Single().Url.Should().Be("/MediaCover/12/banner.jpg?lastWrite=1234"); } + [Test] + public void should_convert_album_cover_urls_to_local() + { + var covers = new List + { + new MediaCover.MediaCover {CoverType = MediaCoverTypes.Disc} + }; + + Mocker.GetMock().Setup(c => c.FileGetLastWrite(It.IsAny())) + .Returns(new DateTime(1234)); + + Mocker.GetMock().Setup(c => c.FileExists(It.IsAny())) + .Returns(true); + + Subject.ConvertToLocalUrls(12, covers, 6); + + + covers.Single().Url.Should().Be("/MediaCover/12/6/disc.jpg?lastWrite=1234"); + } + [Test] public void should_convert_media_urls_to_local_without_time_if_file_doesnt_exist() { @@ -76,7 +102,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests .Setup(v => v.FileExists(It.IsAny())) .Returns(true); - Subject.HandleAsync(new SeriesUpdatedEvent(_series)); + Subject.HandleAsync(new ArtistUpdatedEvent(_artist)); Mocker.GetMock() .Verify(v => v.Resize(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); @@ -93,7 +119,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests .Setup(v => v.FileExists(It.IsAny())) .Returns(false); - Subject.HandleAsync(new SeriesUpdatedEvent(_series)); + Subject.HandleAsync(new ArtistUpdatedEvent(_artist)); Mocker.GetMock() .Verify(v => v.Resize(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); @@ -114,7 +140,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests .Setup(v => v.GetFileSize(It.IsAny())) .Returns(1000); - Subject.HandleAsync(new SeriesUpdatedEvent(_series)); + Subject.HandleAsync(new ArtistUpdatedEvent(_artist)); Mocker.GetMock() .Verify(v => v.Resize(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); @@ -135,7 +161,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests .Setup(v => v.GetFileSize(It.IsAny())) .Returns(0); - Subject.HandleAsync(new SeriesUpdatedEvent(_series)); + Subject.HandleAsync(new ArtistUpdatedEvent(_artist)); Mocker.GetMock() .Verify(v => v.Resize(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); @@ -156,10 +182,10 @@ namespace NzbDrone.Core.Test.MediaCoverTests .Setup(v => v.Resize(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(); - Subject.HandleAsync(new SeriesUpdatedEvent(_series)); + Subject.HandleAsync(new ArtistUpdatedEvent(_artist)); Mocker.GetMock() .Verify(v => v.Resize(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Extras/ExtraService.cs b/src/NzbDrone.Core/Extras/ExtraService.cs index 7447e162e..6716e25db 100644 --- a/src/NzbDrone.Core/Extras/ExtraService.cs +++ b/src/NzbDrone.Core/Extras/ExtraService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -105,13 +105,13 @@ namespace NzbDrone.Core.Extras public void Handle(MediaCoversUpdatedEvent message) { - var series = message.Series; - var episodeFiles = GetEpisodeFiles(series.Id); + //var artist = message.Artist; + //var episodeFiles = GetEpisodeFiles(artist.Id); - foreach (var extraFileManager in _extraFileManagers) - { - extraFileManager.CreateAfterSeriesScan(series, episodeFiles); - } + //foreach (var extraFileManager in _extraFileManagers) + //{ + // extraFileManager.CreateAfterSeriesScan(artist, episodeFiles); + //} } public void Handle(EpisodeFolderCreatedEvent message) diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index e65fa5f5b..913e27eac 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -9,23 +9,24 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Tv.Events; +using NzbDrone.Core.Music; +using NzbDrone.Core.Music.Events; namespace NzbDrone.Core.MediaCover { public interface IMapCoversToLocal { - void ConvertToLocalUrls(int seriesId, IEnumerable covers); - string GetCoverPath(int seriesId, MediaCoverTypes mediaCoverTypes, int? height = null); + void ConvertToLocalUrls(int artistId, IEnumerable covers, int? albumId = null); + string GetCoverPath(int artistId, MediaCoverTypes mediaCoverTypes, int? height = null, int? albumId = null); } public class MediaCoverService : - IHandleAsync, - IHandleAsync, + IHandleAsync, + IHandleAsync, IMapCoversToLocal { private readonly IImageResizer _resizer; + private readonly IAlbumService _albumService; private readonly IHttpClient _httpClient; private readonly IDiskProvider _diskProvider; private readonly ICoverExistsSpecification _coverExistsSpecification; @@ -36,6 +37,7 @@ namespace NzbDrone.Core.MediaCover private readonly string _coverRootFolder; public MediaCoverService(IImageResizer resizer, + IAlbumService albumService, IHttpClient httpClient, IDiskProvider diskProvider, IAppFolderInfo appFolderInfo, @@ -45,6 +47,7 @@ namespace NzbDrone.Core.MediaCover Logger logger) { _resizer = resizer; + _albumService = albumService; _httpClient = httpClient; _diskProvider = diskProvider; _coverExistsSpecification = coverExistsSpecification; @@ -55,20 +58,32 @@ namespace NzbDrone.Core.MediaCover _coverRootFolder = appFolderInfo.GetMediaCoverPath(); } - public string GetCoverPath(int seriesId, MediaCoverTypes coverTypes, int? height = null) + public string GetCoverPath(int artistId, MediaCoverTypes coverTypes, int? height = null, int? albumId = null) { var heightSuffix = height.HasValue ? "-" + height.ToString() : ""; - return Path.Combine(GetSeriesCoverPath(seriesId), coverTypes.ToString().ToLower() + heightSuffix + ".jpg"); + if (albumId.HasValue) + { + return Path.Combine(GetAlbumCoverPath(artistId, albumId.Value), coverTypes.ToString().ToLower() + heightSuffix + ".jpg"); + } + + return Path.Combine(GetArtistCoverPath(artistId), coverTypes.ToString().ToLower() + heightSuffix + ".jpg"); } - public void ConvertToLocalUrls(int seriesId, IEnumerable covers) + public void ConvertToLocalUrls(int artistId, IEnumerable covers, int? albumId = null) { foreach (var mediaCover in covers) { - var filePath = GetCoverPath(seriesId, mediaCover.CoverType); + var filePath = GetCoverPath(artistId, mediaCover.CoverType, null, albumId); - mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + seriesId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg"; + if (albumId.HasValue) + { + mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + artistId + "/" + albumId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg"; + } + else + { + mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + artistId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg"; + } if (_diskProvider.FileExists(filePath)) { @@ -78,47 +93,87 @@ namespace NzbDrone.Core.MediaCover } } - private string GetSeriesCoverPath(int seriesId) + private string GetArtistCoverPath(int artistId) { - return Path.Combine(_coverRootFolder, seriesId.ToString()); + return Path.Combine(_coverRootFolder, artistId.ToString()); } - private void EnsureCovers(Series series) + private string GetAlbumCoverPath(int artistId, int albumId) { - foreach (var cover in series.Images) + return Path.Combine(_coverRootFolder, artistId.ToString(), albumId.ToString()); + } + + private void EnsureCovers(Artist artist) + { + foreach (var cover in artist.Images) { - var fileName = GetCoverPath(series.Id, cover.CoverType); + var fileName = GetCoverPath(artist.Id, cover.CoverType); var alreadyExists = false; try { alreadyExists = _coverExistsSpecification.AlreadyExists(cover.Url, fileName); if (!alreadyExists) { - DownloadCover(series, cover); + DownloadCover(artist, cover); } } catch (WebException e) { - _logger.Warn("Couldn't download media cover for {0}. {1}", series, e.Message); + _logger.Warn("Couldn't download media cover for {0}. {1}", artist, e.Message); } catch (Exception e) { - _logger.Error(e, "Couldn't download media cover for {0}", series); + _logger.Error(e, "Couldn't download media cover for {0}", artist); } - EnsureResizedCovers(series, cover, !alreadyExists); + EnsureResizedCovers(artist, cover, !alreadyExists); } } - private void DownloadCover(Series series, MediaCover cover) + private void EnsureAlbumCovers(Album album) { - var fileName = GetCoverPath(series.Id, cover.CoverType); + foreach (var cover in album.Images) + { + var fileName = GetCoverPath(album.ArtistId, cover.CoverType, null, album.Id); + var alreadyExists = false; + try + { + alreadyExists = _coverExistsSpecification.AlreadyExists(cover.Url, fileName); + if (!alreadyExists) + { + DownloadAlbumCover(album, cover); + } + } + catch (WebException e) + { + _logger.Warn("Couldn't download media cover for {0}. {1}", album, e.Message); + } + catch (Exception e) + { + _logger.Error(e, "Couldn't download media cover for {0}", album); + } - _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, series, cover.Url); + EnsureResizedCovers(album.Artist, cover, !alreadyExists, album); + } + } + + private void DownloadCover(Artist artist, MediaCover cover) + { + var fileName = GetCoverPath(artist.Id, cover.CoverType); + + _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, artist, cover.Url); _httpClient.DownloadFile(cover.Url, fileName); } - private void EnsureResizedCovers(Series series, MediaCover cover, bool forceResize) + private void DownloadAlbumCover(Album album, MediaCover cover) + { + var fileName = GetCoverPath(album.ArtistId, cover.CoverType, null, album.Id); + + _logger.Info("Downloading {0} for {1} {2}", cover.CoverType, album, cover.Url); + _httpClient.DownloadFile(cover.Url, fileName); + } + + private void EnsureResizedCovers(Artist artist, MediaCover cover, bool forceResize, Album album = null) { int[] heights; @@ -144,37 +199,70 @@ namespace NzbDrone.Core.MediaCover heights = new[] { 360, 180 }; break; } + - foreach (var height in heights) + if (album == null) { - var mainFileName = GetCoverPath(series.Id, cover.CoverType); - var resizeFileName = GetCoverPath(series.Id, cover.CoverType, height); - - if (forceResize || !_diskProvider.FileExists(resizeFileName) || _diskProvider.GetFileSize(resizeFileName) == 0) + foreach (var height in heights) { - _logger.Debug("Resizing {0}-{1} for {2}", cover.CoverType, height, series); + var mainFileName = GetCoverPath(artist.Id, cover.CoverType); + var resizeFileName = GetCoverPath(artist.Id, cover.CoverType, height); - try + if (forceResize || !_diskProvider.FileExists(resizeFileName) || _diskProvider.GetFileSize(resizeFileName) == 0) { - _resizer.Resize(mainFileName, resizeFileName, height); + _logger.Debug("Resizing {0}-{1} for {2}", cover.CoverType, height, artist); + + try + { + _resizer.Resize(mainFileName, resizeFileName, height); + } + catch + { + _logger.Debug("Couldn't resize media cover {0}-{1} for {2}, using full size image instead.", cover.CoverType, height, artist); + } } - catch + } + } + else + { + foreach (var height in heights) + { + var mainFileName = GetCoverPath(album.ArtistId, cover.CoverType, null, album.Id); + var resizeFileName = GetCoverPath(album.ArtistId, cover.CoverType, height, album.Id); + + if (forceResize || !_diskProvider.FileExists(resizeFileName) || _diskProvider.GetFileSize(resizeFileName) == 0) { - _logger.Debug("Couldn't resize media cover {0}-{1} for {2}, using full size image instead.", cover.CoverType, height, series); + _logger.Debug("Resizing {0}-{1} for {2}", cover.CoverType, height, artist); + + try + { + _resizer.Resize(mainFileName, resizeFileName, height); + } + catch + { + _logger.Debug("Couldn't resize media cover {0}-{1} for {2}, using full size image instead.", cover.CoverType, height, album); + } } } } } - public void HandleAsync(SeriesUpdatedEvent message) + public void HandleAsync(ArtistUpdatedEvent message) { - EnsureCovers(message.Series); - _eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Series)); + EnsureCovers(message.Artist); + + var albums = _albumService.GetAlbumsByArtist(message.Artist.Id); + foreach (Album album in albums) + { + EnsureAlbumCovers(album); + } + + _eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Artist)); } - public void HandleAsync(SeriesDeletedEvent message) + public void HandleAsync(ArtistDeletedEvent message) { - var path = GetSeriesCoverPath(message.Series.Id); + var path = GetArtistCoverPath(message.Artist.Id); if (_diskProvider.FolderExists(path)) { _diskProvider.DeleteFolder(path, true); diff --git a/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs b/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs index 7335f7f9b..364a74ff1 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs @@ -1,15 +1,22 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Common.Messaging; using NzbDrone.Core.Tv; +using NzbDrone.Core.Music; namespace NzbDrone.Core.MediaCover { public class MediaCoversUpdatedEvent : IEvent { - public Series Series { get; set; } + public Artist Artist { get; set; } + public Album Album { get; set; } - public MediaCoversUpdatedEvent(Series series) + public MediaCoversUpdatedEvent(Artist artist) { - Series = series; + Artist = artist; + } + + public MediaCoversUpdatedEvent(Album album) + { + Album = album; } } }