mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-26 22:46:53 -04:00
Added: A Huge Cleanup of old Series Code. (Let's pray nothing breaks :P) (#2589)
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public interface IDetectSample
|
||||
{
|
||||
bool IsSample(Movie movie, QualityModel quality, string path, long size, bool isSpecial);
|
||||
}
|
||||
|
||||
public class DetectSample : IDetectSample
|
||||
{
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private static List<Quality> _largeSampleSizeQualities = new List<Quality> { Quality.HDTV1080p, Quality.WEBDL1080p, Quality.Bluray1080p };
|
||||
|
||||
public DetectSample(IVideoFileInfoReader videoFileInfoReader, Logger logger)
|
||||
{
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public static long SampleSizeLimit => 70.Megabytes();
|
||||
|
||||
public bool IsSample(Movie movie, QualityModel quality, string path, long size, bool isSpecial)
|
||||
{
|
||||
if (isSpecial)
|
||||
{
|
||||
_logger.Debug("Special, skipping sample check");
|
||||
return false;
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(path);
|
||||
|
||||
if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Debug("Skipping sample check for .flv file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extension != null && extension.Equals(".strm", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Debug("Skipping sample check for .strm file");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var runTime = _videoFileInfoReader.GetRunTime(path);
|
||||
var minimumRuntime = GetMinimumAllowedRuntime(movie);
|
||||
|
||||
if (runTime.TotalMinutes.Equals(0))
|
||||
{
|
||||
_logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (runTime.TotalSeconds < minimumRuntime)
|
||||
{
|
||||
_logger.Debug("[{0}] appears to be a sample. Runtime: {1} seconds. Expected at least: {2} seconds", path, runTime, minimumRuntime);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
_logger.Debug("Falling back to file size detection");
|
||||
|
||||
return CheckSize(size, quality);
|
||||
}
|
||||
|
||||
_logger.Debug("Runtime is over 90 seconds");
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckSize(long size, QualityModel quality)
|
||||
{
|
||||
if (_largeSampleSizeQualities.Contains(quality.Quality))
|
||||
{
|
||||
if (size < SampleSizeLimit * 2)
|
||||
{
|
||||
_logger.Debug("1080p file is less than sample limit");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (size < SampleSizeLimit)
|
||||
{
|
||||
_logger.Debug("File is less than sample limit");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int GetMinimumAllowedRuntime(Movie movie)
|
||||
{
|
||||
if (movie.Runtime < 1)
|
||||
{
|
||||
return 5 * 60;
|
||||
}
|
||||
|
||||
return movie.Runtime / 5 * 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public interface IImportDecisionEngineSpecification
|
||||
{
|
||||
Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Extras;
|
||||
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public interface IImportApprovedMovie
|
||||
{
|
||||
List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto);
|
||||
}
|
||||
|
||||
public class ImportApprovedMovie : IImportApprovedMovie
|
||||
{
|
||||
private readonly IUpgradeMediaFiles _movieFileUpgrader;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IExtraService _extraService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportApprovedMovie(IUpgradeMediaFiles movieFileUpgrader,
|
||||
IMediaFileService mediaFileService,
|
||||
IExtraService extraService,
|
||||
IDiskProvider diskProvider,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_movieFileUpgrader = movieFileUpgrader;
|
||||
_mediaFileService = mediaFileService;
|
||||
_extraService = extraService;
|
||||
_diskProvider = diskProvider;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownload, DownloadClientItem downloadClientItem = null, ImportMode importMode = ImportMode.Auto)
|
||||
{
|
||||
_logger.Debug("Decisions: {0}", decisions.Count);
|
||||
|
||||
//I added a null op for the rare case that the quality is null. TODO: find out why that would even happen in the first place.
|
||||
var qualifiedImports = decisions.Where(c => c.Approved)
|
||||
.GroupBy(c => c.LocalMovie.Movie.Id, (i, s) => s
|
||||
.OrderByDescending(c => c.LocalMovie.Quality ?? new QualityModel{Quality = Quality.Unknown}, new QualityModelComparer(s.First().LocalMovie.Movie.Profile))
|
||||
.ThenByDescending(c => c.LocalMovie.Size))
|
||||
.SelectMany(c => c)
|
||||
.ToList();
|
||||
|
||||
|
||||
|
||||
var importResults = new List<ImportResult>();
|
||||
|
||||
foreach (var importDecision in qualifiedImports.OrderBy(e => e.LocalMovie.Size)
|
||||
.ThenByDescending(e => e.LocalMovie.Size))
|
||||
{
|
||||
var localMovie = importDecision.LocalMovie;
|
||||
var oldFiles = new List<MovieFile>();
|
||||
|
||||
try
|
||||
{
|
||||
//check if already imported
|
||||
if (importResults.Select(r => r.ImportDecision.LocalMovie.Movie)
|
||||
.Select(m => m.Id).Contains(localMovie.Movie.Id))
|
||||
{
|
||||
importResults.Add(new ImportResult(importDecision, "Movie has already been imported"));
|
||||
continue;
|
||||
}
|
||||
|
||||
var movieFile = new MovieFile();
|
||||
movieFile.DateAdded = DateTime.UtcNow;
|
||||
movieFile.MovieId = localMovie.Movie.Id;
|
||||
movieFile.Path = localMovie.Path.CleanFilePath();
|
||||
movieFile.Size = _diskProvider.GetFileSize(localMovie.Path);
|
||||
movieFile.Quality = localMovie.Quality;
|
||||
movieFile.MediaInfo = localMovie.MediaInfo;
|
||||
movieFile.Movie = localMovie.Movie;
|
||||
movieFile.ReleaseGroup = localMovie.ParsedMovieInfo.ReleaseGroup;
|
||||
movieFile.Edition = localMovie.ParsedMovieInfo.Edition;
|
||||
|
||||
bool copyOnly;
|
||||
switch (importMode)
|
||||
{
|
||||
default:
|
||||
case ImportMode.Auto:
|
||||
copyOnly = downloadClientItem != null && downloadClientItem.IsReadOnly;
|
||||
break;
|
||||
case ImportMode.Move:
|
||||
copyOnly = false;
|
||||
break;
|
||||
case ImportMode.Copy:
|
||||
copyOnly = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
movieFile.SceneName = GetSceneName(downloadClientItem, localMovie);
|
||||
|
||||
var moveResult = _movieFileUpgrader.UpgradeMovieFile(movieFile, localMovie, copyOnly); //TODO: Check if this works
|
||||
oldFiles = moveResult.OldFiles;
|
||||
}
|
||||
else
|
||||
{
|
||||
movieFile.RelativePath = localMovie.Movie.Path.GetRelativePath(movieFile.Path);
|
||||
}
|
||||
|
||||
_mediaFileService.Add(movieFile);
|
||||
importResults.Add(new ImportResult(importDecision));
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
_extraService.ImportExtraFiles(localMovie, movieFile, copyOnly);
|
||||
}
|
||||
|
||||
if (downloadClientItem != null)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, movieFile, newDownload, downloadClientItem.DownloadClient, downloadClientItem.DownloadId, downloadClientItem.IsReadOnly));
|
||||
}
|
||||
else
|
||||
{
|
||||
_eventAggregator.PublishEvent(new MovieImportedEvent(localMovie, movieFile, newDownload));
|
||||
}
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new MovieDownloadedEvent(localMovie, movieFile, oldFiles, downloadClientItem));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warn(e, "Couldn't import movie " + localMovie);
|
||||
importResults.Add(new ImportResult(importDecision, "Failed to import movie"));
|
||||
}
|
||||
}
|
||||
|
||||
//Adding all the rejected decisions
|
||||
importResults.AddRange(decisions.Where(c => !c.Approved)
|
||||
.Select(d => new ImportResult(d, d.Rejections.Select(r => r.Reason).ToArray())));
|
||||
|
||||
return importResults;
|
||||
}
|
||||
|
||||
private string GetSceneName(DownloadClientItem downloadClientItem, LocalMovie localMovie)
|
||||
{
|
||||
if (downloadClientItem != null)
|
||||
{
|
||||
var title = Parser.Parser.RemoveFileExtension(downloadClientItem.Title);
|
||||
|
||||
var parsedTitle = Parser.Parser.ParseMovieTitle(title, false);
|
||||
|
||||
if (parsedTitle != null)
|
||||
{
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileNameWithoutExtension(localMovie.Path.CleanFilePath());
|
||||
|
||||
if (SceneChecker.IsSceneTitle(fileName))
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public class ImportDecision
|
||||
{
|
||||
public LocalMovie LocalMovie { get; private set; }
|
||||
public IEnumerable<Rejection> Rejections { get; private set; }
|
||||
|
||||
public bool Approved => Rejections.Empty();
|
||||
|
||||
public ImportDecision(LocalMovie localMovie, params Rejection[] rejections)
|
||||
{
|
||||
LocalMovie = localMovie;
|
||||
Rejections = rejections.ToList();
|
||||
//LocalMovie = new LocalMovie
|
||||
//{
|
||||
// Quality = localMovie.Quality,
|
||||
// ExistingFile = localMovie.ExistingFile,
|
||||
// MediaInfo = localMovie.MediaInfo,
|
||||
// ParsedMovieInfo = localMovie.ParsedMovieInfo,
|
||||
// Path = localMovie.Path,
|
||||
// Size = localMovie.Size
|
||||
//};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
|
||||
|
||||
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 shouldCheckQuality);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource);
|
||||
}
|
||||
|
||||
public class ImportDecisionMaker : IMakeImportDecision
|
||||
{
|
||||
private readonly IEnumerable<IImportDecisionEngineSpecification> _specifications;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly IQualityDefinitionService _qualitiesService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
|
||||
IParsingService parsingService,
|
||||
IMediaFileService mediaFileService,
|
||||
IDiskProvider diskProvider,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
IDetectSample detectSample,
|
||||
IQualityDefinitionService qualitiesService,
|
||||
Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
_parsingService = parsingService;
|
||||
_mediaFileService = mediaFileService;
|
||||
_diskProvider = diskProvider;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_detectSample = detectSample;
|
||||
_qualitiesService = qualitiesService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie)
|
||||
{
|
||||
return GetImportDecisions(videoFiles, movie, null, null, true, false);
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool shouldCheckQuality = false)
|
||||
{
|
||||
return GetImportDecisions(videoFiles, movie, null, null, true, shouldCheckQuality);
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource)
|
||||
{
|
||||
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie);
|
||||
|
||||
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());
|
||||
|
||||
var shouldUseFolderName = ShouldUseFolderName(videoFiles, movie, folderInfo);
|
||||
var decisions = new List<ImportDecision>();
|
||||
|
||||
foreach (var file in newFiles)
|
||||
{
|
||||
decisions.AddIfNotNull(GetDecision(file, movie, downloadClientItem, folderInfo, sceneSource, shouldUseFolderName));
|
||||
}
|
||||
|
||||
return decisions;
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality)
|
||||
{
|
||||
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie);
|
||||
|
||||
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());
|
||||
|
||||
var shouldUseFolderName = ShouldUseFolderName(videoFiles, movie, folderInfo);
|
||||
var decisions = new List<ImportDecision>();
|
||||
|
||||
foreach (var file in newFiles)
|
||||
{
|
||||
decisions.AddIfNotNull(GetDecision(file, movie, downloadClientItem, folderInfo, sceneSource, shouldUseFolderName, shouldCheckQuality));
|
||||
}
|
||||
|
||||
return decisions;
|
||||
}
|
||||
|
||||
private ImportDecision GetDecision(string file, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName, bool shouldCheckQuality = false)
|
||||
{
|
||||
ImportDecision decision = null;
|
||||
|
||||
try
|
||||
{
|
||||
var localMovie = _parsingService.GetLocalMovie(file, movie, shouldUseFolderName ? folderInfo : null, sceneSource);
|
||||
|
||||
if (localMovie != null)
|
||||
{
|
||||
localMovie.Quality = GetQuality(folderInfo, localMovie.Quality, movie);
|
||||
localMovie.Size = _diskProvider.GetFileSize(file);
|
||||
|
||||
_logger.Debug("Size: {0}", localMovie.Size);
|
||||
var current = localMovie.Quality;
|
||||
localMovie.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
|
||||
//TODO: make it so media info doesn't ruin the import process of a new movie
|
||||
if (sceneSource && ShouldCheckQualityForParsedQuality(current.Quality))
|
||||
{
|
||||
|
||||
if (shouldCheckQuality)
|
||||
{
|
||||
_logger.Debug("Checking quality for this video file to make sure nothing mismatched.");
|
||||
var width = localMovie.MediaInfo.Width;
|
||||
|
||||
var qualityName = current.Quality.Name.ToLower();
|
||||
QualityModel updated = null;
|
||||
if (width > 2000)
|
||||
{
|
||||
if (qualityName.Contains("bluray"))
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray2160p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("webdl"))
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL2160p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("hdtv"))
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV2160p);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var def = _qualitiesService.Get(Quality.HDTV2160p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV2160p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.WEBDL2160p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL2160p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.Bluray2160p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray2160p);
|
||||
}
|
||||
if (updated == null)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray2160p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (width > 1400)
|
||||
{
|
||||
if (qualityName.Contains("bluray"))
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray1080p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("webdl"))
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL1080p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("hdtv"))
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV1080p);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var def = _qualitiesService.Get(Quality.HDTV1080p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV1080p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.WEBDL1080p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL1080p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.Bluray1080p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray1080p);
|
||||
}
|
||||
if (updated == null)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray1080p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
if (width > 900)
|
||||
{
|
||||
if (qualityName.Contains("bluray"))
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray720p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("webdl"))
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL720p);
|
||||
}
|
||||
|
||||
else if (qualityName.Contains("hdtv"))
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV720p);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
var def = _qualitiesService.Get(Quality.HDTV720p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.HDTV720p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.WEBDL720p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.WEBDL720p);
|
||||
}
|
||||
def = _qualitiesService.Get(Quality.Bluray720p);
|
||||
if (localMovie.Size > def.MinSize && def.MaxSize > localMovie.Size)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray720p);
|
||||
}
|
||||
if (updated == null)
|
||||
{
|
||||
updated = new QualityModel(Quality.Bluray720p);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (updated != null && updated != current)
|
||||
{
|
||||
_logger.Debug("Quality ({0}) of the file is different than the one we have ({1})", updated, current);
|
||||
updated.QualitySource = QualitySource.MediaInfo;
|
||||
localMovie.Quality = updated;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
decision = GetDecision(localMovie, downloadClientItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
decision = GetDecision(localMovie, downloadClientItem);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
localMovie = new LocalMovie();
|
||||
localMovie.Path = file;
|
||||
|
||||
decision = new ImportDecision(localMovie, new Rejection("Unable to parse file"));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Couldn't import file. " + file);
|
||||
|
||||
var localMovie = new LocalMovie { Path = file };
|
||||
decision = new ImportDecision(localMovie, new Rejection("Unexpected error processing file"));
|
||||
}
|
||||
|
||||
//LocalMovie nullMovie = null;
|
||||
|
||||
//decision = new ImportDecision(nullMovie, new Rejection("IMPLEMENTATION MISSING!!!"));
|
||||
|
||||
return decision;
|
||||
}
|
||||
|
||||
private 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());
|
||||
}
|
||||
|
||||
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 e)
|
||||
{
|
||||
//e.Data.Add("report", remoteEpisode.Report.ToJson());
|
||||
//e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
|
||||
_logger.Error(e, "Couldn't evaluate decision on " + localMovie.Path);
|
||||
return new Rejection(string.Format("{0}: {1}", spec.GetType().Name, e.Message));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool ShouldUseFolderName(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo)
|
||||
{
|
||||
if (folderInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//if (folderInfo.FullSeason)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
return videoFiles.Count(file =>
|
||||
{
|
||||
var size = _diskProvider.GetFileSize(file);
|
||||
var fileQuality = QualityParser.ParseQuality(file);
|
||||
//var sample = null;//_detectSample.IsSample(movie, GetQuality(folderInfo, fileQuality, movie), file, size, folderInfo.IsPossibleSpecialEpisode); //Todo to this
|
||||
|
||||
return true;
|
||||
|
||||
//if (sample)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SceneChecker.IsSceneTitle(Path.GetFileName(file)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}) == 1;
|
||||
}
|
||||
|
||||
private QualityModel GetQuality(ParsedMovieInfo folderInfo, QualityModel fileQuality, Movie movie)
|
||||
{
|
||||
if (UseFolderQuality(folderInfo, fileQuality, movie))
|
||||
{
|
||||
_logger.Debug("Using quality from folder: {0}", folderInfo.Quality);
|
||||
return folderInfo.Quality;
|
||||
}
|
||||
|
||||
return fileQuality;
|
||||
}
|
||||
|
||||
private bool UseFolderQuality(ParsedMovieInfo folderInfo, QualityModel fileQuality, Movie movie)
|
||||
{
|
||||
if (folderInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (folderInfo.Quality.Quality == Quality.Unknown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileQuality.QualitySource == QualitySource.Extension)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (new QualityModelComparer(movie.Profile).Compare(folderInfo.Quality, fileQuality) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ShouldCheckQualityForParsedQuality(Quality quality)
|
||||
{
|
||||
List<Quality> shouldNotCheck = new List<Quality> { Quality.WORKPRINT, Quality.TELECINE, Quality.TELESYNC,
|
||||
Quality.DVDSCR, Quality.DVD, Quality.CAM, Quality.DVDR, Quality.Remux1080p, Quality.Remux2160p, Quality.REGIONAL
|
||||
};
|
||||
|
||||
if (shouldNotCheck.Contains(quality))
|
||||
{
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public enum ImportMode
|
||||
{
|
||||
Auto = 0,
|
||||
Move = 1,
|
||||
Copy = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public class ImportResult
|
||||
{
|
||||
public ImportDecision ImportDecision { get; private set; }
|
||||
public List<string> Errors { get; private set; }
|
||||
|
||||
public ImportResultType Result
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Errors.Any())
|
||||
{
|
||||
if (ImportDecision.Approved)
|
||||
{
|
||||
return ImportResultType.Skipped;
|
||||
}
|
||||
|
||||
return ImportResultType.Rejected;
|
||||
}
|
||||
|
||||
return ImportResultType.Imported;
|
||||
}
|
||||
}
|
||||
|
||||
public ImportResult(ImportDecision importDecision, params string[] errors)
|
||||
{
|
||||
Ensure.That(importDecision, () => importDecision).IsNotNull();
|
||||
|
||||
ImportDecision = importDecision;
|
||||
Errors = errors.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public enum ImportResultType
|
||||
{
|
||||
Imported,
|
||||
Rejected,
|
||||
Skipped
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
{
|
||||
public class ManualImportCommand : Command
|
||||
{
|
||||
public List<ManualImportFile> Files { get; set; }
|
||||
|
||||
public override bool SendUpdatesToClient => true;
|
||||
|
||||
public ImportMode ImportMode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
{
|
||||
public class ManualImportFile
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public int SeriesId { get; set; }
|
||||
public List<int> EpisodeIds { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
public int MovieId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
{
|
||||
public class ManualImportItem
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string RelativePath { get; set; }
|
||||
public string Name { get; set; }
|
||||
public long Size { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
public IEnumerable<Rejection> Rejections { get; set; }
|
||||
public Movie Movie { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
{
|
||||
public interface IManualImportService
|
||||
{
|
||||
List<ManualImportItem> GetMediaFiles(string path, string downloadId);
|
||||
}
|
||||
|
||||
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IImportApprovedMovie _importApprovedMovie;
|
||||
private readonly ITrackedDownloadService _trackedDownloadService;
|
||||
private readonly IDownloadedMovieImportService _downloadedMovieImportService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IConfigService _config;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ManualImportService(IDiskProvider diskProvider,
|
||||
IParsingService parsingService,
|
||||
IDiskScanService diskScanService,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IMovieService movieService,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
IImportApprovedMovie importApprovedMovie,
|
||||
ITrackedDownloadService trackedDownloadService,
|
||||
IDownloadedMovieImportService downloadedMovieImportService,
|
||||
IEventAggregator eventAggregator,
|
||||
IConfigService config,
|
||||
Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_parsingService = parsingService;
|
||||
_diskScanService = diskScanService;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_movieService = movieService;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_importApprovedMovie = importApprovedMovie;
|
||||
_trackedDownloadService = trackedDownloadService;
|
||||
_downloadedMovieImportService = downloadedMovieImportService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ManualImportItem> GetMediaFiles(string path, string downloadId)
|
||||
{
|
||||
if (downloadId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var trackedDownload = _trackedDownloadService.Find(downloadId);
|
||||
|
||||
if (trackedDownload == null)
|
||||
{
|
||||
return new List<ManualImportItem>();
|
||||
}
|
||||
|
||||
path = trackedDownload.DownloadItem.OutputPath.FullPath;
|
||||
}
|
||||
|
||||
if (!_diskProvider.FolderExists(path))
|
||||
{
|
||||
if (!_diskProvider.FileExists(path))
|
||||
{
|
||||
return new List<ManualImportItem>();
|
||||
}
|
||||
|
||||
return new List<ManualImportItem> { ProcessFile(path, downloadId) };
|
||||
}
|
||||
|
||||
return ProcessFolder(path, downloadId);
|
||||
}
|
||||
|
||||
private List<ManualImportItem> ProcessFolder(string folder, string downloadId)
|
||||
{
|
||||
DownloadClientItem downloadClientItem = null;
|
||||
var directoryInfo = new DirectoryInfo(folder);
|
||||
var movie = _parsingService.GetMovie(directoryInfo.Name);
|
||||
|
||||
if (downloadId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var trackedDownload = _trackedDownloadService.Find(downloadId);
|
||||
downloadClientItem = trackedDownload.DownloadItem;
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
movie = trackedDownload.RemoteMovie.Movie;
|
||||
}
|
||||
}
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
var files = _diskScanService.GetVideoFiles(folder);
|
||||
|
||||
return files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList();
|
||||
}
|
||||
|
||||
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name, _config.ParsingLeniency > 0);
|
||||
var movieFiles = _diskScanService.GetVideoFiles(folder).ToList();
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(movieFiles, movie, downloadClientItem, folderInfo, SceneSource(movie, folder), false);
|
||||
|
||||
return decisions.Select(decision => MapItem(decision, folder, downloadId)).ToList();
|
||||
}
|
||||
|
||||
private ManualImportItem ProcessFile(string file, string downloadId, string folder = null)
|
||||
{
|
||||
if (folder.IsNullOrWhiteSpace())
|
||||
{
|
||||
folder = new FileInfo(file).Directory.FullName;
|
||||
}
|
||||
|
||||
DownloadClientItem downloadClientItem = null;
|
||||
var relativeFile = folder.GetRelativePath(file);
|
||||
|
||||
var movie = _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]);
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
movie = _parsingService.GetMovie(relativeFile);
|
||||
}
|
||||
|
||||
if (downloadId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var trackedDownload = _trackedDownloadService.Find(downloadId);
|
||||
downloadClientItem = trackedDownload.DownloadItem;
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
movie = trackedDownload.RemoteMovie.Movie;
|
||||
}
|
||||
}
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
var localMovie = new LocalMovie()
|
||||
{
|
||||
Path = file,
|
||||
Quality = QualityParser.ParseQuality(file),
|
||||
Size = _diskProvider.GetFileSize(file)
|
||||
};
|
||||
|
||||
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), folder, downloadId);
|
||||
}
|
||||
|
||||
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
|
||||
movie, downloadClientItem, null, SceneSource(movie, folder), true);
|
||||
|
||||
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : new ManualImportItem
|
||||
{
|
||||
DownloadId = downloadId,
|
||||
Path = file,
|
||||
RelativePath = folder.GetRelativePath(file),
|
||||
Name = Path.GetFileNameWithoutExtension(file),
|
||||
Rejections = new List<Rejection>
|
||||
{
|
||||
new Rejection("Unable to process file")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private bool SceneSource(Movie movie, string folder)
|
||||
{
|
||||
return !(movie.Path.PathEquals(folder) || movie.Path.IsParentPath(folder));
|
||||
}
|
||||
|
||||
private ManualImportItem MapItem(ImportDecision decision, string folder, string downloadId)
|
||||
{
|
||||
var item = new ManualImportItem();
|
||||
|
||||
item.Path = decision.LocalMovie.Path;
|
||||
item.RelativePath = folder.GetRelativePath(decision.LocalMovie.Path);
|
||||
item.Name = Path.GetFileNameWithoutExtension(decision.LocalMovie.Path);
|
||||
item.DownloadId = downloadId;
|
||||
|
||||
if (decision.LocalMovie.Movie != null)
|
||||
{
|
||||
item.Movie = decision.LocalMovie.Movie;
|
||||
}
|
||||
|
||||
item.Quality = decision.LocalMovie.Quality;
|
||||
item.Size = _diskProvider.GetFileSize(decision.LocalMovie.Path);
|
||||
item.Rejections = decision.Rejections;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public void Execute(ManualImportCommand message)
|
||||
{
|
||||
_logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode);
|
||||
|
||||
var imported = new List<ImportResult>();
|
||||
var importedTrackedDownload = new List<ManuallyImportedFile>();
|
||||
|
||||
for (int i = 0; i < message.Files.Count; i++)
|
||||
{
|
||||
_logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);
|
||||
|
||||
var file = message.Files[i];
|
||||
var movie = _movieService.GetMovie(file.MovieId);
|
||||
var parsedMovieInfo = Parser.Parser.ParseMoviePath(file.Path, _config.ParsingLeniency > 0) ?? new ParsedMovieInfo();
|
||||
var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
|
||||
var existingFile = movie.Path.IsParentPath(file.Path);
|
||||
|
||||
var localMovie = new LocalMovie
|
||||
{
|
||||
ExistingFile = false,
|
||||
MediaInfo = mediaInfo,
|
||||
ParsedMovieInfo = parsedMovieInfo,
|
||||
Path = file.Path,
|
||||
Quality = file.Quality,
|
||||
Movie = movie,
|
||||
Size = 0
|
||||
};
|
||||
|
||||
//TODO: Cleanup non-tracked downloads
|
||||
|
||||
var importDecision = new ImportDecision(localMovie);
|
||||
|
||||
if (file.DownloadId.IsNullOrWhiteSpace())
|
||||
{
|
||||
imported.AddRange(_importApprovedMovie.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var trackedDownload = _trackedDownloadService.Find(file.DownloadId);
|
||||
var importResult = _importApprovedMovie.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();
|
||||
|
||||
imported.Add(importResult);
|
||||
|
||||
importedTrackedDownload.Add(new ManuallyImportedFile
|
||||
{
|
||||
TrackedDownload = trackedDownload,
|
||||
ImportResult = importResult
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_logger.ProgressTrace("Manually imported {0} files", imported.Count);
|
||||
|
||||
foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
|
||||
{
|
||||
var trackedDownload = groupedTrackedDownload.First().TrackedDownload;
|
||||
|
||||
if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
|
||||
{
|
||||
if (_downloadedMovieImportService.ShouldDeleteFolder(
|
||||
new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath),
|
||||
trackedDownload.RemoteMovie.Movie) && !trackedDownload.DownloadItem.IsReadOnly)
|
||||
{
|
||||
_diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, 1)) //TODO: trackedDownload.RemoteMovie.Movie.Count is always 1?
|
||||
{
|
||||
trackedDownload.State = TrackedDownloadStage.Imported;
|
||||
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
{
|
||||
public class ManuallyImportedFile
|
||||
{
|
||||
public TrackedDownload TrackedDownload { get; set; }
|
||||
public ImportResult ImportResult { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class FreeSpaceSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public FreeSpaceSpecification(IDiskProvider diskProvider, IConfigService configService, Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
if (_configService.SkipFreeSpaceCheckWhenImporting)
|
||||
{
|
||||
_logger.Debug("Skipping free space check when importing");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (localMovie.ExistingFile)
|
||||
{
|
||||
_logger.Debug("Skipping free space check for existing movie");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var path = Directory.GetParent(localMovie.Movie.Path);
|
||||
var freeSpace = _diskProvider.GetAvailableSpace(path.FullName);
|
||||
|
||||
if (!freeSpace.HasValue)
|
||||
{
|
||||
_logger.Debug("Free space check returned an invalid result for: {0}", path);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (freeSpace < localMovie.Size + 100.Megabytes())
|
||||
{
|
||||
_logger.Warn("Not enough free space ({0}) to import: {1} ({2})", freeSpace, localMovie, localMovie.Size);
|
||||
return Decision.Reject("Not enough free space");
|
||||
}
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
{
|
||||
_logger.Error("Unable to check free disk space while importing. " + ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to check free disk space while importing: " + localMovie.Path);
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class GrabbedReleaseQualitySpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly IHistoryService _historyService;
|
||||
|
||||
public GrabbedReleaseQualitySpecification(Logger logger, IHistoryService historyService)
|
||||
{
|
||||
_logger = logger;
|
||||
_historyService = historyService;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
if (downloadClientItem == null)
|
||||
{
|
||||
_logger.Debug("No download client item provided, skipping.");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var grabbedHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId)
|
||||
.Where(h => h.EventType == HistoryEventType.Grabbed)
|
||||
.ToList();
|
||||
|
||||
if (grabbedHistory.Empty())
|
||||
{
|
||||
_logger.Debug("No grabbed history for this download client item");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var parsedReleaseName = Parser.Parser.ParseMovieTitle(grabbedHistory.First().SourceTitle,false);
|
||||
|
||||
foreach (var item in grabbedHistory)
|
||||
{
|
||||
if (item.Quality.Quality != Quality.Unknown && item.Quality != localMovie.Quality)
|
||||
{
|
||||
_logger.Debug("Quality for grabbed release ({0}) does not match the quality of the file ({1})", item.Quality, localMovie.Quality);
|
||||
return Decision.Reject("File quality does not match quality of the grabbed release");
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class MatchesFolderSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MatchesFolderSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
if (localMovie.ExistingFile)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var dirInfo = new FileInfo(localMovie.Path).Directory;
|
||||
|
||||
if (dirInfo == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var folderInfo = Parser.Parser.ParseMovieTitle(dirInfo.Name, false);
|
||||
|
||||
if (folderInfo == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class NotSampleSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NotSampleSpecification(IDetectSample detectSample,
|
||||
Logger logger)
|
||||
{
|
||||
_detectSample = detectSample;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localEpisode, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
var sample = _detectSample.IsSample(localEpisode.Movie,
|
||||
localEpisode.Quality,
|
||||
localEpisode.Path,
|
||||
localEpisode.Size,
|
||||
false);
|
||||
|
||||
if (sample)
|
||||
{
|
||||
return Decision.Reject("Sample");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class NotUnpackingSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NotUnpackingSpecification(IDiskProvider diskProvider, IConfigService configService, Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
if (localMovie.ExistingFile)
|
||||
{
|
||||
_logger.Debug("{0} is in movie folder, skipping unpacking check", localMovie.Path);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
foreach (var workingFolder in _configService.DownloadClientWorkingFolders.Split('|'))
|
||||
{
|
||||
DirectoryInfo parent = Directory.GetParent(localMovie.Path);
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent.Name.StartsWith(workingFolder))
|
||||
{
|
||||
if (OsInfo.IsNotWindows)
|
||||
{
|
||||
_logger.Debug("{0} is still being unpacked", localMovie.Path);
|
||||
return Decision.Reject("File is still being unpacked");
|
||||
}
|
||||
|
||||
if (_diskProvider.FileGetLastWrite(localMovie.Path) > DateTime.UtcNow.AddMinutes(-5))
|
||||
{
|
||||
_logger.Debug("{0} appears to be unpacking still", localMovie.Path);
|
||||
return Decision.Reject("File is still being unpacked");
|
||||
}
|
||||
}
|
||||
|
||||
parent = parent.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class SameFileSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SameFileSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
var movieFile = localMovie.Movie.MovieFile;
|
||||
|
||||
if (localMovie.Movie.MovieFileId == 0)
|
||||
{
|
||||
_logger.Debug("No existing movie file, skipping");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (movieFile.Size == localMovie.Size)
|
||||
{
|
||||
_logger.Debug("'{0}' Has the same filesize as existing file", localMovie.Path);
|
||||
return Decision.Reject("Has the same filesize as existing file");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class UnverifiedSceneNumberingSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UnverifiedSceneNumberingSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Specifications
|
||||
{
|
||||
public class UpgradeSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UpgradeSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user