@ -1,4 +1,4 @@
using System ;
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
@ -11,7 +11,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Extras.Files ;
using NzbDrone.Core.Extras.Metadata.Files ;
using NzbDrone.Core.MediaFiles ;
using NzbDrone.Core. Tv ;
using NzbDrone.Core. Music ;
namespace NzbDrone.Core.Extras.Metadata
{
@ -24,6 +24,7 @@ namespace NzbDrone.Core.Extras.Metadata
private readonly IHttpClient _httpClient ;
private readonly IMediaFileAttributeService _mediaFileAttributeService ;
private readonly IMetadataFileService _metadataFileService ;
private readonly IAlbumService _albumService ;
private readonly Logger _logger ;
public MetadataService ( IConfigService configService ,
@ -34,6 +35,7 @@ namespace NzbDrone.Core.Extras.Metadata
IHttpClient httpClient ,
IMediaFileAttributeService mediaFileAttributeService ,
IMetadataFileService metadataFileService ,
IAlbumService albumService ,
Logger logger )
: base ( configService , diskProvider , diskTransferService , logger )
{
@ -44,19 +46,20 @@ namespace NzbDrone.Core.Extras.Metadata
_httpClient = httpClient ;
_mediaFileAttributeService = mediaFileAttributeService ;
_metadataFileService = metadataFileService ;
_albumService = albumService ;
_logger = logger ;
}
public override int Order = > 0 ;
public override IEnumerable < ExtraFile > CreateAfter SeriesScan( Series series , List < EpisodeFile > episode Files)
public override IEnumerable < ExtraFile > CreateAfter ArtistScan( Artist artist , List < Album > albums , List < TrackFile > track Files)
{
var metadataFiles = _metadataFileService . GetFilesBy Series( series . Id ) ;
_cleanMetadataService . Clean ( series ) ;
var metadataFiles = _metadataFileService . GetFilesBy Artist( artist . Id ) ;
_cleanMetadataService . Clean ( artist ) ;
if ( ! _diskProvider . FolderExists ( series . Path ) )
if ( ! _diskProvider . FolderExists ( artist . Path ) )
{
_logger . Info ( " Series folder does not exist, skipping metadata creation") ;
_logger . Info ( " Artist folder does not exist, skipping metadata creation") ;
return Enumerable . Empty < MetadataFile > ( ) ;
}
@ -66,14 +69,20 @@ namespace NzbDrone.Core.Extras.Metadata
{
var consumerFiles = GetMetadataFilesForConsumer ( consumer , metadataFiles ) ;
files . AddIfNotNull ( Process SeriesMetadata( consumer , series , consumerFiles ) ) ;
files . AddRange ( Process SeriesImages( consumer , series , consumerFiles ) ) ;
files . AddRange ( Process SeasonImages( consumer , series , consumerFiles ) ) ;
files . AddIfNotNull ( Process ArtistMetadata( consumer , artist , consumerFiles ) ) ;
files . AddRange ( Process ArtistImages( consumer , artist , consumerFiles ) ) ;
files . AddRange ( Process AlbumImages( consumer , artist , consumerFiles ) ) ;
foreach ( var episodeFile in episodeFile s)
foreach ( var album in album s)
{
files . AddIfNotNull ( ProcessEpisodeMetadata ( consumer , series , episodeFile , consumerFiles ) ) ;
files . AddRange ( ProcessEpisodeImages ( consumer , series , episodeFile , consumerFiles ) ) ;
album . Artist = artist ;
files . AddIfNotNull ( ProcessAlbumMetadata ( consumer , album , consumerFiles ) ) ;
}
foreach ( var trackFile in trackFiles )
{
files . AddIfNotNull ( ProcessEpisodeMetadata ( consumer , artist , trackFile , consumerFiles ) ) ;
files . AddRange ( ProcessEpisodeImages ( consumer , artist , trackFile , consumerFiles ) ) ;
}
}
@ -82,15 +91,15 @@ namespace NzbDrone.Core.Extras.Metadata
return files ;
}
public override IEnumerable < ExtraFile > CreateAfter EpisodeImport( Series series , EpisodeFile episode File)
public override IEnumerable < ExtraFile > CreateAfter TrackImport( Artist artist , TrackFile track File)
{
var files = new List < MetadataFile > ( ) ;
foreach ( var consumer in _metadataFactory . Enabled ( ) )
{
files . AddIfNotNull ( ProcessEpisodeMetadata ( consumer , series, episode File, new List < MetadataFile > ( ) ) ) ;
files . AddRange ( ProcessEpisodeImages ( consumer , series, episode File, new List < MetadataFile > ( ) ) ) ;
files . AddIfNotNull ( ProcessEpisodeMetadata ( consumer , artist, track File, new List < MetadataFile > ( ) ) ) ;
files . AddRange ( ProcessEpisodeImages ( consumer , artist, track File, new List < MetadataFile > ( ) ) ) ;
}
_metadataFileService . Upsert ( files ) ;
@ -98,11 +107,11 @@ namespace NzbDrone.Core.Extras.Metadata
return files ;
}
public override IEnumerable < ExtraFile > CreateAfter EpisodeImport( Series series , string seriesFolder , string season Folder)
public override IEnumerable < ExtraFile > CreateAfter TrackImport( Artist artist , string artistFolder , string album Folder)
{
var metadataFiles = _metadataFileService . GetFilesBy Series( series . Id ) ;
var metadataFiles = _metadataFileService . GetFilesBy Artist( artist . Id ) ;
if ( seriesFolder. IsNullOrWhiteSpace ( ) & & season Folder. IsNullOrWhiteSpace ( ) )
if ( artistFolder. IsNullOrWhiteSpace ( ) & & album Folder. IsNullOrWhiteSpace ( ) )
{
return new List < MetadataFile > ( ) ;
}
@ -113,15 +122,15 @@ namespace NzbDrone.Core.Extras.Metadata
{
var consumerFiles = GetMetadataFilesForConsumer ( consumer , metadataFiles ) ;
if ( series Folder. IsNotNullOrWhiteSpace ( ) )
if ( artist Folder. IsNotNullOrWhiteSpace ( ) )
{
files . AddIfNotNull ( Process SeriesMetadata( consumer , series , consumerFiles ) ) ;
files . AddRange ( Process SeriesImages( consumer , series , consumerFiles ) ) ;
files . AddIfNotNull ( Process ArtistMetadata( consumer , artist , consumerFiles ) ) ;
files . AddRange ( Process ArtistImages( consumer , artist , consumerFiles ) ) ;
}
if ( season Folder. IsNotNullOrWhiteSpace ( ) )
if ( album Folder. IsNotNullOrWhiteSpace ( ) )
{
files . AddRange ( Process SeasonImages( consumer , series , consumerFiles ) ) ;
files . AddRange ( Process AlbumImages( consumer , artist , consumerFiles ) ) ;
}
}
@ -130,9 +139,9 @@ namespace NzbDrone.Core.Extras.Metadata
return files ;
}
public override IEnumerable < ExtraFile > MoveFilesAfterRename ( Series series , List < EpisodeFile > episode Files)
public override IEnumerable < ExtraFile > MoveFilesAfterRename ( Artist artist , List < TrackFile > track Files)
{
var metadataFiles = _metadataFileService . GetFilesBy Series( series . Id ) ;
var metadataFiles = _metadataFileService . GetFilesBy Artist( artist . Id ) ;
var movedFiles = new List < MetadataFile > ( ) ;
// TODO: Move EpisodeImage and EpisodeMetadata metadata files, instead of relying on consumers to do it
@ -140,21 +149,21 @@ namespace NzbDrone.Core.Extras.Metadata
foreach ( var consumer in _metadataFactory . GetAvailableProviders ( ) )
{
foreach ( var episodeFile in episode Files)
foreach ( var trackFile in track Files)
{
var metadataFilesForConsumer = GetMetadataFilesForConsumer ( consumer , metadataFiles ) . Where ( m = > m . EpisodeFileId = = episode File. Id ) . ToList ( ) ;
var metadataFilesForConsumer = GetMetadataFilesForConsumer ( consumer , metadataFiles ) . Where ( m = > m . TrackFileId = = track File. Id ) . ToList ( ) ;
foreach ( var metadataFile in metadataFilesForConsumer )
{
var newFileName = consumer . GetFilenameAfterMove ( series, episode File, metadataFile ) ;
var existingFileName = Path . Combine ( series . Path , metadataFile . RelativePath ) ;
var newFileName = consumer . GetFilenameAfterMove ( artist, track File, metadataFile ) ;
var existingFileName = Path . Combine ( artist . Path , metadataFile . RelativePath ) ;
if ( newFileName . PathNotEquals ( existingFileName ) )
{
try
{
_diskProvider . MoveFile ( existingFileName , newFileName ) ;
metadataFile . RelativePath = series . Path . GetRelativePath ( newFileName ) ;
metadataFile . RelativePath = artist . Path . GetRelativePath ( newFileName ) ;
movedFiles . Add ( metadataFile ) ;
}
catch ( Exception ex )
@ -171,40 +180,84 @@ namespace NzbDrone.Core.Extras.Metadata
return movedFiles ;
}
public override ExtraFile Import ( Series series , EpisodeFile episode File, string path , string extension , bool readOnly )
public override ExtraFile Import ( Artist artist , TrackFile track File, string path , string extension , bool readOnly )
{
return null ;
}
private List < MetadataFile > GetMetadataFilesForConsumer ( IMetadata consumer , List < MetadataFile > series Metadata)
private List < MetadataFile > GetMetadataFilesForConsumer ( IMetadata consumer , List < MetadataFile > artist Metadata)
{
return seriesMetadata . Where ( c = > c . Consumer = = consumer . GetType ( ) . Name ) . ToList ( ) ;
return artistMetadata . Where ( c = > c . Consumer = = consumer . GetType ( ) . Name ) . ToList ( ) ;
}
private MetadataFile ProcessArtistMetadata ( IMetadata consumer , Artist artist , List < MetadataFile > existingMetadataFiles )
{
var artistMetadata = consumer . ArtistMetadata ( artist ) ;
if ( artistMetadata = = null )
{
return null ;
}
var hash = artistMetadata . Contents . SHA256Hash ( ) ;
var metadata = GetMetadataFile ( artist , existingMetadataFiles , e = > e . Type = = MetadataType . ArtistMetadata ) ? ?
new MetadataFile
{
ArtistId = artist . Id ,
Consumer = consumer . GetType ( ) . Name ,
Type = MetadataType . ArtistMetadata
} ;
if ( hash = = metadata . Hash )
{
if ( artistMetadata . RelativePath ! = metadata . RelativePath )
{
metadata . RelativePath = artistMetadata . RelativePath ;
return metadata ;
}
return null ;
}
var fullPath = Path . Combine ( artist . Path , artistMetadata . RelativePath ) ;
_logger . Debug ( "Writing Artist Metadata to: {0}" , fullPath ) ;
SaveMetadataFile ( fullPath , artistMetadata . Contents ) ;
metadata . Hash = hash ;
metadata . RelativePath = artistMetadata . RelativePath ;
metadata . Extension = Path . GetExtension ( fullPath ) ;
return metadata ;
}
private MetadataFile ProcessSeriesMetadata ( IMetadata consumer , Series series , List < MetadataFile > existingMetadataFiles )
private MetadataFile Process AlbumMetadata( IMetadata consumer , Album album , List < MetadataFile > existingMetadataFiles )
{
var seriesMetadata = consumer . SeriesMetadata ( series ) ;
var albumMetadata = consumer . AlbumMetadata ( album . Artist , album ) ;
if ( seriesMetadata = = null )
if ( album Metadata = = null )
{
return null ;
}
var hash = seriesMetadata . Contents . SHA256Hash ( ) ;
var hash = album Metadata. Contents . SHA256Hash ( ) ;
var metadata = GetMetadataFile ( series , existingMetadataFiles , e = > e . Type = = MetadataType . SeriesMetadata ) ? ?
var metadata = GetMetadataFile ( album. Artist , existingMetadataFiles , e = > e . Type = = MetadataType . AlbumMetadata & & e . AlbumId = = album . Id ) ? ?
new MetadataFile
{
SeriesId = series . Id ,
ArtistId = album . ArtistId ,
AlbumId = album . Id ,
Consumer = consumer . GetType ( ) . Name ,
Type = MetadataType . SeriesMetadata
Type = MetadataType . Album Metadata
} ;
if ( hash = = metadata . Hash )
{
if ( seriesMetadata . RelativePath ! = metadata . RelativePath )
if ( album Metadata. RelativePath ! = metadata . RelativePath )
{
metadata . RelativePath = seriesMetadata . RelativePath ;
metadata . RelativePath = album Metadata. RelativePath ;
return metadata ;
}
@ -212,35 +265,35 @@ namespace NzbDrone.Core.Extras.Metadata
return null ;
}
var fullPath = Path . Combine ( series. Path , series Metadata. RelativePath ) ;
var fullPath = Path . Combine ( album. Path , album Metadata. RelativePath ) ;
_logger . Debug ( "Writing Series Metadata to: {0}", fullPath ) ;
SaveMetadataFile ( fullPath , series Metadata. Contents ) ;
_logger . Debug ( "Writing Album Metadata to: {0}", fullPath ) ;
SaveMetadataFile ( fullPath , album Metadata. Contents ) ;
metadata . Hash = hash ;
metadata . RelativePath = series Metadata. RelativePath ;
metadata . RelativePath = album Metadata. RelativePath ;
metadata . Extension = Path . GetExtension ( fullPath ) ;
return metadata ;
}
private MetadataFile ProcessEpisodeMetadata ( IMetadata consumer , Series series , EpisodeFile episode File, List < MetadataFile > existingMetadataFiles )
private MetadataFile ProcessEpisodeMetadata ( IMetadata consumer , Artist artist , TrackFile track File, List < MetadataFile > existingMetadataFiles )
{
var episodeMetadata = consumer . EpisodeMetadata( series , episode File) ;
var episodeMetadata = consumer . TrackMetadata( artist , track File) ;
if ( episodeMetadata = = null )
{
return null ;
}
var fullPath = Path . Combine ( series . Path , episodeMetadata . RelativePath ) ;
var fullPath = Path . Combine ( artist . Path , episodeMetadata . RelativePath ) ;
var existingMetadata = GetMetadataFile ( series , existingMetadataFiles , c = > c . Type = = MetadataType . Episode Metadata & &
c . EpisodeFileId = = episode File. Id ) ;
var existingMetadata = GetMetadataFile ( artist , existingMetadataFiles , c = > c . Type = = MetadataType . Track Metadata & &
c . TrackFileId = = track File. Id ) ;
if ( existingMetadata ! = null )
{
var existingFullPath = Path . Combine ( series . Path , existingMetadata . RelativePath ) ;
var existingFullPath = Path . Combine ( artist . Path , existingMetadata . RelativePath ) ;
if ( fullPath . PathNotEquals ( existingFullPath ) )
{
_diskTransferService . TransferFile ( existingFullPath , fullPath , TransferMode . Move ) ;
@ -253,11 +306,11 @@ namespace NzbDrone.Core.Extras.Metadata
var metadata = existingMetadata ? ?
new MetadataFile
{
SeriesId = series . Id ,
SeasonNumber = episodeFile . SeasonNumber ,
EpisodeFileId = episode File. Id ,
ArtistId = artist . Id ,
AlbumId = trackFile . AlbumId ,
TrackFileId = track File. Id ,
Consumer = consumer . GetType ( ) . Name ,
Type = MetadataType . Episode Metadata,
Type = MetadataType . Track Metadata,
RelativePath = episodeMetadata . RelativePath ,
Extension = Path . GetExtension ( fullPath )
} ;
@ -267,7 +320,7 @@ namespace NzbDrone.Core.Extras.Metadata
return null ;
}
_logger . Debug ( "Writing Episode Metadata to: {0}", fullPath ) ;
_logger . Debug ( "Writing Track Metadata to: {0}", fullPath ) ;
SaveMetadataFile ( fullPath , episodeMetadata . Contents ) ;
metadata . Hash = hash ;
@ -275,32 +328,32 @@ namespace NzbDrone.Core.Extras.Metadata
return metadata ;
}
private List < MetadataFile > Process SeriesImages( IMetadata consumer , Series series , List < MetadataFile > existingMetadataFiles )
private List < MetadataFile > Process ArtistImages( IMetadata consumer , Artist artist , List < MetadataFile > existingMetadataFiles )
{
var result = new List < MetadataFile > ( ) ;
foreach ( var image in consumer . SeriesImages( series ) )
foreach ( var image in consumer . ArtistImages( artist ) )
{
var fullPath = Path . Combine ( series . Path , image . RelativePath ) ;
var fullPath = Path . Combine ( artist . Path , image . RelativePath ) ;
if ( _diskProvider . FileExists ( fullPath ) )
{
_logger . Debug ( " Series image already exists: {0}", fullPath ) ;
_logger . Debug ( " Artist image already exists: {0}", fullPath ) ;
continue ;
}
var metadata = GetMetadataFile ( series , existingMetadataFiles , c = > c . Type = = MetadataType . Series Image & &
var metadata = GetMetadataFile ( artist , existingMetadataFiles , c = > c . Type = = MetadataType . Artist Image & &
c . RelativePath = = image . RelativePath ) ? ?
new MetadataFile
{
SeriesId = series . Id ,
ArtistId = artist . Id ,
Consumer = consumer . GetType ( ) . Name ,
Type = MetadataType . Series Image,
Type = MetadataType . Artist Image,
RelativePath = image . RelativePath ,
Extension = Path . GetExtension ( fullPath )
} ;
DownloadImage ( series , image ) ;
DownloadImage ( artist , image ) ;
result . Add ( metadata ) ;
}
@ -308,36 +361,38 @@ namespace NzbDrone.Core.Extras.Metadata
return result ;
}
private List < MetadataFile > Process SeasonImages( IMetadata consumer , Series series , List < MetadataFile > existingMetadataFiles )
private List < MetadataFile > Process AlbumImages( IMetadata consumer , Artist artist , List < MetadataFile > existingMetadataFiles )
{
var result = new List < MetadataFile > ( ) ;
foreach ( var season in series . Seasons )
var albums = _albumService . GetAlbumsByArtist ( artist . Id ) ;
foreach ( var album in albums )
{
foreach ( var image in consumer . SeasonImages ( series , season ) )
foreach ( var image in consumer . AlbumImages( artist , album ) )
{
var fullPath = Path . Combine ( series . Path , image . RelativePath ) ;
var fullPath = Path . Combine ( artist . Path , image . RelativePath ) ;
if ( _diskProvider . FileExists ( fullPath ) )
{
_logger . Debug ( " Season image already exists: {0}", fullPath ) ;
_logger . Debug ( " Album image already exists: {0}", fullPath ) ;
continue ;
}
var metadata = GetMetadataFile ( series , existingMetadataFiles , c = > c . Type = = MetadataType . Season Image & &
c . SeasonNumber = = season . SeasonNumber & &
var metadata = GetMetadataFile ( artist , existingMetadataFiles , c = > c . Type = = MetadataType . Album Image & &
c . AlbumId = = album . Id & &
c . RelativePath = = image . RelativePath ) ? ?
new MetadataFile
{
SeriesId = series . Id ,
SeasonNumber = season . SeasonNumber ,
ArtistId = artist . Id ,
AlbumId = album . Id ,
Consumer = consumer . GetType ( ) . Name ,
Type = MetadataType . Season Image,
Type = MetadataType . Album Image,
RelativePath = image . RelativePath ,
Extension = Path . GetExtension ( fullPath )
} ;
DownloadImage ( series , image ) ;
DownloadImage ( artist , image ) ;
result . Add ( metadata ) ;
}
@ -346,26 +401,26 @@ namespace NzbDrone.Core.Extras.Metadata
return result ;
}
private List < MetadataFile > ProcessEpisodeImages ( IMetadata consumer , Series series , EpisodeFile episode File, List < MetadataFile > existingMetadataFiles )
private List < MetadataFile > ProcessEpisodeImages ( IMetadata consumer , Artist artist , TrackFile track File, List < MetadataFile > existingMetadataFiles )
{
var result = new List < MetadataFile > ( ) ;
foreach ( var image in consumer . EpisodeImages( series , episode File) )
foreach ( var image in consumer . TrackImages( artist , track File) )
{
var fullPath = Path . Combine ( series . Path , image . RelativePath ) ;
var fullPath = Path . Combine ( artist . Path , image . RelativePath ) ;
if ( _diskProvider . FileExists ( fullPath ) )
{
_logger . Debug ( " Episode image already exists: {0}", fullPath ) ;
_logger . Debug ( " Track image already exists: {0}", fullPath ) ;
continue ;
}
var existingMetadata = GetMetadataFile ( series , existingMetadataFiles , c = > c . Type = = MetadataType . Episode Image & &
c . EpisodeFileId = = episode File. Id ) ;
var existingMetadata = GetMetadataFile ( artist , existingMetadataFiles , c = > c . Type = = MetadataType . Track Image & &
c . TrackFileId = = track File. Id ) ;
if ( existingMetadata ! = null )
{
var existingFullPath = Path . Combine ( series . Path , existingMetadata . RelativePath ) ;
var existingFullPath = Path . Combine ( artist . Path , existingMetadata . RelativePath ) ;
if ( fullPath . PathNotEquals ( existingFullPath ) )
{
_diskTransferService . TransferFile ( existingFullPath , fullPath , TransferMode . Move ) ;
@ -378,16 +433,16 @@ namespace NzbDrone.Core.Extras.Metadata
var metadata = existingMetadata ? ?
new MetadataFile
{
SeriesId = series . Id ,
SeasonNumber = episodeFile . SeasonNumber ,
EpisodeFileId = episode File. Id ,
ArtistId = artist . Id ,
AlbumId = trackFile . AlbumId ,
TrackFileId = track File. Id ,
Consumer = consumer . GetType ( ) . Name ,
Type = MetadataType . Episode Image,
Type = MetadataType . Track Image,
RelativePath = image . RelativePath ,
Extension = Path . GetExtension ( fullPath )
} ;
DownloadImage ( series , image ) ;
DownloadImage ( artist , image ) ;
result . Add ( metadata ) ;
}
@ -395,9 +450,9 @@ namespace NzbDrone.Core.Extras.Metadata
return result ;
}
private void DownloadImage ( Series series , ImageFileResult image )
private void DownloadImage ( Artist artist , ImageFileResult image )
{
var fullPath = Path . Combine ( series . Path , image . RelativePath ) ;
var fullPath = Path . Combine ( artist . Path , image . RelativePath ) ;
try
{
@ -413,11 +468,11 @@ namespace NzbDrone.Core.Extras.Metadata
}
catch ( WebException ex )
{
_logger . Warn ( ex , "Couldn't download image {0} for {1}. {2}" , image . Url , series , ex . Message ) ;
_logger . Warn ( ex , "Couldn't download image {0} for {1}. {2}" , image . Url , artist , ex . Message ) ;
}
catch ( Exception ex )
{
_logger . Error ( ex , "Couldn't download image {0} for {1}. {2}" , image . Url , series , ex . Message ) ;
_logger . Error ( ex , "Couldn't download image {0} for {1}. {2}" , image . Url , artist , ex . Message ) ;
}
}
@ -427,7 +482,7 @@ namespace NzbDrone.Core.Extras.Metadata
_mediaFileAttributeService . SetFilePermissions ( path ) ;
}
private MetadataFile GetMetadataFile ( Series series , List < MetadataFile > existingMetadataFiles , Func < MetadataFile , bool > predicate )
private MetadataFile GetMetadataFile ( Artist artist , List < MetadataFile > existingMetadataFiles , Func < MetadataFile , bool > predicate )
{
var matchingMetadataFiles = existingMetadataFiles . Where ( predicate ) . ToList ( ) ;
@ -439,7 +494,7 @@ namespace NzbDrone.Core.Extras.Metadata
//Remove duplicate metadata files from DB and disk
foreach ( var file in matchingMetadataFiles . Skip ( 1 ) )
{
var path = Path . Combine ( series . Path , file . RelativePath ) ;
var path = Path . Combine ( artist . Path , file . RelativePath ) ;
_logger . Debug ( "Removing duplicate Metadata file: {0}" , path ) ;