using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using FluentValidation ;
using FluentValidation.Results ;
using NLog ;
using NzbDrone.Common.EnsureThat ;
using NzbDrone.Common.Extensions ;
using NzbDrone.Core.Exceptions ;
using NzbDrone.Core.MetadataSource ;
using NzbDrone.Core.Organizer ;
using NzbDrone.Core.Parser ;
namespace NzbDrone.Core.Tv
{
public interface IAddSeriesService
{
Series AddSeries ( Series newSeries ) ;
List < Series > AddSeries ( List < Series > newSeries , bool ignoreErrors = false ) ;
}
public class AddSeriesService : IAddSeriesService
{
private readonly ISeriesService _seriesService ;
private readonly IProvideSeriesInfo _seriesInfo ;
private readonly IBuildFileNames _fileNameBuilder ;
private readonly IAddSeriesValidator _addSeriesValidator ;
private readonly Logger _logger ;
public AddSeriesService ( ISeriesService seriesService ,
IProvideSeriesInfo seriesInfo ,
IBuildFileNames fileNameBuilder ,
IAddSeriesValidator addSeriesValidator ,
Logger logger )
{
_seriesService = seriesService ;
_seriesInfo = seriesInfo ;
_fileNameBuilder = fileNameBuilder ;
_addSeriesValidator = addSeriesValidator ;
_logger = logger ;
}
public Series AddSeries ( Series newSeries )
{
Ensure . That ( newSeries , ( ) = > newSeries ) . IsNotNull ( ) ;
newSeries = AddSkyhookData ( newSeries ) ;
newSeries = SetPropertiesAndValidate ( newSeries ) ;
_logger . Info ( "Adding Series {0} Path: [{1}]" , newSeries , newSeries . Path ) ;
_seriesService . AddSeries ( newSeries ) ;
return newSeries ;
}
public List < Series > AddSeries ( List < Series > newSeries , bool ignoreErrors = false )
{
var added = DateTime . UtcNow ;
var seriesToAdd = new List < Series > ( ) ;
var existingSeriesTvdbIds = _seriesService . AllSeriesTvdbIds ( ) ;
foreach ( var s in newSeries )
{
if ( s . Path . IsNullOrWhiteSpace ( ) )
{
_logger . Info ( "Adding Series {0} Root Folder Path: [{1}]" , s , s . RootFolderPath ) ;
}
else
{
_logger . Info ( "Adding Series {0} Path: [{1}]" , s , s . Path ) ;
}
try
{
var series = AddSkyhookData ( s ) ;
series = SetPropertiesAndValidate ( series ) ;
series . Added = added ;
if ( existingSeriesTvdbIds . Any ( f = > f = = series . TvdbId ) )
{
_logger . Debug ( "TVDB ID {0} was not added due to validation failure: Series {1} already exists in database" , s . TvdbId , s ) ;
continue ;
}
if ( seriesToAdd . Any ( f = > f . TvdbId = = series . TvdbId ) )
{
_logger . Trace ( "TVDB ID {0} was already added from another import list, not adding series {1} again" , s . TvdbId , s ) ;
continue ;
}
var duplicateSlug = seriesToAdd . FirstOrDefault ( f = > f . TitleSlug = = series . TitleSlug ) ;
if ( duplicateSlug ! = null )
{
_logger . Debug ( "TVDB ID {0} was not added due to validation failure: Duplicate Slug {1} used by series {2}" , s . TvdbId , s . TitleSlug , duplicateSlug . TvdbId ) ;
continue ;
}
seriesToAdd . Add ( series ) ;
}
catch ( ValidationException ex )
{
if ( ! ignoreErrors )
{
throw ;
}
_logger . Debug ( "Series {0} with TVDB ID {1} was not added due to validation failures. {2}" , s , s . TvdbId , ex . Message ) ;
}
}
return _seriesService . AddSeries ( seriesToAdd ) ;
}
private Series AddSkyhookData ( Series newSeries )
{
Tuple < Series , List < Episode > > tuple ;
try
{
tuple = _seriesInfo . GetSeriesInfo ( newSeries . TvdbId ) ;
}
catch ( SeriesNotFoundException )
{
_logger . Error ( "Series {0} with TVDB ID {1} was not found, it may have been removed from TheTVDB. Path: {2}" , newSeries , newSeries . TvdbId , newSeries . Path ) ;
throw new ValidationException ( new List < ValidationFailure >
{
new ValidationFailure ( "TvdbId" , $"A series with this ID was not found. Path: {newSeries.Path}" , newSeries . TvdbId )
} ) ;
}
var series = tuple . Item1 ;
// If seasons were passed in on the new series use them, otherwise use the seasons from Skyhook
newSeries . Seasons = newSeries . Seasons ! = null & & newSeries . Seasons . Any ( ) ? newSeries . Seasons : series . Seasons ;
series . ApplyChanges ( newSeries ) ;
return series ;
}
private Series SetPropertiesAndValidate ( Series newSeries )
{
if ( string . IsNullOrWhiteSpace ( newSeries . Path ) )
{
var folderName = _fileNameBuilder . GetSeriesFolder ( newSeries ) ;
newSeries . Path = Path . Combine ( newSeries . RootFolderPath , folderName ) ;
}
newSeries . CleanTitle = newSeries . Title . CleanSeriesTitle ( ) ;
newSeries . SortTitle = SeriesTitleNormalizer . Normalize ( newSeries . Title , newSeries . TvdbId ) ;
newSeries . Added = DateTime . UtcNow ;
if ( newSeries . AddOptions ! = null & & newSeries . AddOptions . Monitor = = MonitorTypes . None )
{
newSeries . Monitored = false ;
}
var validationResult = _addSeriesValidator . Validate ( newSeries ) ;
if ( ! validationResult . IsValid )
{
throw new ValidationException ( validationResult . Errors ) ;
}
return newSeries ;
}
}
}