mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-14 18:57:39 -07:00
Episode import uses specs and moves before import now
This commit is contained in:
parent
9ed5a06504
commit
aeb8ee06f6
22 changed files with 942 additions and 499 deletions
|
@ -6,6 +6,7 @@ using NLog;
|
|||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
@ -15,7 +16,6 @@ namespace NzbDrone.Core.MediaFiles
|
|||
{
|
||||
public interface IDiskScanService
|
||||
{
|
||||
EpisodeFile ImportFile(Series series, string filePath);
|
||||
string[] GetVideoFiles(string path, bool allDirectories = true);
|
||||
}
|
||||
|
||||
|
@ -25,19 +25,20 @@ namespace NzbDrone.Core.MediaFiles
|
|||
private static readonly string[] MediaExtensions = new[] { ".mkv", ".avi", ".wmv", ".mp4", ".mpg", ".mpeg", ".xvid", ".flv", ".mov", ".rm", ".rmvb", ".divx", ".dvr-ms", ".ts", ".ogm", ".m4v", ".strm" };
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||
private readonly IMessageAggregator _messageAggregator;
|
||||
|
||||
public DiskScanService(IDiskProvider diskProvider, ISeriesService seriesService, IMediaFileService mediaFileService, IVideoFileInfoReader videoFileInfoReader,
|
||||
IParsingService parsingService, IMessageAggregator messageAggregator)
|
||||
public DiskScanService(IDiskProvider diskProvider,
|
||||
ISeriesService seriesService,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IImportApprovedEpisodes importApprovedEpisodes,
|
||||
IMessageAggregator messageAggregator)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_seriesService = seriesService;
|
||||
_mediaFileService = mediaFileService;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_parsingService = parsingService;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_importApprovedEpisodes = importApprovedEpisodes;
|
||||
_messageAggregator = messageAggregator;
|
||||
}
|
||||
|
||||
|
@ -53,71 +54,8 @@ namespace NzbDrone.Core.MediaFiles
|
|||
|
||||
var mediaFileList = GetVideoFiles(series.Path);
|
||||
|
||||
foreach (var filePath in mediaFileList)
|
||||
{
|
||||
try
|
||||
{
|
||||
ImportFile(series, filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException("Couldn't import file " + filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
//Todo: Find the "best" episode file for all found episodes and import that one
|
||||
//Todo: Move the episode linking to here, instead of import (or rename import)
|
||||
}
|
||||
|
||||
public EpisodeFile ImportFile(Series series, string filePath)
|
||||
{
|
||||
Logger.Trace("Importing file to database [{0}]", filePath);
|
||||
|
||||
if (_mediaFileService.Exists(filePath))
|
||||
{
|
||||
Logger.Trace("[{0}] already exists in the database. skipping.", filePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
var parsedEpisode = _parsingService.GetEpisodes(filePath, series);
|
||||
|
||||
if (parsedEpisode == null || !parsedEpisode.Episodes.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var size = _diskProvider.GetFileSize(filePath);
|
||||
|
||||
if (series.SeriesType == SeriesTypes.Daily || parsedEpisode.SeasonNumber > 0)
|
||||
{
|
||||
var runTime = _videoFileInfoReader.GetRunTime(filePath);
|
||||
if (size < Constants.IgnoreFileSize && runTime.TotalMinutes < 3)
|
||||
{
|
||||
Logger.Trace("[{0}] appears to be a sample. skipping.", filePath);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && e.EpisodeFile.Value.Quality > parsedEpisode.Quality))
|
||||
{
|
||||
Logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", filePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
var episodeFile = new EpisodeFile();
|
||||
episodeFile.DateAdded = DateTime.UtcNow;
|
||||
episodeFile.SeriesId = series.Id;
|
||||
episodeFile.Path = filePath.CleanPath();
|
||||
episodeFile.Size = size;
|
||||
episodeFile.Quality = parsedEpisode.Quality;
|
||||
episodeFile.SeasonNumber = parsedEpisode.SeasonNumber;
|
||||
episodeFile.SceneName = Path.GetFileNameWithoutExtension(filePath.CleanPath());
|
||||
episodeFile.Episodes = parsedEpisode.Episodes;
|
||||
|
||||
//Todo: We shouldn't actually import the file until we confirm its the only one we want.
|
||||
//Todo: Separate episodeFile creation from importing (pass file to import to import)
|
||||
_mediaFileService.Add(episodeFile);
|
||||
return episodeFile;
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, series);
|
||||
_importApprovedEpisodes.Import(decisions);
|
||||
}
|
||||
|
||||
public string[] GetVideoFiles(string path, bool allDirectories = true)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
|
@ -19,7 +20,8 @@ namespace NzbDrone.Core.MediaFiles
|
|||
private readonly IMoveEpisodeFiles _episodeFileMover;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IMessageAggregator _messageAggregator;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
|
@ -28,7 +30,8 @@ namespace NzbDrone.Core.MediaFiles
|
|||
IMoveEpisodeFiles episodeFileMover,
|
||||
IParsingService parsingService,
|
||||
IConfigService configService,
|
||||
IMessageAggregator messageAggregator,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IImportApprovedEpisodes importApprovedEpisodes,
|
||||
Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
|
@ -37,7 +40,8 @@ namespace NzbDrone.Core.MediaFiles
|
|||
_episodeFileMover = episodeFileMover;
|
||||
_parsingService = parsingService;
|
||||
_configService = configService;
|
||||
_messageAggregator = messageAggregator;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_importApprovedEpisodes = importApprovedEpisodes;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -92,7 +96,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
}
|
||||
}
|
||||
|
||||
public void ProcessSubFolder(DirectoryInfo subfolderInfo)
|
||||
private void ProcessSubFolder(DirectoryInfo subfolderInfo)
|
||||
{
|
||||
var series = _parsingService.GetSeries(subfolderInfo.Name);
|
||||
|
||||
|
@ -102,12 +106,9 @@ namespace NzbDrone.Core.MediaFiles
|
|||
return;
|
||||
}
|
||||
|
||||
var files = _diskScanService.GetVideoFiles(subfolderInfo.FullName);
|
||||
var videoFiles = _diskScanService.GetVideoFiles(subfolderInfo.FullName);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
ProcessVideoFile(file, series);
|
||||
}
|
||||
ProcessFiles(videoFiles, series);
|
||||
}
|
||||
|
||||
private void ProcessVideoFile(string videoFile, Series series)
|
||||
|
@ -118,13 +119,13 @@ namespace NzbDrone.Core.MediaFiles
|
|||
return;
|
||||
}
|
||||
|
||||
var episodeFile = _diskScanService.ImportFile(series, videoFile);
|
||||
ProcessFiles(new [] { videoFile }, series);
|
||||
}
|
||||
|
||||
if (episodeFile != null)
|
||||
{
|
||||
_episodeFileMover.MoveEpisodeFile(episodeFile, true);
|
||||
_messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile));
|
||||
}
|
||||
private void ProcessFiles(IEnumerable<string> videoFiles, Series series)
|
||||
{
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles, series);
|
||||
_importApprovedEpisodes.Import(decisions, true);
|
||||
}
|
||||
|
||||
public void Execute(DownloadedEpisodesScanCommand message)
|
||||
|
|
|
@ -6,13 +6,15 @@ using NzbDrone.Common;
|
|||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public interface IMoveEpisodeFiles
|
||||
{
|
||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false);
|
||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile);
|
||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
|
||||
}
|
||||
|
||||
public class MoveEpisodeFiles : IMoveEpisodeFiles
|
||||
|
@ -36,56 +38,69 @@ namespace NzbDrone.Core.MediaFiles
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false)
|
||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile)
|
||||
{
|
||||
if (episodeFile == null)
|
||||
throw new ArgumentNullException("episodeFile");
|
||||
|
||||
var series = _seriesRepository.Get(episodeFile.SeriesId);
|
||||
var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
|
||||
string newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile);
|
||||
var newFile = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
|
||||
var newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile);
|
||||
var destinationFilename = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
|
||||
|
||||
//Only rename if existing and new filenames don't match
|
||||
if (DiskProvider.PathEquals(episodeFile.Path, newFile))
|
||||
{
|
||||
_logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path);
|
||||
return null;
|
||||
}
|
||||
episodeFile = MoveFile(episodeFile, destinationFilename);
|
||||
|
||||
_mediaFileService.Update(episodeFile);
|
||||
|
||||
return episodeFile;
|
||||
}
|
||||
|
||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
||||
{
|
||||
var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);
|
||||
var destinationFilename = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
|
||||
episodeFile = MoveFile(episodeFile, destinationFilename);
|
||||
|
||||
//TODO: This just re-parses the source path (which is how we got localEpisode to begin with)
|
||||
var parsedEpisodeInfo = Parser.Parser.ParsePath(localEpisode.Path);
|
||||
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(parsedEpisodeInfo, localEpisode.Series));
|
||||
|
||||
return episodeFile;
|
||||
}
|
||||
|
||||
private EpisodeFile MoveFile(EpisodeFile episodeFile, string destinationFilename)
|
||||
{
|
||||
if (!_diskProvider.FileExists(episodeFile.Path))
|
||||
{
|
||||
_logger.Error("Episode file path does not exist, {0}", episodeFile.Path);
|
||||
return null;
|
||||
}
|
||||
|
||||
_diskProvider.CreateFolder(new FileInfo(newFile).DirectoryName);
|
||||
//Only rename if existing and new filenames don't match
|
||||
if (DiskProvider.PathEquals(episodeFile.Path, destinationFilename))
|
||||
{
|
||||
_logger.Debug("Skipping file rename, source and destination are the same: {0}", episodeFile.Path);
|
||||
return null;
|
||||
}
|
||||
|
||||
_logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, newFile);
|
||||
_diskProvider.MoveFile(episodeFile.Path, newFile);
|
||||
_diskProvider.CreateFolder(new FileInfo(destinationFilename).DirectoryName);
|
||||
|
||||
_logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, destinationFilename);
|
||||
_diskProvider.MoveFile(episodeFile.Path, destinationFilename);
|
||||
|
||||
//Wrapped in Try/Catch to prevent this from causing issues with remote NAS boxes, the move worked, which is more important.
|
||||
try
|
||||
{
|
||||
_diskProvider.InheritFolderPermissions(newFile);
|
||||
_diskProvider.InheritFolderPermissions(destinationFilename);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger.Debug("Unable to apply folder permissions to: ", newFile);
|
||||
_logger.Debug("Unable to apply folder permissions to: ", destinationFilename);
|
||||
_logger.TraceException(ex.Message, ex);
|
||||
}
|
||||
|
||||
episodeFile.Path = newFile;
|
||||
_mediaFileService.Update(episodeFile);
|
||||
|
||||
var parsedEpisodeInfo = Parser.Parser.ParsePath(episodeFile.Path);
|
||||
parsedEpisodeInfo.Quality = episodeFile.Quality;
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
_messageAggregator.PublishEvent(new EpisodeDownloadedEvent(parsedEpisodeInfo, series));
|
||||
}
|
||||
|
||||
episodeFile.Path = destinationFilename;
|
||||
|
||||
return episodeFile;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IImportDecisionEngineSpecification : IRejectWithReason
|
||||
{
|
||||
bool IsSatisfiedBy(LocalEpisode localEpisode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IImportApprovedEpisodes
|
||||
{
|
||||
List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownloads = false);
|
||||
}
|
||||
|
||||
public class ImportApprovedEpisodes : IImportApprovedEpisodes
|
||||
{
|
||||
private readonly IMoveEpisodeFiles _episodeFileMover;
|
||||
private readonly MediaFileService _mediaFileService;
|
||||
private readonly DiskProvider _diskProvider;
|
||||
private readonly IMessageAggregator _messageAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportApprovedEpisodes(IMoveEpisodeFiles episodeFileMover,
|
||||
MediaFileService mediaFileService,
|
||||
DiskProvider diskProvider,
|
||||
IMessageAggregator messageAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_episodeFileMover = episodeFileMover;
|
||||
_mediaFileService = mediaFileService;
|
||||
_diskProvider = diskProvider;
|
||||
_messageAggregator = messageAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownload = false)
|
||||
{
|
||||
var qualifiedReports = GetQualifiedReports(decisions);
|
||||
var imported = new List<ImportDecision>();
|
||||
|
||||
foreach (var report in qualifiedReports)
|
||||
{
|
||||
var localEpisode = report.LocalEpisode;
|
||||
|
||||
try
|
||||
{
|
||||
if (imported.SelectMany(r => r.LocalEpisode.Episodes)
|
||||
.Select(e => e.Id)
|
||||
.ToList()
|
||||
.Intersect(localEpisode.Episodes.Select(e => e.Id))
|
||||
.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var episodeFile = new EpisodeFile();
|
||||
episodeFile.DateAdded = DateTime.UtcNow;
|
||||
episodeFile.SeriesId = localEpisode.Series.Id;
|
||||
episodeFile.Path = localEpisode.Path.CleanPath();
|
||||
episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
|
||||
episodeFile.Quality = localEpisode.Quality;
|
||||
episodeFile.SeasonNumber = localEpisode.SeasonNumber;
|
||||
episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanPath());
|
||||
episodeFile.Episodes = localEpisode.Episodes;
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
episodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode);
|
||||
}
|
||||
|
||||
_mediaFileService.Add(episodeFile);
|
||||
_messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile));
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.WarnException("Couldn't add report to download queue. " + localEpisode, e);
|
||||
}
|
||||
}
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
private List<ImportDecision> GetQualifiedReports(List<ImportDecision> decisions)
|
||||
{
|
||||
return decisions.Where(c => c.Approved)
|
||||
.OrderByDescending(c => c.LocalEpisode.Quality)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
28
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs
Normal file
28
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecision.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public class ImportDecision
|
||||
{
|
||||
public LocalEpisode LocalEpisode { get; private set; }
|
||||
public IEnumerable<string> Rejections { get; private set; }
|
||||
|
||||
public bool Approved
|
||||
{
|
||||
get
|
||||
{
|
||||
return !Rejections.Any();
|
||||
}
|
||||
}
|
||||
|
||||
public ImportDecision(LocalEpisode localEpisode, params string[] rejections)
|
||||
{
|
||||
LocalEpisode = localEpisode;
|
||||
Rejections = rejections.ToList();
|
||||
}
|
||||
}
|
||||
}
|
106
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs
Normal file
106
NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public interface IMakeImportDecision
|
||||
{
|
||||
List<ImportDecision> GetImportDecisions(IEnumerable<String> videoFiles, Series series);
|
||||
}
|
||||
|
||||
public class ImportDecisionMaker : IMakeImportDecision
|
||||
{
|
||||
private readonly IEnumerable<IRejectWithReason> _specifications;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportDecisionMaker(IEnumerable<IRejectWithReason> specifications, IParsingService parsingService, Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
_parsingService = parsingService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(IEnumerable<String> videoFiles, Series series)
|
||||
{
|
||||
return GetDecisions(videoFiles, series).ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles, Series series)
|
||||
{
|
||||
foreach (var file in videoFiles)
|
||||
{
|
||||
ImportDecision decision = null;
|
||||
|
||||
try
|
||||
{
|
||||
var parsedEpisode = _parsingService.GetEpisodes(file, series);
|
||||
|
||||
if (parsedEpisode != null)
|
||||
{
|
||||
decision = GetDecision(parsedEpisode);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
parsedEpisode = new LocalEpisode();
|
||||
parsedEpisode.Path = file;
|
||||
|
||||
decision = new ImportDecision(parsedEpisode, "Unable to parse file");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.ErrorException("Couldn't process report.", e);
|
||||
}
|
||||
|
||||
if (decision != null)
|
||||
{
|
||||
yield return decision;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ImportDecision GetDecision(LocalEpisode localEpisode)
|
||||
{
|
||||
var reasons = _specifications.Select(c => EvaluateSpec(c, localEpisode))
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c));
|
||||
|
||||
return new ImportDecision(localEpisode, reasons.ToArray());
|
||||
}
|
||||
|
||||
private string EvaluateSpec(IRejectWithReason spec, LocalEpisode localEpisode)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(spec.RejectionReason))
|
||||
{
|
||||
throw new InvalidOperationException("[Need Rejection Text]");
|
||||
}
|
||||
|
||||
var generalSpecification = spec as IImportDecisionEngineSpecification;
|
||||
if (generalSpecification != null && !generalSpecification.IsSatisfiedBy(localEpisode))
|
||||
{
|
||||
return spec.RejectionReason;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//e.Data.Add("report", remoteEpisode.Report.ToJson());
|
||||
//e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
|
||||
_logger.ErrorException("Couldn't evaluate decision on " + localEpisode.Path, e);
|
||||
return string.Format("{0}: {1}", spec.GetType().Name, e.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class NotAlreadyImportedSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NotAlreadyImportedSpecification(IMediaFileService mediaFileService, Logger logger)
|
||||
{
|
||||
_mediaFileService = mediaFileService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Is Sample"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (_mediaFileService.Exists(localEpisode.Path))
|
||||
{
|
||||
_logger.Trace("[{0}] already exists in the database. skipping.", localEpisode.Path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class NotSampleSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NotSampleSpecification(IDiskProvider diskProvider, IVideoFileInfoReader videoFileInfoReader, Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Sample"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.Series.SeriesType == SeriesTypes.Daily)
|
||||
{
|
||||
_logger.Trace("Daily Series, skipping sample check");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (localEpisode.SeasonNumber == 0)
|
||||
{
|
||||
_logger.Trace("Special, skipping sample check");
|
||||
return true;
|
||||
}
|
||||
|
||||
var size = _diskProvider.GetFileSize(localEpisode.Path);
|
||||
var runTime = _videoFileInfoReader.GetRunTime(localEpisode.Path);
|
||||
|
||||
if (size < Constants.IgnoreFileSize && runTime.TotalMinutes < 3)
|
||||
{
|
||||
_logger.Trace("[{0}] appears to be a sample.", localEpisode.Path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class UpgradeSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UpgradeSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason { get { return "Is Sample"; } }
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && e.EpisodeFile.Value.Quality > localEpisode.Quality))
|
||||
{
|
||||
_logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue