Radarr/src/NzbDrone.Core/MediaFiles/MovieImport/ImportDecisionMaker.cs

206 lines
8.1 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
2019-12-22 22:08:53 +00:00
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.MovieImport
{
public interface IMakeImportDecision
{
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool filterExistingFiles);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource);
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool filterExistingFiles);
ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem);
}
public class ImportDecisionMaker : IMakeImportDecision
{
private readonly IEnumerable<IImportDecisionEngineSpecification> _specifications;
private readonly IMediaFileService _mediaFileService;
2019-07-16 02:27:35 +00:00
private readonly IAggregationService _aggregationService;
private readonly IDiskProvider _diskProvider;
private readonly IDetectSample _detectSample;
private readonly IParsingService _parsingService;
private readonly Logger _logger;
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
IMediaFileService mediaFileService,
2019-07-16 02:27:35 +00:00
IAggregationService aggregationService,
IDiskProvider diskProvider,
IDetectSample detectSample,
IParsingService parsingService,
Logger logger)
{
_specifications = specifications;
_mediaFileService = mediaFileService;
2019-07-16 02:27:35 +00:00
_aggregationService = aggregationService;
_diskProvider = diskProvider;
_detectSample = detectSample;
_parsingService = parsingService;
_logger = logger;
}
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie)
{
return GetImportDecisions(videoFiles, movie, null, null, false);
}
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool filterExistingFiles)
{
return GetImportDecisions(videoFiles, movie, null, null, false, filterExistingFiles);
}
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource)
{
return GetImportDecisions(videoFiles, movie, downloadClientItem, folderInfo, sceneSource, true);
}
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool filterExistingFiles)
{
var newFiles = filterExistingFiles ? _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie) : videoFiles.ToList();
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count);
2019-07-16 02:27:35 +00:00
ParsedMovieInfo downloadClientItemInfo = null;
2019-07-16 02:27:35 +00:00
if (downloadClientItem != null)
{
downloadClientItemInfo = Parser.Parser.ParseMovieTitle(downloadClientItem.Title);
downloadClientItemInfo = _parsingService.EnhanceMovieInfo(downloadClientItemInfo);
}
var nonSampleVideoFileCount = GetNonSampleVideoFileCount(newFiles, movie);
var decisions = new List<ImportDecision>();
foreach (var file in newFiles)
{
2019-07-16 02:27:35 +00:00
var localMovie = new LocalMovie
{
Movie = movie,
DownloadClientMovieInfo = downloadClientItemInfo,
FolderMovieInfo = folderInfo,
Path = file,
SceneSource = sceneSource,
ExistingFile = movie.Path.IsParentPath(file)
2019-07-16 02:27:35 +00:00
};
decisions.AddIfNotNull(GetDecision(localMovie, downloadClientItem, nonSampleVideoFileCount > 1));
}
return decisions;
}
public ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem)
{
var reasons = _specifications.Select(c => EvaluateSpec(c, localMovie, downloadClientItem))
.Where(c => c != null);
return new ImportDecision(localMovie, reasons.ToArray());
}
2019-07-16 02:27:35 +00:00
private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
{
ImportDecision decision = null;
var fileMovieInfo = Parser.Parser.ParseMoviePath(localMovie.Path);
if (fileMovieInfo != null)
{
fileMovieInfo = _parsingService.EnhanceMovieInfo(fileMovieInfo);
2019-12-22 21:24:11 +00:00
}
2019-07-16 02:27:35 +00:00
localMovie.FileMovieInfo = fileMovieInfo;
localMovie.Size = _diskProvider.GetFileSize(localMovie.Path);
2019-07-16 02:27:35 +00:00
try
{
_aggregationService.Augment(localMovie, downloadClientItem, otherFiles);
2019-07-16 02:27:35 +00:00
if (localMovie.Movie == null)
{
2019-07-16 02:27:35 +00:00
decision = new ImportDecision(localMovie, new Rejection("Invalid movie"));
}
else
{
2019-07-16 02:27:35 +00:00
decision = GetDecision(localMovie, downloadClientItem);
}
}
2019-07-16 02:27:35 +00:00
catch (AugmentingFailedException)
{
decision = new ImportDecision(localMovie, new Rejection("Unable to parse file"));
}
catch (Exception ex)
{
2019-07-16 02:27:35 +00:00
_logger.Error(ex, "Couldn't import file. {0}", localMovie.Path);
decision = new ImportDecision(localMovie, new Rejection("Unexpected error processing file"));
}
2019-07-16 02:27:35 +00:00
if (decision == null)
{
_logger.Error("Unable to make a decision on {0}", localMovie.Path);
}
else if (decision.Rejections.Any())
{
_logger.Debug("File rejected for the following reasons: {0}", string.Join(", ", decision.Rejections));
}
else
{
_logger.Debug("File accepted");
}
return decision;
}
private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalMovie localMovie, DownloadClientItem downloadClientItem)
{
try
{
var result = spec.IsSatisfiedBy(localMovie, downloadClientItem);
if (!result.Accepted)
{
return new Rejection(result.Reason);
}
}
catch (NotImplementedException e)
{
_logger.Warn(e, "Spec " + spec.ToString() + " currently does not implement evaluation for movies.");
return null;
}
catch (Exception ex)
{
_logger.Error(ex, "Couldn't evaluate decision on {0}", localMovie.Path);
return new Rejection($"{spec.GetType().Name}: {ex.Message}");
}
return null;
}
private int GetNonSampleVideoFileCount(List<string> videoFiles, Movie movie)
{
2019-07-16 02:27:35 +00:00
return videoFiles.Count(file =>
{
var sample = _detectSample.IsSample(movie, file);
2019-07-16 02:27:35 +00:00
if (sample == DetectSampleResult.Sample)
{
return false;
}
return true;
2019-07-16 02:27:35 +00:00
});
}
}
}