2017-09-18 03:20:36 +00:00
using NLog ;
2017-06-18 02:27:01 +00:00
using NzbDrone.Common.Extensions ;
using NzbDrone.Core.Messaging.Events ;
using NzbDrone.Core.Music.Events ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
2018-01-18 02:28:47 +00:00
using NzbDrone.Core.MetadataSource ;
using NzbDrone.Common.Instrumentation.Extensions ;
using NzbDrone.Core.Exceptions ;
using NzbDrone.Core.Messaging.Commands ;
using NzbDrone.Core.Music.Commands ;
2019-03-15 12:10:45 +00:00
using NzbDrone.Core.MediaFiles ;
2019-03-22 09:33:48 +00:00
using NzbDrone.Common.EnsureThat ;
2017-06-18 02:27:01 +00:00
namespace NzbDrone.Core.Music
{
public interface IRefreshAlbumService
{
2019-03-15 12:10:45 +00:00
void RefreshAlbumInfo ( Album album , bool forceUpdateFileTags ) ;
void RefreshAlbumInfo ( List < Album > albums , bool forceAlbumRefresh , bool forceUpdateFileTags ) ;
2017-06-18 02:27:01 +00:00
}
2018-01-18 02:28:47 +00:00
public class RefreshAlbumService : IRefreshAlbumService , IExecute < RefreshAlbumCommand >
2017-06-18 02:27:01 +00:00
{
private readonly IAlbumService _albumService ;
2018-01-18 02:28:47 +00:00
private readonly IArtistService _artistService ;
2018-12-15 00:02:43 +00:00
private readonly IArtistMetadataRepository _artistMetadataRepository ;
private readonly IReleaseService _releaseService ;
2018-01-18 02:28:47 +00:00
private readonly IProvideAlbumInfo _albumInfo ;
2017-06-18 02:27:01 +00:00
private readonly IRefreshTrackService _refreshTrackService ;
2019-03-15 12:10:45 +00:00
private readonly IAudioTagService _audioTagService ;
2017-06-18 02:27:01 +00:00
private readonly IEventAggregator _eventAggregator ;
2018-02-13 01:32:18 +00:00
private readonly ICheckIfAlbumShouldBeRefreshed _checkIfAlbumShouldBeRefreshed ;
2017-06-18 02:27:01 +00:00
private readonly Logger _logger ;
2018-01-18 02:28:47 +00:00
public RefreshAlbumService ( IAlbumService albumService ,
IArtistService artistService ,
2018-12-15 00:02:43 +00:00
IArtistMetadataRepository artistMetadataRepository ,
IReleaseService releaseService ,
2018-01-18 02:28:47 +00:00
IProvideAlbumInfo albumInfo ,
IRefreshTrackService refreshTrackService ,
2019-03-15 12:10:45 +00:00
IAudioTagService audioTagService ,
2018-01-18 02:28:47 +00:00
IEventAggregator eventAggregator ,
2018-02-13 01:32:18 +00:00
ICheckIfAlbumShouldBeRefreshed checkIfAlbumShouldBeRefreshed ,
2018-01-18 02:28:47 +00:00
Logger logger )
2017-06-18 02:27:01 +00:00
{
_albumService = albumService ;
2018-01-18 02:28:47 +00:00
_artistService = artistService ;
2018-12-15 00:02:43 +00:00
_artistMetadataRepository = artistMetadataRepository ;
_releaseService = releaseService ;
2018-01-18 02:28:47 +00:00
_albumInfo = albumInfo ;
2017-06-18 02:27:01 +00:00
_refreshTrackService = refreshTrackService ;
2019-03-15 12:10:45 +00:00
_audioTagService = audioTagService ;
2017-06-18 02:27:01 +00:00
_eventAggregator = eventAggregator ;
2018-02-13 01:32:18 +00:00
_checkIfAlbumShouldBeRefreshed = checkIfAlbumShouldBeRefreshed ;
2017-06-18 02:27:01 +00:00
_logger = logger ;
}
2019-03-15 12:10:45 +00:00
public void RefreshAlbumInfo ( List < Album > albums , bool forceAlbumRefresh , bool forceUpdateFileTags )
2018-01-28 06:27:33 +00:00
{
foreach ( var album in albums )
{
2018-05-28 07:49:34 +00:00
if ( forceAlbumRefresh | | _checkIfAlbumShouldBeRefreshed . ShouldRefresh ( album ) )
2018-02-13 01:32:18 +00:00
{
2019-03-15 12:10:45 +00:00
RefreshAlbumInfo ( album , forceUpdateFileTags ) ;
2018-02-13 01:32:18 +00:00
}
2018-01-28 06:27:33 +00:00
}
}
2019-03-15 12:10:45 +00:00
public void RefreshAlbumInfo ( Album album , bool forceUpdateFileTags )
2018-01-18 02:28:47 +00:00
{
_logger . ProgressInfo ( "Updating Info for {0}" , album . Title ) ;
2018-12-15 00:02:43 +00:00
Tuple < string , Album , List < ArtistMetadata > > tuple ;
2018-01-18 02:28:47 +00:00
try
{
2018-12-15 00:02:43 +00:00
tuple = _albumInfo . GetAlbumInfo ( album . ForeignAlbumId ) ;
2018-01-18 02:28:47 +00:00
}
catch ( AlbumNotFoundException )
{
2019-03-15 12:10:45 +00:00
_logger . Error ( $"{album} was not found, it may have been removed from Metadata sources." ) ;
2018-01-18 02:28:47 +00:00
return ;
}
2019-04-16 00:52:43 +00:00
if ( tuple . Item2 . AlbumReleases . Value . Count = = 0 )
{
_logger . Debug ( $"{album} has no valid releases, removing." ) ;
_albumService . DeleteMany ( new List < Album > { album } ) ;
return ;
}
2019-03-15 12:10:45 +00:00
var remoteMetadata = tuple . Item3 . DistinctBy ( x = > x . ForeignArtistId ) . ToList ( ) ;
var existingMetadata = _artistMetadataRepository . FindById ( remoteMetadata . Select ( x = > x . ForeignArtistId ) . ToList ( ) ) ;
var updateMetadataList = new List < ArtistMetadata > ( ) ;
var addMetadataList = new List < ArtistMetadata > ( ) ;
var upToDateMetadataCount = 0 ;
foreach ( var meta in remoteMetadata )
{
var existing = existingMetadata . SingleOrDefault ( x = > x . ForeignArtistId = = meta . ForeignArtistId ) ;
if ( existing ! = null )
{
meta . Id = existing . Id ;
if ( ! meta . Equals ( existing ) )
{
updateMetadataList . Add ( meta ) ;
}
else
{
upToDateMetadataCount + + ;
}
}
else
{
addMetadataList . Add ( meta ) ;
}
}
_logger . Debug ( $"{album}: {upToDateMetadataCount} artist metadata up to date; Updating {updateMetadataList.Count}, Adding {addMetadataList.Count} artist metadata entries." ) ;
_artistMetadataRepository . UpdateMany ( updateMetadataList ) ;
_artistMetadataRepository . InsertMany ( addMetadataList ) ;
forceUpdateFileTags | = updateMetadataList . Any ( ) ;
2018-12-15 00:02:43 +00:00
var albumInfo = tuple . Item2 ;
2018-01-18 02:28:47 +00:00
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 ;
}
2019-03-15 12:10:45 +00:00
// the only thing written to tags from the album object is the title
forceUpdateFileTags | = album . Title ! = ( albumInfo . Title ? ? "Unknown" ) ;
2018-01-18 02:28:47 +00:00
album . LastInfoSync = DateTime . UtcNow ;
album . CleanTitle = albumInfo . CleanTitle ;
album . Title = albumInfo . Title ? ? "Unknown" ;
2019-04-21 02:56:03 +00:00
album . Overview = albumInfo . Overview . IsNullOrWhiteSpace ( ) ? album . Overview : albumInfo . Overview ;
2018-07-20 22:33:56 +00:00
album . Disambiguation = albumInfo . Disambiguation ;
2018-01-18 02:28:47 +00:00
album . AlbumType = albumInfo . AlbumType ;
album . SecondaryTypes = albumInfo . SecondaryTypes ;
album . Genres = albumInfo . Genres ;
2019-04-21 02:56:03 +00:00
album . Images = albumInfo . Images . Any ( ) ? albumInfo . Images : album . Images ;
2018-12-15 00:02:43 +00:00
album . Links = albumInfo . Links ;
2018-01-18 02:28:47 +00:00
album . ReleaseDate = albumInfo . ReleaseDate ;
2018-04-08 06:48:34 +00:00
album . Ratings = albumInfo . Ratings ;
2018-12-15 00:02:43 +00:00
album . AlbumReleases = new List < AlbumRelease > ( ) ;
var remoteReleases = albumInfo . AlbumReleases . Value . DistinctBy ( m = > m . ForeignReleaseId ) . ToList ( ) ;
2019-03-15 12:10:45 +00:00
var existingReleases = _releaseService . GetReleasesForRefresh ( album . Id , remoteReleases . Select ( x = > x . ForeignReleaseId ) ) ;
2019-03-22 09:33:48 +00:00
// Keep track of which existing release we want to end up monitored
var existingToMonitor = existingReleases . Where ( x = > x . Monitored ) . OrderByDescending ( x = > x . TrackCount ) . FirstOrDefault ( ) ;
2018-12-15 00:02:43 +00:00
var newReleaseList = new List < AlbumRelease > ( ) ;
var updateReleaseList = new List < AlbumRelease > ( ) ;
2019-03-22 09:33:48 +00:00
var upToDateReleaseList = new List < AlbumRelease > ( ) ;
2018-12-15 00:02:43 +00:00
foreach ( var release in remoteReleases )
{
release . AlbumId = album . Id ;
2019-03-15 12:10:45 +00:00
release . Album = album ;
2019-03-22 09:33:48 +00:00
// force to unmonitored, then fix monitored one later
// once we have made sure that it's unique. This make sure
// that we unmonitor anything in database that shouldn't be monitored.
release . Monitored = false ;
2018-12-15 00:02:43 +00:00
var releaseToRefresh = existingReleases . SingleOrDefault ( r = > r . ForeignReleaseId = = release . ForeignReleaseId ) ;
if ( releaseToRefresh ! = null )
{
existingReleases . Remove ( releaseToRefresh ) ;
2019-03-15 12:10:45 +00:00
// copy across the db keys and check for equality
2018-12-15 00:02:43 +00:00
release . Id = releaseToRefresh . Id ;
2019-03-15 12:10:45 +00:00
release . AlbumId = releaseToRefresh . AlbumId ;
if ( ! releaseToRefresh . Equals ( release ) )
{
updateReleaseList . Add ( release ) ;
}
else
{
2019-03-22 09:33:48 +00:00
upToDateReleaseList . Add ( release ) ;
2019-03-15 12:10:45 +00:00
}
2018-12-15 00:02:43 +00:00
}
else
{
newReleaseList . Add ( release ) ;
}
2019-03-22 09:33:48 +00:00
2018-12-15 00:02:43 +00:00
album . AlbumReleases . Value . Add ( release ) ;
}
2019-03-22 09:33:48 +00:00
var refreshedToMonitor = remoteReleases . SingleOrDefault ( x = > x . ForeignReleaseId = = existingToMonitor ? . ForeignReleaseId ) ? ?
remoteReleases . OrderByDescending ( x = > x . TrackCount ) . First ( ) ;
refreshedToMonitor . Monitored = true ;
if ( upToDateReleaseList . Contains ( refreshedToMonitor ) )
{
// we weren't going to update, but have changed monitored so now need to
upToDateReleaseList . Remove ( refreshedToMonitor ) ;
updateReleaseList . Add ( refreshedToMonitor ) ;
}
else if ( updateReleaseList . Contains ( refreshedToMonitor ) & & refreshedToMonitor . Equals ( existingToMonitor ) )
{
// we were going to update because Monitored was incorrect but now it matches
// and so no need to update
updateReleaseList . Remove ( refreshedToMonitor ) ;
upToDateReleaseList . Add ( refreshedToMonitor ) ;
}
Ensure . That ( album . AlbumReleases . Value . Count ( x = > x . Monitored ) = = 1 ) . IsTrue ( ) ;
_logger . Debug ( $"{album} {upToDateReleaseList.Count} releases up to date; Deleting {existingReleases.Count}, Updating {updateReleaseList.Count}, Adding {newReleaseList.Count} releases." ) ;
2019-03-15 12:10:45 +00:00
// before deleting anything, remove musicbrainz ids for things we are deleting
_audioTagService . RemoveMusicBrainzTags ( existingReleases ) ;
2019-01-12 16:56:13 +00:00
2018-12-15 00:02:43 +00:00
_releaseService . DeleteMany ( existingReleases ) ;
2019-01-12 16:56:13 +00:00
_releaseService . UpdateMany ( updateReleaseList ) ;
_releaseService . InsertMany ( newReleaseList ) ;
2018-12-15 00:02:43 +00:00
2019-03-15 12:10:45 +00:00
// if we have updated a monitored release, refresh all file tags
forceUpdateFileTags | = updateReleaseList . Any ( x = > x . Monitored ) ;
_refreshTrackService . RefreshTrackInfo ( album , forceUpdateFileTags ) ;
2018-01-28 06:27:33 +00:00
_albumService . UpdateMany ( new List < Album > { album } ) ;
2018-04-09 03:14:26 +00:00
_logger . Debug ( "Finished album refresh for {0}" , album . Title ) ;
2018-01-18 02:28:47 +00:00
}
public void Execute ( RefreshAlbumCommand message )
{
if ( message . AlbumId . HasValue )
{
var album = _albumService . GetAlbum ( message . AlbumId . Value ) ;
2018-12-15 00:02:43 +00:00
var artist = _artistService . GetArtistByMetadataId ( album . ArtistMetadataId ) ;
2019-03-15 12:10:45 +00:00
RefreshAlbumInfo ( album , false ) ;
2018-01-18 02:28:47 +00:00
_eventAggregator . PublishEvent ( new ArtistUpdatedEvent ( artist ) ) ;
}
}
2017-06-18 02:27:01 +00:00
}
}