From 9206258370f0c3c2fd9b16e37f2c2748b2c65757 Mon Sep 17 00:00:00 2001 From: Tim Turner Date: Tue, 10 Jan 2017 21:12:47 -0500 Subject: [PATCH] Fixes Manual Import and DroneFactory Automatic Import still doesn't work - waiting for answer from Sonarr team. --- .../Commands/DownloadedMovieScanCommand.cs | 17 ++ .../DownloadedMovieCommandService.cs | 268 ++++-------------- .../DownloadedMovieImportService.cs | 266 +++++++++++++++++ .../Manual/ManualImportService.cs | 239 +++++++++++++--- src/NzbDrone.Core/NzbDrone.Core.csproj | 2 + src/UI/ManualImport/ManualImportLayout.js | 4 +- .../DroneFactory/DroneFactoryViewTemplate.hbs | 2 +- src/UI/Wanted/Missing/MissingLayout.js | 2 +- 8 files changed, 539 insertions(+), 261 deletions(-) create mode 100644 src/NzbDrone.Core/MediaFiles/Commands/DownloadedMovieScanCommand.cs create mode 100644 src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs diff --git a/src/NzbDrone.Core/MediaFiles/Commands/DownloadedMovieScanCommand.cs b/src/NzbDrone.Core/MediaFiles/Commands/DownloadedMovieScanCommand.cs new file mode 100644 index 000000000..69e1bb34d --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/Commands/DownloadedMovieScanCommand.cs @@ -0,0 +1,17 @@ +using NzbDrone.Core.MediaFiles.EpisodeImport; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.MediaFiles.Commands +{ + public class DownloadedMovieScanCommand : Command + { + public override bool SendUpdatesToClient => SendUpdates; + + public bool SendUpdates { get; set; } + + // Properties used by third-party apps, do not modify. + public string Path { get; set; } + public string DownloadClientId { get; set; } + public ImportMode ImportMode { get; set; } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs index 211f6f13c..5f9c5362f 100644 --- a/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs +++ b/src/NzbDrone.Core/MediaFiles/DownloadedMovieCommandService.cs @@ -1,265 +1,107 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using NLog; +using NLog; using NzbDrone.Common.Disk; -using NzbDrone.Core.DecisionEngine; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Download.TrackedDownloads; +using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.EpisodeImport; -using NzbDrone.Core.Parser; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Download; -using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Messaging.Commands; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; namespace NzbDrone.Core.MediaFiles { - public interface IDownloadedMovieImportService - { - List ProcessRootFolder(DirectoryInfo directoryInfo); - List ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null); - bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie); - } - - public class DownloadedMovieImportService : IDownloadedMovieImportService + public class DownloadedMovieCommandService : IExecute { + private readonly IDownloadedMovieImportService _downloadedMovieImportService; + private readonly ITrackedDownloadService _trackedDownloadService; private readonly IDiskProvider _diskProvider; - private readonly IDiskScanService _diskScanService; - private readonly IMovieService _movieService; - private readonly IParsingService _parsingService; - private readonly IMakeImportDecision _importDecisionMaker; - private readonly IImportApprovedMovie _importApprovedMovie; - private readonly IDetectSample _detectSample; + private readonly IConfigService _configService; private readonly Logger _logger; - public DownloadedMovieImportService(IDiskProvider diskProvider, - IDiskScanService diskScanService, - IMovieService movieService, - IParsingService parsingService, - IMakeImportDecision importDecisionMaker, - IImportApprovedMovie importApprovedMovie, - IDetectSample detectSample, - Logger logger) + public DownloadedMovieCommandService(IDownloadedMovieImportService downloadedMovieImportService, + ITrackedDownloadService trackedDownloadService, + IDiskProvider diskProvider, + IConfigService configService, + Logger logger) { + _downloadedMovieImportService = downloadedMovieImportService; + _trackedDownloadService = trackedDownloadService; _diskProvider = diskProvider; - _diskScanService = diskScanService; - _movieService = movieService; - _parsingService = parsingService; - _importDecisionMaker = importDecisionMaker; - _importApprovedMovie = importApprovedMovie; - _detectSample = detectSample; + _configService = configService; _logger = logger; } - public List ProcessRootFolder(DirectoryInfo directoryInfo) + private List ProcessDroneFactoryFolder() { - var results = new List(); + var downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder; - foreach (var subFolder in _diskProvider.GetDirectories(directoryInfo.FullName)) + if (string.IsNullOrEmpty(downloadedEpisodesFolder)) { - var folderResults = ProcessFolder(new DirectoryInfo(subFolder), ImportMode.Auto, null); - results.AddRange(folderResults); + _logger.Trace("Drone Factory folder is not configured"); + return new List(); } - foreach (var videoFile in _diskScanService.GetVideoFiles(directoryInfo.FullName, false)) + if (!_diskProvider.FolderExists(downloadedEpisodesFolder)) { - var fileResults = ProcessFile(new FileInfo(videoFile), ImportMode.Auto, null); - results.AddRange(fileResults); + _logger.Warn("Drone Factory folder [{0}] doesn't exist.", downloadedEpisodesFolder); + return new List(); } - return results; + return _downloadedMovieImportService.ProcessRootFolder(new DirectoryInfo(downloadedEpisodesFolder)); } - public List ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null) + private List ProcessPath(DownloadedMovieScanCommand message) { - if (_diskProvider.FolderExists(path)) + if (!_diskProvider.FolderExists(message.Path) && !_diskProvider.FileExists(message.Path)) { - var directoryInfo = new DirectoryInfo(path); - - if (movie == null) - { - return ProcessFolder(directoryInfo, importMode, downloadClientItem); - } - - return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem); + _logger.Warn("Folder/File specified for import scan [{0}] doesn't exist.", message.Path); + return new List(); } - if (_diskProvider.FileExists(path)) + if (message.DownloadClientId.IsNotNullOrWhiteSpace()) { - var fileInfo = new FileInfo(path); + var trackedDownload = _trackedDownloadService.Find(message.DownloadClientId); - if (movie == null) + if (trackedDownload != null) { - return ProcessFile(fileInfo, importMode, downloadClientItem); - } + _logger.Debug("External directory scan request for known download {0}. [{1}]", message.DownloadClientId, message.Path); - return ProcessFile(fileInfo, importMode, movie, downloadClientItem); - } - - _logger.Error("Import failed, path does not exist or is not accessible by Sonarr: {0}", path); - return new List(); - } - - public bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie) - { - var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName); - var rarFiles = _diskProvider.GetFiles(directoryInfo.FullName, SearchOption.AllDirectories).Where(f => Path.GetExtension(f) == ".rar"); - - foreach (var videoFile in videoFiles) - { - var episodeParseResult = Parser.Parser.ParseTitle(Path.GetFileName(videoFile)); - - if (episodeParseResult == null) - { - _logger.Warn("Unable to parse file on import: [{0}]", videoFile); - return false; + return _downloadedMovieImportService.ProcessPath(message.Path, message.ImportMode, trackedDownload.RemoteMovie.Movie, trackedDownload.DownloadItem); } - - var size = _diskProvider.GetFileSize(videoFile); - var quality = QualityParser.ParseQuality(videoFile); - - if (!_detectSample.IsSample(movie, quality, videoFile, size, episodeParseResult.IsPossibleSpecialEpisode)) + else { - _logger.Warn("Non-sample file detected: [{0}]", videoFile); - return false; - } - } - - if (rarFiles.Any(f => _diskProvider.GetFileSize(f) > 10.Megabytes())) - { - _logger.Warn("RAR file detected, will require manual cleanup"); - return false; - } - - return true; - } - - private List ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, DownloadClientItem downloadClientItem) - { - var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); - var movie = _parsingService.GetMovie(cleanedUpName); - - if (movie == null) - { - _logger.Debug("Unknown Movie {0}", cleanedUpName); - - return new List - { - UnknownMovieResult("Unknown Movie") - }; - } + _logger.Warn("External directory scan request for unknown download {0}, attempting normal import. [{1}]", message.DownloadClientId, message.Path); - return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem); - } - - private List ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem) - { - if (_movieService.MoviePathExists(directoryInfo.FullName)) - { - _logger.Warn("Unable to process folder that is mapped to an existing show"); - return new List(); - } - - var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); - var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name); - - if (folderInfo != null) - { - _logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality); - } - - var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName); - - if (downloadClientItem == null) - { - foreach (var videoFile in videoFiles) - { - if (_diskProvider.IsFileLocked(videoFile)) - { - return new List - { - FileIsLockedResult(videoFile) - }; - } + return _downloadedMovieImportService.ProcessPath(message.Path, message.ImportMode); } } - var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true); - var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode); - - if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && - importResults.Any(i => i.Result == ImportResultType.Imported) && - ShouldDeleteFolder(directoryInfo, movie)) - { - _logger.Debug("Deleting folder after importing valid files"); - _diskProvider.DeleteFolder(directoryInfo.FullName, true); - } - - return importResults; + return _downloadedMovieImportService.ProcessPath(message.Path, message.ImportMode); } - private List ProcessFile(FileInfo fileInfo, ImportMode importMode, DownloadClientItem downloadClientItem) + public void Execute(DownloadedMovieScanCommand message) { - var movie = _parsingService.GetMovie(Path.GetFileNameWithoutExtension(fileInfo.Name)); + List importResults; - if (movie == null) + if (message.Path.IsNotNullOrWhiteSpace()) { - _logger.Debug("Unknown Movie for file: {0}", fileInfo.Name); - - return new List - { - UnknownMovieResult(string.Format("Unknown Movie for file: {0}", fileInfo.Name), fileInfo.FullName) - }; + importResults = ProcessPath(message); } - - return ProcessFile(fileInfo, importMode, movie, downloadClientItem); - } - - private List ProcessFile(FileInfo fileInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem) - { - if (Path.GetFileNameWithoutExtension(fileInfo.Name).StartsWith("._")) + else { - _logger.Debug("[{0}] starts with '._', skipping", fileInfo.FullName); - - return new List - { - new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, new Rejection("Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'") - }; + importResults = ProcessDroneFactoryFolder(); } - if (downloadClientItem == null) + if (importResults == null || importResults.All(v => v.Result != ImportResultType.Imported)) { - if (_diskProvider.IsFileLocked(fileInfo.FullName)) - { - return new List - { - FileIsLockedResult(fileInfo.FullName) - }; - } + // Atm we don't report it as a command failure, coz that would cause the download to be failed. + // Changing the message won't do a thing either, coz it will get set to 'Completed' a msec later. + //message.SetMessage("Failed to import"); } - - var decisions = _importDecisionMaker.GetImportDecisions(new List() { fileInfo.FullName }, movie, null, true); - - return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode); - } - - private string GetCleanedUpFolderName(string folder) - { - folder = folder.Replace("_UNPACK_", "") - .Replace("_FAILED_", ""); - - return folder; - } - - private ImportResult FileIsLockedResult(string videoFile) - { - _logger.Debug("[{0}] is currently locked by another process, skipping", videoFile); - return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, new Rejection("Locked file, try again later")), "Locked file, try again later"); - } - - private ImportResult UnknownMovieResult(string message, string videoFile = null) - { - var localEpisode = videoFile == null ? null : new LocalEpisode { Path = videoFile }; - - return new ImportResult(new ImportDecision(localEpisode, new Rejection("Unknown Movie")), message); } } } diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs new file mode 100644 index 000000000..05617dffe --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs @@ -0,0 +1,266 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NLog; +using NzbDrone.Common.Disk; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.MediaFiles.EpisodeImport; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Download; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.MediaFiles.Commands; + +namespace NzbDrone.Core.MediaFiles +{ + public interface IDownloadedMovieImportService + { + List ProcessRootFolder(DirectoryInfo directoryInfo); + List ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null); + bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie); + } + + public class DownloadedMovieImportService : IDownloadedMovieImportService + { + private readonly IDiskProvider _diskProvider; + private readonly IDiskScanService _diskScanService; + private readonly IMovieService _movieService; + private readonly IParsingService _parsingService; + private readonly IMakeImportDecision _importDecisionMaker; + private readonly IImportApprovedMovie _importApprovedMovie; + private readonly IDetectSample _detectSample; + private readonly Logger _logger; + + public DownloadedMovieImportService(IDiskProvider diskProvider, + IDiskScanService diskScanService, + IMovieService movieService, + IParsingService parsingService, + IMakeImportDecision importDecisionMaker, + IImportApprovedMovie importApprovedMovie, + IDetectSample detectSample, + Logger logger) + { + _diskProvider = diskProvider; + _diskScanService = diskScanService; + _movieService = movieService; + _parsingService = parsingService; + _importDecisionMaker = importDecisionMaker; + _importApprovedMovie = importApprovedMovie; + _detectSample = detectSample; + _logger = logger; + } + + public List ProcessRootFolder(DirectoryInfo directoryInfo) + { + var results = new List(); + + foreach (var subFolder in _diskProvider.GetDirectories(directoryInfo.FullName)) + { + var folderResults = ProcessFolder(new DirectoryInfo(subFolder), ImportMode.Auto, null); + results.AddRange(folderResults); + } + + foreach (var videoFile in _diskScanService.GetVideoFiles(directoryInfo.FullName, false)) + { + var fileResults = ProcessFile(new FileInfo(videoFile), ImportMode.Auto, null); + results.AddRange(fileResults); + } + + return results; + } + + public List ProcessPath(string path, ImportMode importMode = ImportMode.Auto, Movie movie = null, DownloadClientItem downloadClientItem = null) + { + if (_diskProvider.FolderExists(path)) + { + var directoryInfo = new DirectoryInfo(path); + + if (movie == null) + { + return ProcessFolder(directoryInfo, importMode, downloadClientItem); + } + + return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem); + } + + if (_diskProvider.FileExists(path)) + { + var fileInfo = new FileInfo(path); + + if (movie == null) + { + return ProcessFile(fileInfo, importMode, downloadClientItem); + } + + return ProcessFile(fileInfo, importMode, movie, downloadClientItem); + } + + _logger.Error("Import failed, path does not exist or is not accessible by Radarr: {0}", path); + return new List(); + } + + public bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie) + { + var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName); + var rarFiles = _diskProvider.GetFiles(directoryInfo.FullName, SearchOption.AllDirectories).Where(f => Path.GetExtension(f) == ".rar"); + + foreach (var videoFile in videoFiles) + { + var episodeParseResult = Parser.Parser.ParseTitle(Path.GetFileName(videoFile)); + + if (episodeParseResult == null) + { + _logger.Warn("Unable to parse file on import: [{0}]", videoFile); + return false; + } + + var size = _diskProvider.GetFileSize(videoFile); + var quality = QualityParser.ParseQuality(videoFile); + + if (!_detectSample.IsSample(movie, quality, videoFile, size, episodeParseResult.IsPossibleSpecialEpisode)) + { + _logger.Warn("Non-sample file detected: [{0}]", videoFile); + return false; + } + } + + if (rarFiles.Any(f => _diskProvider.GetFileSize(f) > 10.Megabytes())) + { + _logger.Warn("RAR file detected, will require manual cleanup"); + return false; + } + + return true; + } + + private List ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, DownloadClientItem downloadClientItem) + { + var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); + var movie = _parsingService.GetMovie(cleanedUpName); + + if (movie == null) + { + _logger.Debug("Unknown Movie {0}", cleanedUpName); + + return new List + { + UnknownMovieResult("Unknown Movie") + }; + } + + return ProcessFolder(directoryInfo, importMode, movie, downloadClientItem); + } + + private List ProcessFolder(DirectoryInfo directoryInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem) + { + if (_movieService.MoviePathExists(directoryInfo.FullName)) + { + _logger.Warn("Unable to process folder that is mapped to an existing show"); + return new List(); + } + + var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name); + var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name); + + if (folderInfo != null) + { + _logger.Debug("{0} folder quality: {1}", cleanedUpName, folderInfo.Quality); + } + + var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName); + + if (downloadClientItem == null) + { + foreach (var videoFile in videoFiles) + { + if (_diskProvider.IsFileLocked(videoFile)) + { + return new List + { + FileIsLockedResult(videoFile) + }; + } + } + } + + var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, folderInfo, true); + var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode); + + if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && + importResults.Any(i => i.Result == ImportResultType.Imported) && + ShouldDeleteFolder(directoryInfo, movie)) + { + _logger.Debug("Deleting folder after importing valid files"); + _diskProvider.DeleteFolder(directoryInfo.FullName, true); + } + + return importResults; + } + + private List ProcessFile(FileInfo fileInfo, ImportMode importMode, DownloadClientItem downloadClientItem) + { + var movie = _parsingService.GetMovie(Path.GetFileNameWithoutExtension(fileInfo.Name)); + + if (movie == null) + { + _logger.Debug("Unknown Movie for file: {0}", fileInfo.Name); + + return new List + { + UnknownMovieResult(string.Format("Unknown Movie for file: {0}", fileInfo.Name), fileInfo.FullName) + }; + } + + return ProcessFile(fileInfo, importMode, movie, downloadClientItem); + } + + private List ProcessFile(FileInfo fileInfo, ImportMode importMode, Movie movie, DownloadClientItem downloadClientItem) + { + if (Path.GetFileNameWithoutExtension(fileInfo.Name).StartsWith("._")) + { + _logger.Debug("[{0}] starts with '._', skipping", fileInfo.FullName); + + return new List + { + new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, new Rejection("Invalid video file, filename starts with '._'")), "Invalid video file, filename starts with '._'") + }; + } + + if (downloadClientItem == null) + { + if (_diskProvider.IsFileLocked(fileInfo.FullName)) + { + return new List + { + FileIsLockedResult(fileInfo.FullName) + }; + } + } + + var decisions = _importDecisionMaker.GetImportDecisions(new List() { fileInfo.FullName }, movie, null, true); + + return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode); + } + + private string GetCleanedUpFolderName(string folder) + { + folder = folder.Replace("_UNPACK_", "") + .Replace("_FAILED_", ""); + + return folder; + } + + private ImportResult FileIsLockedResult(string videoFile) + { + _logger.Debug("[{0}] is currently locked by another process, skipping", videoFile); + return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, new Rejection("Locked file, try again later")), "Locked file, try again later"); + } + + private ImportResult UnknownMovieResult(string message, string videoFile = null) + { + var localEpisode = videoFile == null ? null : new LocalEpisode { Path = videoFile }; + + return new ImportResult(new ImportDecision(localEpisode, new Rejection("Unknown Movie")), message); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs index d85a2e119..e8f228327 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs @@ -30,11 +30,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual private readonly IDiskScanService _diskScanService; private readonly IMakeImportDecision _importDecisionMaker; private readonly ISeriesService _seriesService; + private readonly IMovieService _movieService; private readonly IEpisodeService _episodeService; private readonly IVideoFileInfoReader _videoFileInfoReader; private readonly IImportApprovedEpisodes _importApprovedEpisodes; + private readonly IImportApprovedMovie _importApprovedMovie; private readonly ITrackedDownloadService _trackedDownloadService; private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService; + private readonly IDownloadedMovieImportService _downloadedMovieImportService; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; @@ -43,11 +46,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual IDiskScanService diskScanService, IMakeImportDecision importDecisionMaker, ISeriesService seriesService, + IMovieService movieService, IEpisodeService episodeService, IVideoFileInfoReader videoFileInfoReader, IImportApprovedEpisodes importApprovedEpisodes, + IImportApprovedMovie importApprovedMovie, ITrackedDownloadService trackedDownloadService, IDownloadedEpisodesImportService downloadedEpisodesImportService, + IDownloadedMovieImportService downloadedMovieImportService, IEventAggregator eventAggregator, Logger logger) { @@ -56,11 +62,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual _diskScanService = diskScanService; _importDecisionMaker = importDecisionMaker; _seriesService = seriesService; + _movieService = movieService; _episodeService = episodeService; _videoFileInfoReader = videoFileInfoReader; _importApprovedEpisodes = importApprovedEpisodes; + _importApprovedMovie = importApprovedMovie; _trackedDownloadService = trackedDownloadService; _downloadedEpisodesImportService = downloadedEpisodesImportService; + _downloadedMovieImportService = downloadedMovieImportService; _eventAggregator = eventAggregator; _logger = logger; } @@ -126,62 +135,128 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual var relativeFile = folder.GetRelativePath(file); - var series = _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]); + var movie = _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]); - if (series == null) + if (movie == null) { - series = _parsingService.GetSeries(relativeFile); + movie = _parsingService.GetMovie(relativeFile); } - if (series == null && downloadId.IsNotNullOrWhiteSpace()) + if (movie == null && downloadId.IsNotNullOrWhiteSpace()) { var trackedDownload = _trackedDownloadService.Find(downloadId); - series = trackedDownload.RemoteEpisode.Series; + movie = trackedDownload.RemoteMovie.Movie; } - if (series == null) + if (movie == null) { - var localEpisode = new LocalEpisode(); - localEpisode.Path = file; - localEpisode.Quality = QualityParser.ParseQuality(file); - localEpisode.Size = _diskProvider.GetFileSize(file); + var localMovie = new LocalMovie() + { + Path = file, + Quality = QualityParser.ParseQuality(file), + Size = _diskProvider.GetFileSize(file) + }; - return MapItem(new ImportDecision(localEpisode, new Rejection("Unknown Series")), folder, downloadId); + return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), folder, downloadId); } - var importDecisions = _importDecisionMaker.GetImportDecisions(new List {file}, - series, null, SceneSource(series, folder)); + var importDecisions = _importDecisionMaker.GetImportDecisions(new List { file }, + movie, null, SceneSource(movie, folder)); return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null; } + //private ManualImportItem ProcessFile(string file, string downloadId, string folder = null) + //{ + // if (folder.IsNullOrWhiteSpace()) + // { + // folder = new FileInfo(file).Directory.FullName; + // } + + // var relativeFile = folder.GetRelativePath(file); + + // var series = _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]); + + // if (series == null) + // { + // series = _parsingService.GetSeries(relativeFile); + // } + + // if (series == null && downloadId.IsNotNullOrWhiteSpace()) + // { + // var trackedDownload = _trackedDownloadService.Find(downloadId); + // series = trackedDownload.RemoteEpisode.Series; + // } + + // if (series == null) + // { + // var localEpisode = new LocalEpisode(); + // localEpisode.Path = file; + // localEpisode.Quality = QualityParser.ParseQuality(file); + // localEpisode.Size = _diskProvider.GetFileSize(file); + + // return MapItem(new ImportDecision(localEpisode, new Rejection("Unknown Series")), folder, downloadId); + // } + + // var importDecisions = _importDecisionMaker.GetImportDecisions(new List {file}, + // series, null, SceneSource(series, folder)); + + // return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null; + //} + private bool SceneSource(Series series, string folder) { return !(series.Path.PathEquals(folder) || series.Path.IsParentPath(folder)); } + 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.LocalEpisode.Path; + // item.RelativePath = folder.GetRelativePath(decision.LocalEpisode.Path); + // item.Name = Path.GetFileNameWithoutExtension(decision.LocalEpisode.Path); + // item.DownloadId = downloadId; + + // if (decision.LocalEpisode.Series != null) + // { + // item.Series = decision.LocalEpisode.Series; + // } + + // if (decision.LocalEpisode.Episodes.Any()) + // { + // item.SeasonNumber = decision.LocalEpisode.SeasonNumber; + // item.Episodes = decision.LocalEpisode.Episodes; + // } + + // item.Quality = decision.LocalEpisode.Quality; + // item.Size = _diskProvider.GetFileSize(decision.LocalEpisode.Path); + // item.Rejections = decision.Rejections; + + // return item; + //} + private ManualImportItem MapItem(ImportDecision decision, string folder, string downloadId) { var item = new ManualImportItem(); - item.Path = decision.LocalEpisode.Path; - item.RelativePath = folder.GetRelativePath(decision.LocalEpisode.Path); - item.Name = Path.GetFileNameWithoutExtension(decision.LocalEpisode.Path); + item.Path = decision.LocalMovie.Path; + item.RelativePath = folder.GetRelativePath(decision.LocalMovie.Path); + item.Name = Path.GetFileNameWithoutExtension(decision.LocalMovie.Path); item.DownloadId = downloadId; - if (decision.LocalEpisode.Series != null) + if (decision.LocalMovie.Movie != null) { - item.Series = decision.LocalEpisode.Series; + item.Movie = decision.LocalMovie.Movie; } - if (decision.LocalEpisode.Episodes.Any()) - { - item.SeasonNumber = decision.LocalEpisode.SeasonNumber; - item.Episodes = decision.LocalEpisode.Episodes; - } - - item.Quality = decision.LocalEpisode.Quality; - item.Size = _diskProvider.GetFileSize(decision.LocalEpisode.Path); + item.Quality = decision.LocalMovie.Quality; + item.Size = _diskProvider.GetFileSize(decision.LocalMovie.Path); item.Rejections = decision.Rejections; return item; @@ -199,45 +274,43 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual _logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count); var file = message.Files[i]; - var series = _seriesService.GetSeries(file.SeriesId); - var episodes = _episodeService.GetEpisodes(file.EpisodeIds); - var parsedEpisodeInfo = Parser.Parser.ParsePath(file.Path) ?? new ParsedEpisodeInfo(); + var movie = _movieService.GetMovie(file.MovieId); + var parsedMovieInfo = Parser.Parser.ParseMoviePath(file.Path) ?? new ParsedMovieInfo(); var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path); - var existingFile = series.Path.IsParentPath(file.Path); + var existingFile = movie.Path.IsParentPath(file.Path); - var localEpisode = new LocalEpisode + var localMovie = new LocalMovie { ExistingFile = false, - Episodes = episodes, MediaInfo = mediaInfo, - ParsedEpisodeInfo = parsedEpisodeInfo, + ParsedMovieInfo = parsedMovieInfo, Path = file.Path, Quality = file.Quality, - Series = series, + Movie = movie, Size = 0 }; //TODO: Cleanup non-tracked downloads - var importDecision = new ImportDecision(localEpisode); + var importDecision = new ImportDecision(localMovie); if (file.DownloadId.IsNullOrWhiteSpace()) { - imported.AddRange(_importApprovedEpisodes.Import(new List { importDecision }, !existingFile, null, message.ImportMode)); + imported.AddRange(_importApprovedMovie.Import(new List { importDecision }, !existingFile, null, message.ImportMode)); } else { var trackedDownload = _trackedDownloadService.Find(file.DownloadId); - var importResult = _importApprovedEpisodes.Import(new List { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First(); + var importResult = _importApprovedMovie.Import(new List { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First(); imported.Add(importResult); importedTrackedDownload.Add(new ManuallyImportedFile - { - TrackedDownload = trackedDownload, - ImportResult = importResult - }); + { + TrackedDownload = trackedDownload, + ImportResult = importResult + }); } } @@ -249,20 +322,98 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath)) { - if (_downloadedEpisodesImportService.ShouldDeleteFolder( + if (_downloadedMovieImportService.ShouldDeleteFolder( new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath), - trackedDownload.RemoteEpisode.Series) && !trackedDownload.DownloadItem.IsReadOnly) + 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, trackedDownload.RemoteEpisode.Episodes.Count)) + 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)); } } } + + //public void Execute(ManualImportCommand message) + //{ + // _logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode); + + // var imported = new List(); + // var importedTrackedDownload = new List(); + + // 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 series = _seriesService.GetSeries(file.SeriesId); + // var episodes = _episodeService.GetEpisodes(file.EpisodeIds); + // var parsedEpisodeInfo = Parser.Parser.ParsePath(file.Path) ?? new ParsedEpisodeInfo(); + // var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path); + // var existingFile = series.Path.IsParentPath(file.Path); + + // var localEpisode = new LocalEpisode + // { + // ExistingFile = false, + // Episodes = episodes, + // MediaInfo = mediaInfo, + // ParsedEpisodeInfo = parsedEpisodeInfo, + // Path = file.Path, + // Quality = file.Quality, + // Series = series, + // Size = 0 + // }; + + // //TODO: Cleanup non-tracked downloads + + // var importDecision = new ImportDecision(localEpisode); + + // if (file.DownloadId.IsNullOrWhiteSpace()) + // { + // imported.AddRange(_importApprovedEpisodes.Import(new List { importDecision }, !existingFile, null, message.ImportMode)); + // } + + // else + // { + // var trackedDownload = _trackedDownloadService.Find(file.DownloadId); + // var importResult = _importApprovedEpisodes.Import(new List { 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 (_downloadedEpisodesImportService.ShouldDeleteFolder( + // new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath), + // trackedDownload.RemoteEpisode.Series) && !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, trackedDownload.RemoteEpisode.Episodes.Count)) + // { + // trackedDownload.State = TrackedDownloadStage.Imported; + // _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload)); + // } + // } + //} } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 97f67da50..b9a1b17dd 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -707,10 +707,12 @@ + + diff --git a/src/UI/ManualImport/ManualImportLayout.js b/src/UI/ManualImport/ManualImportLayout.js index 215415b5e..97c20e75d 100644 --- a/src/UI/ManualImport/ManualImportLayout.js +++ b/src/UI/ManualImport/ManualImportLayout.js @@ -168,8 +168,8 @@ module.exports = Marionette.Layout.extend({ }, _automaticImport : function (e) { - CommandController.Execute('downloadedEpisodesScan', { - name : 'downloadedEpisodesScan', + CommandController.Execute('downloadedMovieScan', { + name : 'downloadedMovieScan', path : e.folder }); diff --git a/src/UI/Settings/DownloadClient/DroneFactory/DroneFactoryViewTemplate.hbs b/src/UI/Settings/DownloadClient/DroneFactory/DroneFactoryViewTemplate.hbs index 9043ad2f5..a10f5d234 100644 --- a/src/UI/Settings/DownloadClient/DroneFactory/DroneFactoryViewTemplate.hbs +++ b/src/UI/Settings/DownloadClient/DroneFactory/DroneFactoryViewTemplate.hbs @@ -23,7 +23,7 @@
- +
\ No newline at end of file diff --git a/src/UI/Wanted/Missing/MissingLayout.js b/src/UI/Wanted/Missing/MissingLayout.js index 3adb4876b..e6a8ce33d 100644 --- a/src/UI/Wanted/Missing/MissingLayout.js +++ b/src/UI/Wanted/Missing/MissingLayout.js @@ -133,7 +133,7 @@ module.exports = Marionette.Layout.extend({ { title : 'Rescan Drone Factory Folder', icon : 'icon-sonarr-refresh', - command : 'downloadedepisodesscan', + command : 'downloadedMovieScan', properties : { sendUpdates : true } }, {