diff --git a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedMoviesFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedMoviesFixture.cs index 0427b750a..a8345fc33 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedMoviesFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedMoviesFixture.cs @@ -5,6 +5,7 @@ using FizzWare.NBuilder; using FluentAssertions; using Moq; using NUnit.Framework; +using NzbDrone.Common.Disk; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.History; @@ -37,6 +38,8 @@ namespace NzbDrone.Core.Test.MediaFiles _rejectedDecisions = new List(); _approvedDecisions = new List(); + var outputPath = @"C:\Test\Unsorted\TV\30.Rock.S01E01".AsOsAgnostic(); + var movie = Builder.CreateNew() .With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic()) @@ -63,7 +66,14 @@ namespace NzbDrone.Core.Test.MediaFiles .Setup(x => x.FindByDownloadId(It.IsAny())) .Returns(new List()); - _downloadClientItem = Builder.CreateNew().Build(); + _downloadClientItem = Builder.CreateNew() + .With(d => d.OutputPath = new OsPath(outputPath)) + .Build(); + } + + private void GivenNewDownload() + { + _approvedDecisions.ForEach(a => a.LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), Path.GetFileName(a.LocalMovie.Path))); } [Test] @@ -116,7 +126,7 @@ namespace NzbDrone.Core.Test.MediaFiles } [Test] - public void should_publish_EpisodeImportedEvent_for_new_downloads() + public void should_publish_MovieImportedEvent_for_new_downloads() { Subject.Import(new List { _approvedDecisions.First() }, true); @@ -137,6 +147,7 @@ namespace NzbDrone.Core.Test.MediaFiles [Test] public void should_use_nzb_title_as_scene_name() { + GivenNewDownload(); _downloadClientItem.Title = "malcolm.in.the.middle.2015.dvdrip.xvid-ingot"; Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); @@ -149,6 +160,7 @@ namespace NzbDrone.Core.Test.MediaFiles [TestCase(".nzb")] public void should_remove_extension_from_nzb_title_for_scene_name(string extension) { + GivenNewDownload(); var title = "malcolm.in.the.middle.2015.dvdrip.xvid-ingot"; _downloadClientItem.Title = title + extension; @@ -162,7 +174,8 @@ namespace NzbDrone.Core.Test.MediaFiles [Ignore("Series")] public void should_not_use_nzb_title_as_scene_name_if_full_season() { - _approvedDecisions.First().LocalMovie.Path = "c:\\tv\\season1\\malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv".AsOsAgnostic(); + GivenNewDownload(); + _approvedDecisions.First().LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv"); _downloadClientItem.Title = "malcolm.in.the.middle.s02.dvdrip.xvid-ingot"; Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); @@ -174,7 +187,8 @@ namespace NzbDrone.Core.Test.MediaFiles [Ignore("Series")] public void should_use_file_name_as_scenename_only_if_it_looks_like_scenename() { - _approvedDecisions.First().LocalMovie.Path = "c:\\tv\\malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv".AsOsAgnostic(); + GivenNewDownload(); + _approvedDecisions.First().LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "malcolm.in.the.middle.s02e23.dvdrip.xvid-ingot.mkv"); Subject.Import(new List { _approvedDecisions.First() }, true); @@ -184,7 +198,8 @@ namespace NzbDrone.Core.Test.MediaFiles [Test] public void should_not_use_file_name_as_scenename_if_it_doesnt_looks_like_scenename() { - _approvedDecisions.First().LocalMovie.Path = "c:\\tv\\aaaaa.mkv".AsOsAgnostic(); + GivenNewDownload(); + _approvedDecisions.First().LocalMovie.Path = Path.Combine(_downloadClientItem.OutputPath.ToString(), "aaaaa.mkv"); Subject.Import(new List { _approvedDecisions.First() }, true); @@ -220,7 +235,11 @@ namespace NzbDrone.Core.Test.MediaFiles [Test] public void should_copy_when_cannot_move_files_downloads() { - Subject.Import(new List { _approvedDecisions.First() }, true, new DownloadClientItem { Title = "30.Rock.S01E01", CanMoveFiles = false }); + GivenNewDownload(); + _downloadClientItem.Title = "30.Rock.S01E01"; + _downloadClientItem.CanMoveFiles = false; + + Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); Mocker.GetMock() .Verify(v => v.UpgradeMovieFile(It.IsAny(), _approvedDecisions.First().LocalMovie, true), Times.Once()); @@ -229,10 +248,71 @@ namespace NzbDrone.Core.Test.MediaFiles [Test] public void should_use_override_importmode() { - Subject.Import(new List { _approvedDecisions.First() }, true, new DownloadClientItem { Title = "30.Rock.S01E01", CanMoveFiles = false }, ImportMode.Move); + GivenNewDownload(); + _downloadClientItem.Title = "30.Rock.S01E01"; + _downloadClientItem.CanMoveFiles = false; + + Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem, ImportMode.Move); Mocker.GetMock() .Verify(v => v.UpgradeMovieFile(It.IsAny(), _approvedDecisions.First().LocalMovie, false), Times.Once()); } + + [Test] + public void should_use_file_name_only_for_download_client_item_without_a_job_folder() + { + var fileName = "Series.Title.S01E01.720p.HDTV.x264-Sonarr.mkv"; + var path = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), fileName); + + _downloadClientItem.OutputPath = new OsPath(path); + _approvedDecisions.First().LocalMovie.Path = path; + + Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); + + Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.OriginalFilePath == fileName))); + } + + [Test] + public void should_use_folder_and_file_name_only_for_download_client_item_with_a_job_folder() + { + var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr"; + var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name); + + _downloadClientItem.OutputPath = new OsPath(outputPath); + _approvedDecisions.First().LocalMovie.Path = Path.Combine(outputPath, name + ".mkv"); + + Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); + + Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.OriginalFilePath == $"{name}\\{name}.mkv".AsOsAgnostic()))); + } + + [Test] + public void should_include_intermediate_folders_for_download_client_item_with_a_job_folder() + { + var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr"; + var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name); + + _downloadClientItem.OutputPath = new OsPath(outputPath); + _approvedDecisions.First().LocalMovie.Path = Path.Combine(outputPath, "subfolder", name + ".mkv"); + + Subject.Import(new List { _approvedDecisions.First() }, true, _downloadClientItem); + + Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.OriginalFilePath == $"{name}\\subfolder\\{name}.mkv".AsOsAgnostic()))); + } + + [Test] + public void should_use_folder_info_release_title_to_find_relative_path() + { + var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr"; + var outputPath = Path.Combine(@"C:\Test\Unsorted\TV\".AsOsAgnostic(), name); + var localEpisode = _approvedDecisions.First().LocalMovie; + + localEpisode.FolderMovieInfo = new ParsedMovieInfo { SimpleReleaseTitle = name }; + localEpisode.Path = Path.Combine(outputPath, "subfolder", name + ".mkv"); + + Subject.Import(new List { _approvedDecisions.First() }, true, null); + + Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.OriginalFilePath == $"{name}\\subfolder\\{name}.mkv".AsOsAgnostic()))); + } } } diff --git a/src/NzbDrone.Core/MediaFiles/MovieFile.cs b/src/NzbDrone.Core/MediaFiles/MovieFile.cs index 6b7bc88b5..a443e51df 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieFile.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieFile.cs @@ -17,6 +17,7 @@ namespace NzbDrone.Core.MediaFiles public string Path { get; set; } public long Size { get; set; } public DateTime DateAdded { get; set; } + public string OriginalFilePath { get; set; } public string SceneName { get; set; } public string ReleaseGroup { get; set; } public IndexerFlags IndexerFlags { get; set; } diff --git a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs index ee949ded8..8fc65665a 100644 --- a/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs +++ b/src/NzbDrone.Core/MediaFiles/MovieImport/ImportApprovedMovie.cs @@ -118,6 +118,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport if (newDownload) { + movieFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localMovie); movieFile.SceneName = GetSceneName(downloadClientItem, localMovie); var moveResult = _movieFileUpgrader.UpgradeMovieFile(movieFile, localMovie, copyOnly); //TODO: Check if this works @@ -176,6 +177,37 @@ namespace NzbDrone.Core.MediaFiles.MovieImport return importResults; } + private string GetOriginalFilePath(DownloadClientItem downloadClientItem, LocalMovie localMovie) + { + if (downloadClientItem != null) + { + return downloadClientItem.OutputPath.Directory.ToString().GetRelativePath(localMovie.Path); + } + + var path = localMovie.Path; + var folderMovieInfo = localMovie.FolderMovieInfo; + + if (folderMovieInfo != null) + { + var folderPath = path.GetAncestorPath(folderMovieInfo.SimpleReleaseTitle); + + if (folderPath != null) + { + return folderPath.GetParentPath().GetRelativePath(path); + } + } + + var parentPath = path.GetParentPath(); + var grandparentPath = parentPath.GetParentPath(); + + if (grandparentPath != null) + { + return grandparentPath.GetRelativePath(path); + } + + return Path.Combine(Path.GetFileName(parentPath), Path.GetFileName(path)); + } + private string GetSceneName(DownloadClientItem downloadClientItem, LocalMovie localMovie) { if (downloadClientItem != null) diff --git a/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs b/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs index 0ca0f43b2..e65e19feb 100644 --- a/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs +++ b/src/Radarr.Api.V3/MovieFiles/MovieFileResource.cs @@ -23,6 +23,7 @@ namespace Radarr.Api.V3.MovieFiles public QualityModel Quality { get; set; } public List CustomFormats { get; set; } public MediaInfoResource MediaInfo { get; set; } + public string OriginalFilePath { get; set; } public bool QualityCutoffNotMet { get; set; } public List Languages { get; set; } } @@ -51,8 +52,7 @@ namespace Radarr.Api.V3.MovieFiles Quality = model.Quality, Languages = model.Languages, MediaInfo = model.MediaInfo.ToResource(model.SceneName), - - //QualityCutoffNotMet + OriginalFilePath = model.OriginalFilePath }; } @@ -76,7 +76,8 @@ namespace Radarr.Api.V3.MovieFiles IndexerFlags = model.IndexerFlags, Quality = model.Quality, Languages = model.Languages, - MediaInfo = model.MediaInfo.ToResource(model.SceneName) + MediaInfo = model.MediaInfo.ToResource(model.SceneName), + OriginalFilePath = model.OriginalFilePath }; } @@ -101,7 +102,8 @@ namespace Radarr.Api.V3.MovieFiles Quality = model.Quality, Languages = model.Languages, MediaInfo = model.MediaInfo.ToResource(model.SceneName), - QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(movie.Profile, model.Quality) + QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(movie.Profile, model.Quality), + OriginalFilePath = model.OriginalFilePath }; } }