From 6671934c0fff7a5dca194f4dba36e63004cbb4a0 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 23 Jul 2014 16:43:54 -0700 Subject: [PATCH] Relative episode file paths --- .../EpisodeFiles/EpisodeFileModule.cs | 31 ++- .../EpisodeFiles/EpisodeFileResource.cs | 2 +- src/NzbDrone.Api/Series/SeriesModule.cs | 1 - src/NzbDrone.Common/Disk/DiskProviderBase.cs | 5 +- .../Extensions/LoggerProgressExtensions.cs | 2 +- src/NzbDrone.Common/NzbDrone.Common.csproj | 1 + .../Datastore/DatabaseRelationshipFixture.cs | 6 +- .../DatabaseTargetFixture.cs | 4 +- .../MoveEpisodeFileFixture.cs | 5 +- .../ImportDecisionMakerFixture.cs | 4 +- .../ImportApprovedEpisodesFixture.cs | 4 +- .../MediaFiles/MediaFileRepositoryFixture.cs | 1 - .../FilterFixture.cs} | 80 +++--- .../MediaFileTableCleanupServiceFixture.cs | 27 +- .../UpdateMediaInfoServiceFixture.cs | 42 +-- .../UpgradeMediaFileServiceFixture.cs | 13 +- .../NzbDrone.Core.Test.csproj | 4 +- .../OrganizerTests/CleanFixture.cs | 26 ++ .../OrganizerTests/FileNameBuilderFixture.cs | 10 +- .../EpisodesWhereCutoffUnmetFixture.cs | 6 +- .../EpisodesWithFilesFixture.cs | 2 +- .../TvTests/MoveSeriesServiceFixture.cs | 100 ++++++++ src/NzbDrone.Core/Backup/BackupService.cs | 2 +- ...7_convert_episode_file_path_to_relative.cs | 48 ++++ .../Migration/058_drop_epsiode_file_path.cs | 14 + ...per (vaio's conflicted copy 2013-09-04).cs | 241 ------------------ src/NzbDrone.Core/Datastore/TableMapping.cs | 9 +- .../DecisionEngine/DownloadDecisionMaker.cs | 3 +- src/NzbDrone.Core/Download/DownloadService.cs | 2 +- src/NzbDrone.Core/History/HistoryService.cs | 3 +- .../IndexerSearch/EpisodeSearchService.cs | 2 +- .../IndexerSearch/NzbSearchService.cs | 2 +- .../IndexerSearch/SeasonSearchService.cs | 2 +- .../IndexerSearch/SeriesSearchService.cs | 2 +- src/NzbDrone.Core/Indexers/RssSyncService.cs | 2 +- .../MediaFiles/DiskScanService.cs | 2 +- src/NzbDrone.Core/MediaFiles/EpisodeFile.cs | 22 +- .../MediaFiles/EpisodeFileMovingService.cs | 29 ++- .../EpisodeImport/ImportApprovedEpisodes.cs | 5 + .../EpisodeImport/ImportDecisionMaker.cs | 2 +- .../MediaFiles/MediaFileService.cs | 8 +- .../MediaFileTableCleanupService.cs | 16 +- .../MediaInfo/UpdateMediaInfoService.cs | 12 +- .../MediaFiles/RenameEpisodeFileService.cs | 15 +- .../MediaFiles/UpdateEpisodeFileService.cs | 9 +- .../MediaFiles/UpgradeMediaFileService.cs | 8 +- .../Messaging/Commands/TestCommandExecutor.cs | 2 +- .../Consumers/Roksbox/RoksboxMetadata.cs | 13 +- .../MetaData/Consumers/Wdtv/WdtvMetadata.cs | 11 +- .../MetaData/Consumers/Xbmc/XbmcMetadata.cs | 11 +- src/NzbDrone.Core/MetaData/MetadataService.cs | 28 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 6 +- .../Organizer/FileNameBuilder.cs | 5 +- .../Organizer/FilenameSampleService.cs | 8 +- .../ProgressMessageTarget.cs | 1 - .../Tv/Commands/MoveSeriesCommand.cs | 13 + src/NzbDrone.Core/Tv/EpisodeService.cs | 2 +- .../Tv/Events/SeriesMovedEvent.cs | 19 ++ src/NzbDrone.Core/Tv/MoveSeriesService.cs | 72 ++++++ src/NzbDrone.Core/Tv/RefreshSeriesService.cs | 2 +- src/NzbDrone.Core/Tv/SeriesEditedService.cs | 2 - .../Update/InstallUpdateService.cs | 2 +- .../Update/UpdateCheckService.cs | 2 +- 63 files changed, 571 insertions(+), 464 deletions(-) rename src/{NzbDrone.Core => NzbDrone.Common}/Instrumentation/Extensions/LoggerProgressExtensions.cs (95%) rename src/NzbDrone.Core.Test/MediaFiles/{MediaFileServiceTest.cs => MediaFileServiceTests/FilterFixture.cs} (50%) create mode 100644 src/NzbDrone.Core.Test/OrganizerTests/CleanFixture.cs create mode 100644 src/NzbDrone.Core.Test/TvTests/MoveSeriesServiceFixture.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/057_convert_episode_file_path_to_relative.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/058_drop_epsiode_file_path.cs delete mode 100644 src/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper (vaio's conflicted copy 2013-09-04).cs create mode 100644 src/NzbDrone.Core/Tv/Commands/MoveSeriesCommand.cs create mode 100644 src/NzbDrone.Core/Tv/Events/SeriesMovedEvent.cs create mode 100644 src/NzbDrone.Core/Tv/MoveSeriesService.cs diff --git a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs index 99b913671..7a87ff338 100644 --- a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs +++ b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; +using System.IO; +using System.Linq; using NLog; +using NzbDrone.Api.Episodes; using NzbDrone.Api.REST; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.MediaFiles; @@ -7,6 +10,7 @@ using NzbDrone.Api.Mapping; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Tv; namespace NzbDrone.Api.EpisodeFiles { @@ -15,16 +19,19 @@ namespace NzbDrone.Api.EpisodeFiles { private readonly IMediaFileService _mediaFileService; private readonly IRecycleBinProvider _recycleBinProvider; + private readonly ISeriesService _seriesService; private readonly Logger _logger; public EpisodeModule(ICommandExecutor commandExecutor, IMediaFileService mediaFileService, IRecycleBinProvider recycleBinProvider, + ISeriesService seriesService, Logger logger) : base(commandExecutor) { _mediaFileService = mediaFileService; _recycleBinProvider = recycleBinProvider; + _seriesService = seriesService; _logger = logger; GetResourceById = GetEpisodeFile; GetResourceAll = GetEpisodeFiles; @@ -34,7 +41,10 @@ namespace NzbDrone.Api.EpisodeFiles private EpisodeFileResource GetEpisodeFile(int id) { - return _mediaFileService.Get(id).InjectTo(); + var episodeFile = _mediaFileService.Get(id); + var series = _seriesService.GetSeries(episodeFile.SeriesId); + + return MapToResource(series, episodeFile); } private List GetEpisodeFiles() @@ -46,7 +56,10 @@ namespace NzbDrone.Api.EpisodeFiles throw new BadRequestException("seriesId is missing"); } - return ToListResource(() => _mediaFileService.GetFilesBySeries(seriesId.Value)); + var series = _seriesService.GetSeries(seriesId.Value); + + return _mediaFileService.GetFilesBySeries(seriesId.Value) + .Select(f => MapToResource(series, f)).ToList(); } private void SetQuality(EpisodeFileResource episodeFileResource) @@ -59,12 +72,22 @@ namespace NzbDrone.Api.EpisodeFiles private void DeleteEpisodeFile(int id) { var episodeFile = _mediaFileService.Get(id); + var series = _seriesService.GetSeries(episodeFile.SeriesId); + var fullPath = Path.Combine(series.Path, episodeFile.RelativePath); - _logger.Info("Deleting episode file: {0}", episodeFile.Path); - _recycleBinProvider.DeleteFile(episodeFile.Path); + _logger.Info("Deleting episode file: {0}", fullPath); + _recycleBinProvider.DeleteFile(fullPath); _mediaFileService.Delete(episodeFile); } + private static EpisodeFileResource MapToResource(Core.Tv.Series series, EpisodeFile episodeFile) + { + var resource = episodeFile.InjectTo(); + resource.Path = Path.Combine(series.Path, episodeFile.RelativePath); + + return resource; + } + public void Handle(EpisodeFileAddedEvent message) { BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id); diff --git a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs index 311253f3f..696e367eb 100644 --- a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs +++ b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileResource.cs @@ -1,7 +1,6 @@ using System; using NzbDrone.Api.REST; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; namespace NzbDrone.Api.EpisodeFiles { @@ -9,6 +8,7 @@ namespace NzbDrone.Api.EpisodeFiles { public Int32 SeriesId { get; set; } public Int32 SeasonNumber { get; set; } + public String RelativePath { get; set; } public String Path { get; set; } public Int64 Size { get; set; } public DateTime DateAdded { get; set; } diff --git a/src/NzbDrone.Api/Series/SeriesModule.cs b/src/NzbDrone.Api/Series/SeriesModule.cs index d80bf60ff..78c4a2e80 100644 --- a/src/NzbDrone.Api/Series/SeriesModule.cs +++ b/src/NzbDrone.Api/Series/SeriesModule.cs @@ -37,7 +37,6 @@ namespace NzbDrone.Api.Series ISceneMappingService sceneMappingService, IMapCoversToLocal coverMapper, RootFolderValidator rootFolderValidator, - PathExistsValidator pathExistsValidator, SeriesPathValidator seriesPathValidator, SeriesExistsValidator seriesExistsValidator, DroneFactoryValidator droneFactoryValidator, diff --git a/src/NzbDrone.Common/Disk/DiskProviderBase.cs b/src/NzbDrone.Common/Disk/DiskProviderBase.cs index 91bf3ae30..74b0df219 100644 --- a/src/NzbDrone.Common/Disk/DiskProviderBase.cs +++ b/src/NzbDrone.Common/Disk/DiskProviderBase.cs @@ -7,6 +7,7 @@ using NLog; using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; +using NzbDrone.Common.Instrumentation.Extensions; namespace NzbDrone.Common.Disk { @@ -192,7 +193,7 @@ namespace NzbDrone.Common.Disk Ensure.That(source, () => source).IsValidPath(); Ensure.That(target, () => target).IsValidPath(); - Logger.Debug("{0} {1} -> {2}", transferAction, source, target); + Logger.ProgressDebug("{0} {1} -> {2}", transferAction, source, target); var sourceFolder = new DirectoryInfo(source); var targetFolder = new DirectoryInfo(target); @@ -211,7 +212,7 @@ namespace NzbDrone.Common.Disk { var destFile = Path.Combine(target, sourceFile.Name); - Logger.Debug("{0} {1} -> {2}", transferAction, sourceFile, destFile); + Logger.ProgressDebug("{0} {1} -> {2}", transferAction, sourceFile, destFile); switch (transferAction) { diff --git a/src/NzbDrone.Core/Instrumentation/Extensions/LoggerProgressExtensions.cs b/src/NzbDrone.Common/Instrumentation/Extensions/LoggerProgressExtensions.cs similarity index 95% rename from src/NzbDrone.Core/Instrumentation/Extensions/LoggerProgressExtensions.cs rename to src/NzbDrone.Common/Instrumentation/Extensions/LoggerProgressExtensions.cs index 6aeb5ecf7..edbab66c8 100644 --- a/src/NzbDrone.Core/Instrumentation/Extensions/LoggerProgressExtensions.cs +++ b/src/NzbDrone.Common/Instrumentation/Extensions/LoggerProgressExtensions.cs @@ -1,7 +1,7 @@ using System; using NLog; -namespace NzbDrone.Core.Instrumentation.Extensions +namespace NzbDrone.Common.Instrumentation.Extensions { public static class LoggerExtensions { diff --git a/src/NzbDrone.Common/NzbDrone.Common.csproj b/src/NzbDrone.Common/NzbDrone.Common.csproj index 346c4dc04..e0b019d32 100644 --- a/src/NzbDrone.Common/NzbDrone.Common.csproj +++ b/src/NzbDrone.Common/NzbDrone.Common.csproj @@ -118,6 +118,7 @@ + diff --git a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs index e852662e4..6510d12d2 100644 --- a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs @@ -86,14 +86,10 @@ namespace NzbDrone.Core.Test.Datastore Db.Insert(episode); - - - - var loadedEpisodeFile = Db.Single().EpisodeFile.Value; loadedEpisodeFile.Should().NotBeNull(); - loadedEpisodeFile.ShouldHave().AllProperties().But(c => c.DateAdded).EqualTo(episodeFile); + loadedEpisodeFile.ShouldHave().AllProperties().But(c => c.DateAdded).But(c => c.Path).EqualTo(episodeFile); } [Test] diff --git a/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs b/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs index 6d26f4836..f5d99e564 100644 --- a/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs +++ b/src/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs @@ -102,9 +102,9 @@ namespace NzbDrone.Core.Test.InstrumentationTests public void null_string_as_arg_should_not_fail() { var epFile = new EpisodeFile(); - _logger.Debug("File {0} no longer exists on disk. removing from database.", epFile.Path); + _logger.Debug("File {0} no longer exists on disk. removing from database.", epFile.RelativePath); - epFile.Path.Should().BeNull(); + epFile.RelativePath.Should().BeNull(); } diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeFileMovingServiceTests/MoveEpisodeFileFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeFileMovingServiceTests/MoveEpisodeFileFixture.cs index 75fe64ca6..7ca7d67d7 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeFileMovingServiceTests/MoveEpisodeFileFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeFileMovingServiceTests/MoveEpisodeFileFixture.cs @@ -30,7 +30,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests .Build(); _episodeFile = Builder.CreateNew() - .With(f => f.Path = @"C:\Test\File.avi") + .With(f => f.Path = null) + .With(f => f.RelativePath = @"Season 1\File.avi") .Build(); _localEpisode = Builder.CreateNew() @@ -44,7 +45,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests Mocker.GetMock() .Setup(s => s.BuildFilePath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(@"C:\Test\File Name.avi"); + .Returns(@"C:\Test\TV\Series\File Name.avi"); Mocker.GetMock() .Setup(s => s.FileExists(It.IsAny())) diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs index ccaf18d11..8d4637aa2 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs @@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport Mocker.GetMock() - .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny())) + .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny())) .Returns(_videoFiles); } @@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport Mocker.GetMock() - .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny())) + .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny())) .Returns(_videoFiles); Subject.GetImportDecisions(_videoFiles, _series, false); diff --git a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs index 251e02c31..2e134c74c 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Linq; using FizzWare.NBuilder; using FluentAssertions; @@ -31,6 +32,7 @@ namespace NzbDrone.Core.Test.MediaFiles var series = Builder.CreateNew() .With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) + .With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic()) .Build(); var episodes = Builder.CreateListOfSize(5) @@ -48,7 +50,7 @@ namespace NzbDrone.Core.Test.MediaFiles { Series = series, Episodes = new List {episode}, - Path = @"C:\Test\TV\30 Rock\30 Rock - S01E01 - Pilot.avi".AsOsAgnostic(), + Path = Path.Combine(series.Path, "30 Rock - S01E01 - Pilot.avi"), Quality = new QualityModel(Quality.Bluray720p), ParsedEpisodeInfo = new ParsedEpisodeInfo { diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs index e0db21265..ace441e7b 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; namespace NzbDrone.Core.Test.MediaFiles { diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTest.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTests/FilterFixture.cs similarity index 50% rename from src/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTest.cs rename to src/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTests/FilterFixture.cs index d1bd43441..a9007f46b 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTest.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTests/FilterFixture.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Linq; using FluentAssertions; using Moq; @@ -6,20 +7,24 @@ using NUnit.Framework; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Organizer; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFiles +namespace NzbDrone.Core.Test.MediaFiles.MediaFileServiceTests { [TestFixture] - public class MediaFileServiceTest : CoreTest + public class FilterFixture : CoreTest { + private Series _series; - [Test] - [TestCase("Law & Order: Criminal Intent - S10E07 - Icarus [HDTV-720p]", - "Law & Order- Criminal Intent - S10E07 - Icarus [HDTV-720p]")] - public void CleanFileName(string name, string expectedName) + [SetUp] + public void Setup() { - FileNameBuilder.CleanFileName(name).Should().Be(expectedName); + _series = new Series + { + Id = 10, + Path = @"C:\".AsOsAgnostic() + }; } [Test] @@ -27,9 +32,9 @@ namespace NzbDrone.Core.Test.MediaFiles { var files = new List() { - "c:\\file1.avi".AsOsAgnostic(), - "c:\\file2.avi".AsOsAgnostic(), - "c:\\file3.avi".AsOsAgnostic() + "C:\\file1.avi".AsOsAgnostic(), + "C:\\file2.avi".AsOsAgnostic(), + "C:\\file3.avi".AsOsAgnostic() }; Mocker.GetMock() @@ -37,26 +42,25 @@ namespace NzbDrone.Core.Test.MediaFiles .Returns(new List()); - Subject.FilterExistingFiles(files, 10).Should().BeEquivalentTo(files); + Subject.FilterExistingFiles(files, _series).Should().BeEquivalentTo(files); } - [Test] public void filter_should_return_none_if_all_files_exist() { var files = new List() { - "c:\\file1.avi".AsOsAgnostic(), - "c:\\file2.avi".AsOsAgnostic(), - "c:\\file3.avi".AsOsAgnostic() + "C:\\file1.avi".AsOsAgnostic(), + "C:\\file2.avi".AsOsAgnostic(), + "C:\\file3.avi".AsOsAgnostic() }; Mocker.GetMock() .Setup(c => c.GetFilesBySeries(It.IsAny())) - .Returns(files.Select(f => new EpisodeFile { Path = f }).ToList()); + .Returns(files.Select(f => new EpisodeFile { RelativePath = Path.GetFileName(f) }).ToList()); - Subject.FilterExistingFiles(files, 10).Should().BeEmpty(); + Subject.FilterExistingFiles(files, _series).Should().BeEmpty(); } [Test] @@ -64,21 +68,21 @@ namespace NzbDrone.Core.Test.MediaFiles { var files = new List() { - "c:\\file1.avi".AsOsAgnostic(), - "c:\\file2.avi".AsOsAgnostic(), - "c:\\file3.avi".AsOsAgnostic() + "C:\\file1.avi".AsOsAgnostic(), + "C:\\file2.avi".AsOsAgnostic(), + "C:\\file3.avi".AsOsAgnostic() }; Mocker.GetMock() .Setup(c => c.GetFilesBySeries(It.IsAny())) .Returns(new List { - new EpisodeFile{Path = "c:\\file2.avi".AsOsAgnostic()} + new EpisodeFile{ RelativePath = "file2.avi".AsOsAgnostic()} }); - Subject.FilterExistingFiles(files, 10).Should().HaveCount(2); - Subject.FilterExistingFiles(files, 10).Should().NotContain("c:\\file2.avi".AsOsAgnostic()); + Subject.FilterExistingFiles(files, _series).Should().HaveCount(2); + Subject.FilterExistingFiles(files, _series).Should().NotContain("C:\\file2.avi".AsOsAgnostic()); } [Test] @@ -88,21 +92,21 @@ namespace NzbDrone.Core.Test.MediaFiles var files = new List() { - "c:\\file1.avi".AsOsAgnostic(), - "c:\\FILE2.avi".AsOsAgnostic(), - "c:\\file3.avi".AsOsAgnostic() + "C:\\file1.avi".AsOsAgnostic(), + "C:\\FILE2.avi".AsOsAgnostic(), + "C:\\file3.avi".AsOsAgnostic() }; Mocker.GetMock() .Setup(c => c.GetFilesBySeries(It.IsAny())) .Returns(new List { - new EpisodeFile{Path = "c:\\file2.avi".AsOsAgnostic()} + new EpisodeFile{ RelativePath = "file2.avi".AsOsAgnostic()} }); - Subject.FilterExistingFiles(files, 10).Should().HaveCount(2); - Subject.FilterExistingFiles(files, 10).Should().NotContain("c:\\file2.avi".AsOsAgnostic()); + Subject.FilterExistingFiles(files, _series).Should().HaveCount(2); + Subject.FilterExistingFiles(files, _series).Should().NotContain("C:\\file2.avi".AsOsAgnostic()); } [Test] @@ -112,19 +116,19 @@ namespace NzbDrone.Core.Test.MediaFiles var files = new List() { - "c:\\file1.avi".AsOsAgnostic(), - "c:\\FILE2.avi".AsOsAgnostic(), - "c:\\file3.avi".AsOsAgnostic() + "C:\\file1.avi".AsOsAgnostic(), + "C:\\FILE2.avi".AsOsAgnostic(), + "C:\\file3.avi".AsOsAgnostic() }; Mocker.GetMock() .Setup(c => c.GetFilesBySeries(It.IsAny())) .Returns(new List { - new EpisodeFile{Path = "c:\\file2.avi".AsOsAgnostic()} + new EpisodeFile{ RelativePath = "file2.avi".AsOsAgnostic()} }); - Subject.FilterExistingFiles(files, 10).Should().HaveCount(3); + Subject.FilterExistingFiles(files, _series).Should().HaveCount(3); } [Test] @@ -132,16 +136,16 @@ namespace NzbDrone.Core.Test.MediaFiles { var files = new List() { - "c:\\FILE1.avi".AsOsAgnostic() + "C:\\FILE1.avi".AsOsAgnostic() }; Mocker.GetMock() .Setup(c => c.GetFilesBySeries(It.IsAny())) .Returns(new List()); - Subject.FilterExistingFiles(files, 10).Should().HaveCount(1); - Subject.FilterExistingFiles(files, 10).Should().NotContain(files.First().ToLower()); - Subject.FilterExistingFiles(files, 10).Should().Contain(files.First()); + Subject.FilterExistingFiles(files, _series).Should().HaveCount(1); + Subject.FilterExistingFiles(files, _series).Should().NotContain(files.First().ToLower()); + Subject.FilterExistingFiles(files, _series).Should().Contain(files.First()); } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaFileTableCleanupServiceFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaFileTableCleanupServiceFixture.cs index 3d04204be..8ca6fa341 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaFileTableCleanupServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaFileTableCleanupServiceFixture.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.MediaFiles .Returns(Builder.CreateNew().Build()); Mocker.GetMock() - .Setup(e => e.FileExists(It.Is(c => c != DELETED_PATH))) + .Setup(e => e.FileExists(It.Is(c => !c.Contains(DELETED_PATH)))) .Returns(true); Mocker.GetMock() @@ -68,18 +68,18 @@ namespace NzbDrone.Core.Test.MediaFiles } [Test] - public void should_delete_none_existing_files() + public void should_delete_non_existent_files() { var episodeFiles = Builder.CreateListOfSize(10) .Random(2) - .With(c => c.Path = DELETED_PATH) + .With(c => c.RelativePath = DELETED_PATH) .Build(); GivenEpisodeFiles(episodeFiles); Subject.Execute(new CleanMediaFileDb(0)); - Mocker.GetMock().Verify(c => c.Delete(It.Is(e => e.Path == DELETED_PATH), false), Times.Exactly(2)); + Mocker.GetMock().Verify(c => c.Delete(It.Is(e => e.RelativePath == DELETED_PATH), false), Times.Exactly(2)); } [Test] @@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles { var episodeFiles = Builder.CreateListOfSize(10) .Random(10) - .With(c => c.Path = "ExistingPath") + .With(c => c.RelativePath = "ExistingPath") .Build(); GivenEpisodeFiles(episodeFiles); @@ -98,21 +98,6 @@ namespace NzbDrone.Core.Test.MediaFiles Mocker.GetMock().Verify(c => c.Delete(It.IsAny(), false), Times.Exactly(10)); } - [Test] - public void should_delete_files_that_do_not_belong_to_the_series_path() - { - var episodeFiles = Builder.CreateListOfSize(10) - .Random(10) - .With(c => c.Path = "ExistingPath") - .Build(); - - GivenEpisodeFiles(episodeFiles); - - Subject.Execute(new CleanMediaFileDb(0)); - - Mocker.GetMock().Verify(c => c.Delete(It.IsAny(), false), Times.Exactly(10)); - } - [Test] public void should_unlink_episode_when_episodeFile_does_not_exist() { @@ -128,7 +113,7 @@ namespace NzbDrone.Core.Test.MediaFiles { var episodeFiles = Builder.CreateListOfSize(10) .Random(10) - .With(c => c.Path = "ExistingPath") + .With(c => c.RelativePath = "ExistingPath") .Build(); GivenEpisodeFiles(episodeFiles); diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs index 4b29c0b46..e96a91fb3 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs @@ -1,23 +1,33 @@ -using FizzWare.NBuilder; +using System.IO; +using FizzWare.NBuilder; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; -using NzbDrone.Core.Lifecycle; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; using NzbDrone.Test.Common; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace NzbDrone.Core.Test.MediaFiles.MediaInfo { [TestFixture] public class UpdateMediaInfoServiceFixture : CoreTest { + private Series _series; + + [SetUp] + public void Setup() + { + _series = new Series + { + Id = 1, + Path = @"C:\series".AsOsAgnostic() + }; + } + private void GivenFileExists() { Mocker.GetMock() @@ -44,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo { var episodeFiles = Builder.CreateListOfSize(3) .All() - .With(v => v.Path = @"C:\series\media.mkv".AsOsAgnostic()) + .With(v => v.RelativePath = "media.mkv") .TheFirst(1) .With(v => v.MediaInfo = new MediaInfoModel()) .BuildList(); @@ -56,10 +66,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo GivenFileExists(); GivenSuccessfulScan(); - Subject.Handle(new SeriesScannedEvent(new Tv.Series { Id = 1 })); + Subject.Handle(new SeriesScannedEvent(_series)); Mocker.GetMock() - .Verify(v => v.GetMediaInfo(@"C:\series\media.mkv".AsOsAgnostic()), Times.Exactly(2)); + .Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2)); Mocker.GetMock() .Verify(v => v.Update(It.IsAny()), Times.Exactly(2)); @@ -70,7 +80,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo { var episodeFiles = Builder.CreateListOfSize(2) .All() - .With(v => v.Path = @"C:\series\media.mkv".AsOsAgnostic()) + .With(v => v.RelativePath = "media.mkv") .BuildList(); Mocker.GetMock() @@ -79,10 +89,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo GivenSuccessfulScan(); - Subject.Handle(new SeriesScannedEvent(new Tv.Series { Id = 1 })); + Subject.Handle(new SeriesScannedEvent(_series)); Mocker.GetMock() - .Verify(v => v.GetMediaInfo(@"C:\series\media.mkv".AsOsAgnostic()), Times.Never()); + .Verify(v => v.GetMediaInfo("media.mkv"), Times.Never()); Mocker.GetMock() .Verify(v => v.Update(It.IsAny()), Times.Never()); @@ -93,9 +103,9 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo { var episodeFiles = Builder.CreateListOfSize(2) .All() - .With(v => v.Path = @"C:\series\media.mkv".AsOsAgnostic()) + .With(v => v.RelativePath = "media.mkv") .TheFirst(1) - .With(v => v.Path = @"C:\series\media2.mkv".AsOsAgnostic()) + .With(v => v.RelativePath = "media2.mkv") .BuildList(); Mocker.GetMock() @@ -104,12 +114,12 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo GivenFileExists(); GivenSuccessfulScan(); - GivenFailedScan(@"C:\series\media2.mkv".AsOsAgnostic()); + GivenFailedScan(Path.Combine(_series.Path, "media2.mkv")); - Subject.Handle(new SeriesScannedEvent(new Tv.Series { Id = 1 })); + Subject.Handle(new SeriesScannedEvent(_series)); Mocker.GetMock() - .Verify(v => v.GetMediaInfo(@"C:\series\media.mkv".AsOsAgnostic()), Times.Exactly(1)); + .Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(1)); Mocker.GetMock() .Verify(v => v.Update(It.IsAny()), Times.Exactly(1)); diff --git a/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs index f13e960cf..b3214704c 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs @@ -10,6 +10,7 @@ using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.MediaFiles { @@ -22,6 +23,10 @@ namespace NzbDrone.Core.Test.MediaFiles public void Setup() { _localEpisode = new LocalEpisode(); + _localEpisode.Series = new Series + { + Path = @"C:\Test\TV\Series".AsOsAgnostic() + }; _episodeFile = Builder .CreateNew() @@ -42,7 +47,7 @@ namespace NzbDrone.Core.Test.MediaFiles new EpisodeFile { Id = 1, - Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + RelativePath = @"Season 01\30.rock.s01e01.avi", })) .Build() .ToList(); @@ -57,7 +62,7 @@ namespace NzbDrone.Core.Test.MediaFiles new EpisodeFile { Id = 1, - Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + RelativePath = @"Season 01\30.rock.s01e01.avi", })) .Build() .ToList(); @@ -71,14 +76,14 @@ namespace NzbDrone.Core.Test.MediaFiles new EpisodeFile { Id = 1, - Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + RelativePath = @"Season 01\30.rock.s01e01.avi", })) .TheNext(1) .With(e => e.EpisodeFile = new LazyLoaded( new EpisodeFile { Id = 2, - Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e02.avi", + RelativePath = @"Season 01\30.rock.s01e02.avi", })) .Build() .ToList(); diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index bf8c6b567..f55bd818b 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -188,7 +188,8 @@ - + + @@ -262,6 +263,7 @@ + diff --git a/src/NzbDrone.Core.Test/OrganizerTests/CleanFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/CleanFixture.cs new file mode 100644 index 000000000..6fc7dd90d --- /dev/null +++ b/src/NzbDrone.Core.Test/OrganizerTests/CleanFixture.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.OrganizerTests +{ + [TestFixture] + public class CleanFixture : CoreTest + { + [TestCase("Law & Order: Criminal Intent - S10E07 - Icarus [HDTV-720p]", + "Law & Order- Criminal Intent - S10E07 - Icarus [HDTV-720p]")] + public void CleanFileName(string name, string expectedName) + { + FileNameBuilder.CleanFileName(name).Should().Be(expectedName); + } + + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs index 278407505..d532c3541 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs @@ -64,6 +64,8 @@ namespace NzbDrone.Core.Test.OrganizerTests _episodeFile.Quality.Proper = true; } + + [Test] public void should_replace_Series_space_Title() { @@ -227,10 +229,10 @@ namespace NzbDrone.Core.Test.OrganizerTests public void use_file_name_when_sceneName_is_null() { _namingConfig.RenameEpisodes = false; - _episodeFile.Path = @"C:\Test\TV\30 Rock - S01E01 - Test"; + _episodeFile.RelativePath = "30 Rock - S01E01 - Test"; Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile) - .Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.Path)); + .Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.RelativePath)); } [Test] @@ -238,7 +240,7 @@ namespace NzbDrone.Core.Test.OrganizerTests { _namingConfig.RenameEpisodes = false; _episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL"; - _episodeFile.Path = @"C:\Test\TV\30 Rock - S01E01 - Test"; + _episodeFile.RelativePath = "30 Rock - S01E01 - Test"; Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile) .Should().Be("30.Rock.S01E01.xvid-LOL"); @@ -378,7 +380,7 @@ namespace NzbDrone.Core.Test.OrganizerTests _namingConfig.StandardEpisodeFormat = "{Series Title} - {Original Title}"; _episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL"; - _episodeFile.Path = @"C:\Test\TV\30 Rock - S01E01 - Test"; + _episodeFile.RelativePath = "30 Rock - S01E01 - Test"; Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile) .Should().Be("30 Rock - 30.Rock.S01E01.xvid-LOL"); diff --git a/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWhereCutoffUnmetFixture.cs b/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWhereCutoffUnmetFixture.cs index edd9c8369..a812c61db 100644 --- a/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWhereCutoffUnmetFixture.cs +++ b/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWhereCutoffUnmetFixture.cs @@ -67,9 +67,9 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests new QualitiesBelowCutoff(profile.Id, new[] {Quality.SDTV.Id}) }; - var qualityMet = new EpisodeFile { Path = "a", Quality = new QualityModel { Quality = Quality.WEBDL480p } }; - var qualityUnmet = new EpisodeFile { Path = "b", Quality = new QualityModel { Quality = Quality.SDTV } }; - var qualityRawHD = new EpisodeFile { Path = "c", Quality = new QualityModel { Quality = Quality.RAWHD } }; + var qualityMet = new EpisodeFile { RelativePath = "a", Quality = new QualityModel { Quality = Quality.WEBDL480p } }; + var qualityUnmet = new EpisodeFile { RelativePath = "b", Quality = new QualityModel { Quality = Quality.SDTV } }; + var qualityRawHD = new EpisodeFile { RelativePath = "c", Quality = new QualityModel { Quality = Quality.RAWHD } }; MediaFileRepository fileRepository = Mocker.Resolve(); diff --git a/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithFilesFixture.cs b/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithFilesFixture.cs index 3925850ee..b6d0f5e0b 100644 --- a/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithFilesFixture.cs +++ b/src/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithFilesFixture.cs @@ -57,7 +57,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests public void should_only_contain_episodes_for_the_given_series() { var episodeFile = Builder.CreateNew() - .With(f => f.Path = "another path") + .With(f => f.RelativePath = "another path") .With(c => c.Quality = new QualityModel()) .BuildNew(); diff --git a/src/NzbDrone.Core.Test/TvTests/MoveSeriesServiceFixture.cs b/src/NzbDrone.Core.Test/TvTests/MoveSeriesServiceFixture.cs new file mode 100644 index 000000000..0470c0b16 --- /dev/null +++ b/src/NzbDrone.Core.Test/TvTests/MoveSeriesServiceFixture.cs @@ -0,0 +1,100 @@ +using System; +using System.IO; +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Common.Disk; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Tv.Commands; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.TvTests +{ + [TestFixture] + public class MoveSeriesServiceFixture : CoreTest + { + private Series _series; + private MoveSeriesCommand _command; + + [SetUp] + public void Setup() + { + _series = Builder + .CreateNew() + .Build(); + + _command = new MoveSeriesCommand + { + SeriesId = 1, + SourcePath = @"C:\Test\TV\Series".AsOsAgnostic(), + DestinationPath = @"C:\Test\TV2\Series".AsOsAgnostic() + }; + + Mocker.GetMock() + .Setup(s => s.GetSeries(It.IsAny())) + .Returns(_series); + } + + private void GivenFailedMove() + { + Mocker.GetMock() + .Setup(s => s.MoveFolder(It.IsAny(), It.IsAny())) + .Throws(); + } + + [Test] + public void should_log_error_when_move_throws_an_exception() + { + GivenFailedMove(); + + Assert.Throws(() => Subject.Execute(_command)); + + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void should_no_update_series_path_on_error() + { + GivenFailedMove(); + + Assert.Throws(() => Subject.Execute(_command)); + + ExceptionVerification.ExpectedErrors(1); + + Mocker.GetMock() + .Verify(v => v.UpdateSeries(It.IsAny()), Times.Never()); + } + + [Test] + public void should_build_new_path_when_root_folder_is_provided() + { + _command.DestinationPath = null; + _command.DestinationRootFolder = @"C:\Test\TV3".AsOsAgnostic(); + + var expectedPath = @"C:\Test\TV3\Series".AsOsAgnostic(); + + Mocker.GetMock() + .Setup(s => s.GetSeriesFolder(It.IsAny(), null)) + .Returns("Series"); + + Subject.Execute(_command); + + Mocker.GetMock() + .Verify(v => v.UpdateSeries(It.Is(s => s.Path == expectedPath)), Times.Once()); + } + + [Test] + public void should_use_destination_path_if_destination_root_folder_is_blank() + { + Subject.Execute(_command); + + Mocker.GetMock() + .Verify(v => v.UpdateSeries(It.Is(s => s.Path == _command.DestinationPath)), Times.Once()); + + Mocker.GetMock() + .Verify(v => v.GetSeriesFolder(It.IsAny(), null), Times.Never()); + } + } +} diff --git a/src/NzbDrone.Core/Backup/BackupService.cs b/src/NzbDrone.Core/Backup/BackupService.cs index 33209cc9b..5063e5ffb 100644 --- a/src/NzbDrone.Core/Backup/BackupService.cs +++ b/src/NzbDrone.Core/Backup/BackupService.cs @@ -8,8 +8,8 @@ using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Backup diff --git a/src/NzbDrone.Core/Datastore/Migration/057_convert_episode_file_path_to_relative.cs b/src/NzbDrone.Core/Datastore/Migration/057_convert_episode_file_path_to_relative.cs new file mode 100644 index 000000000..a1bf307fd --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/057_convert_episode_file_path_to_relative.cs @@ -0,0 +1,48 @@ +using System.Data; +using System.IO; +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(57)] + public class convert_episode_file_path_to_relative : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Create.Column("RelativePath").OnTable("EpisodeFiles").AsString().Nullable(); + + //TODO: Add unique contraint for series ID and Relative Path + //TODO: Warn if multiple series share the same path + + Execute.WithConnection(UpdateRelativePaths); + } + + private void UpdateRelativePaths(IDbConnection conn, IDbTransaction tran) + { + using (IDbCommand getSeriesCmd = conn.CreateCommand()) + { + getSeriesCmd.Transaction = tran; + getSeriesCmd.CommandText = @"SELECT Id, Path FROM Series"; + using (IDataReader seriesReader = getSeriesCmd.ExecuteReader()) + { + while (seriesReader.Read()) + { + var seriesId = seriesReader.GetInt32(0); + var seriesPath = seriesReader.GetString(1) + Path.DirectorySeparatorChar; + + using (IDbCommand updateCmd = conn.CreateCommand()) + { + updateCmd.Transaction = tran; + updateCmd.CommandText = "UPDATE EpisodeFiles SET RelativePath = REPLACE(Path, ?, '') WHERE SeriesId = ?"; + updateCmd.AddParameter(seriesPath); + updateCmd.AddParameter(seriesId); + + updateCmd.ExecuteNonQuery(); + } + } + } + } + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/058_drop_epsiode_file_path.cs b/src/NzbDrone.Core/Datastore/Migration/058_drop_epsiode_file_path.cs new file mode 100644 index 000000000..61cd22135 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/058_drop_epsiode_file_path.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(58)] + public class drop_episode_file_path : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + SqLiteAlter.DropColumns("EpisodeFiles", new [] { "Path" }); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper (vaio's conflicted copy 2013-09-04).cs b/src/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper (vaio's conflicted copy 2013-09-04).cs deleted file mode 100644 index 0cbf55e4c..000000000 --- a/src/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper (vaio's conflicted copy 2013-09-04).cs +++ /dev/null @@ -1,241 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.SQLite; -using System.Linq; -using System.Text.RegularExpressions; -using NLog; - -namespace NzbDrone.Core.Datastore.Migration.Framework -{ - public interface ISQLiteMigrationHelper - { - Dictionary GetColumns(string tableName); - void CreateTable(string tableName, IEnumerable values, IEnumerable indexes); - void CopyData(string sourceTable, string destinationTable, IEnumerable columns); - void DropTable(string tableName); - void RenameTable(string tableName, string newName); - List GetDuplicates(string tableName, string columnName); - SQLiteTransaction BeginTransaction(); - List GetIndexes(string tableName); - } - - public class SQLiteMigrationHelper : ISQLiteMigrationHelper - { - private readonly SQLiteConnection _connection; - - private static readonly Regex SchemaRegex = new Regex(@"['\""\[](?\w+)['\""\]]\s(?[\w-\s]+)", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); - - private static readonly Regex IndexRegex = new Regex(@"\(""(?.*)""\s(?ASC|DESC)\)$", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); - - public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory, Logger logger) - { - try - { - _connection = new SQLiteConnection(connectionStringFactory.MainDbConnectionString); - _connection.Open(); - } - catch (Exception e) - { - logger.ErrorException("Couldn't open database " + connectionStringFactory.MainDbConnectionString, e); - throw; - } - - } - - private string GetOriginalSql(string tableName) - { - var command = - new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='table' AND name ='{0}'", - tableName)); - - command.Connection = _connection; - - return (string)command.ExecuteScalar(); - } - - public Dictionary GetColumns(string tableName) - { - var originalSql = GetOriginalSql(tableName); - - var matches = SchemaRegex.Matches(originalSql); - - return matches.Cast().ToDictionary( - match => match.Groups["name"].Value.Trim(), - match => new SQLiteColumn - { - Name = match.Groups["name"].Value.Trim(), - Schema = match.Groups["schema"].Value.Trim() - }); - } - - - private static IEnumerable ReadArray(SQLiteDataReader reader) - { - while (reader.Read()) - { - yield return (T)Convert.ChangeType(reader[0], typeof(T)); - } - } - - public List GetIndexes(string tableName) - { - var command = new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name ='{0}'", tableName)); - command.Connection = _connection; - - var reader = command.ExecuteReader(); - var sqls = ReadArray(reader).ToList(); - - - var indexes = new List(); - - foreach (var indexSql in sqls) - { - var newIndex = new SQLiteIndex(); - var matches = IndexRegex.Match(indexSql); - - newIndex.Column = matches.Groups["col"].Value; - newIndex.Unique = indexSql.Contains("UNIQUE"); - newIndex.Table = tableName; - - indexes.Add(newIndex); - } - - return indexes; - } - - public void CreateTable(string tableName, IEnumerable values, IEnumerable indexes) - { - var columns = String.Join(",", values.Select(c => c.ToString())); - - ExecuteNonQuery("CREATE TABLE [{0}] ({1})", tableName, columns); - - foreach (var index in indexes) - { - ExecuteNonQuery("DROP INDEX {0}", index.IndexName); - ExecuteNonQuery(index.CreateSql(tableName)); - } - } - - public void CopyData(string sourceTable, string destinationTable, IEnumerable columns) - { - var originalCount = GetRowCount(sourceTable); - - var columnsToTransfer = String.Join(",", columns.Select(c => c.Name)); - - var transferCommand = BuildCommand("INSERT INTO {0} SELECT {1} FROM {2};", destinationTable, columnsToTransfer, sourceTable); - - transferCommand.ExecuteNonQuery(); - - var transferredRows = GetRowCount(destinationTable); - - - if (transferredRows != originalCount) - { - throw new ApplicationException(string.Format("Expected {0} rows to be copied from [{1}] to [{2}]. But only copied {3}", originalCount, sourceTable, destinationTable, transferredRows)); - } - } - - - public void DropTable(string tableName) - { - var dropCommand = BuildCommand("DROP TABLE {0};", tableName); - dropCommand.ExecuteNonQuery(); - } - - - public void RenameTable(string tableName, string newName) - { - var renameCommand = BuildCommand("ALTER TABLE {0} RENAME TO {1};", tableName, newName); - renameCommand.ExecuteNonQuery(); - } - - public Dictionary GetDuplicates(string tableName, string columnName) - { - var dupCommand = BuildCommand("select id, {0} from {1}", columnName, tableName); - - var result = new Dictionary(); - using (var reader = dupCommand.ExecuteReader()) - { - while (reader.Read()) - { - - } - } - - - return ReadArray().ToList(); - } - - public int GetRowCount(string tableName) - { - var countCommand = BuildCommand("SELECT COUNT(*) FROM {0};", tableName); - return Convert.ToInt32(countCommand.ExecuteScalar()); - } - - - public SQLiteTransaction BeginTransaction() - { - return _connection.BeginTransaction(); - } - - private SQLiteCommand BuildCommand(string format, params string[] args) - { - var command = new SQLiteCommand(string.Format(format, args)); - command.Connection = _connection; - return command; - } - - - - private void ExecuteNonQuery(string command, params string[] args) - { - var sqLiteCommand = new SQLiteCommand(string.Format(command, args)) - { - Connection = _connection - }; - - sqLiteCommand.ExecuteNonQuery(); - } - - - public class SQLiteColumn - { - public string Name { get; set; } - public string Schema { get; set; } - - public override string ToString() - { - return string.Format("[{0}] {1}", Name, Schema); - } - } - - public class SQLiteIndex - { - public string Column { get; set; } - public string Table { get; set; } - public bool Unique { get; set; } - - public override string ToString() - { - return string.Format("[{0}] Unique: {1}", Column, Unique); - } - - public string IndexName - { - get - { - return string.Format("IX_{0}_{1}", Table, Column); - } - } - - public string CreateSql(string tableName) - { - return string.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName); - } - } - } - - -} \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index b0f14fc9d..3c04658ae 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -56,16 +56,17 @@ namespace NzbDrone.Core.Datastore .Relationship() .HasOne(s => s.Profile, s => s.ProfileId); + Mapper.Entity().RegisterModel("EpisodeFiles") + .Ignore(f => f.Path) + .Relationships.AutoMapICollectionOrComplexProperties(); + Mapper.Entity().RegisterModel("Episodes") .Ignore(e => e.SeriesTitle) .Ignore(e => e.Series) .Ignore(e => e.HasFile) .Relationship() .HasOne(episode => episode.EpisodeFile, episode => episode.EpisodeFileId); - - Mapper.Entity().RegisterModel("EpisodeFiles") - .Relationships.AutoMapICollectionOrComplexProperties(); - + Mapper.Entity().RegisterModel("Profiles"); Mapper.Entity().RegisterModel("QualityDefinitions"); Mapper.Entity().RegisterModel("Logs"); diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index 4dd786a90..8aa998ad4 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -3,12 +3,11 @@ using System.Collections.Generic; using System.Linq; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Serializer; using NzbDrone.Core.IndexerSearch.Definitions; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Qualities; namespace NzbDrone.Core.DecisionEngine { diff --git a/src/NzbDrone.Core/Download/DownloadService.cs b/src/NzbDrone.Core/Download/DownloadService.cs index 874683db3..2e3a66344 100644 --- a/src/NzbDrone.Core/Download/DownloadService.cs +++ b/src/NzbDrone.Core/Download/DownloadService.cs @@ -1,7 +1,7 @@ using System; using NLog; using NzbDrone.Common.EnsureThat; -using NzbDrone.Core.Instrumentation.Extensions; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser.Model; diff --git a/src/NzbDrone.Core/History/HistoryService.cs b/src/NzbDrone.Core/History/HistoryService.cs index ca0fbf3bc..d3482cfde 100644 --- a/src/NzbDrone.Core/History/HistoryService.cs +++ b/src/NzbDrone.Core/History/HistoryService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using NLog; using NzbDrone.Common; @@ -169,7 +170,7 @@ namespace NzbDrone.Core.History //Won't have a value since we publish this event before saving to DB. //history.Data.Add("FileId", message.ImportedEpisode.Id.ToString()); history.Data.Add("DroppedPath", message.EpisodeInfo.Path); - history.Data.Add("ImportedPath", message.ImportedEpisode.Path); + history.Data.Add("ImportedPath", Path.Combine(message.EpisodeInfo.Series.Path, message.ImportedEpisode.RelativePath)); history.Data.Add("DownloadClient", message.DownloadClient); history.Data.Add("DownloadClientId", message.DownloadClientId); diff --git a/src/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs b/src/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs index 6a59c3f12..8d87e8f75 100644 --- a/src/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.Download; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Queue; using NzbDrone.Core.Tv; diff --git a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index a030e0989..ec9665ad9 100644 --- a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Globalization; using System.Threading.Tasks; using NLog; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Indexers; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; using System.Linq; diff --git a/src/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs b/src/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs index 85e849e01..677e71a9b 100644 --- a/src/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs @@ -1,6 +1,6 @@ using NLog; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Download; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.IndexerSearch diff --git a/src/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs b/src/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs index d92675b78..c59326b57 100644 --- a/src/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs @@ -1,6 +1,6 @@ using NLog; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Download; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Tv; diff --git a/src/NzbDrone.Core/Indexers/RssSyncService.cs b/src/NzbDrone.Core/Indexers/RssSyncService.cs index 9c9ed7f1f..c14453078 100644 --- a/src/NzbDrone.Core/Indexers/RssSyncService.cs +++ b/src/NzbDrone.Core/Indexers/RssSyncService.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Download.Pending; using NzbDrone.Core.IndexerSearch; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; diff --git a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs index 13a1cd83a..2a8145339 100644 --- a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs +++ b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs @@ -4,8 +4,8 @@ using System.IO; using System.Linq; using NLog; using NzbDrone.Common.Disk; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Configuration; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.Events; diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeFile.cs b/src/NzbDrone.Core/MediaFiles/EpisodeFile.cs index 242ee02aa..b36bb6f02 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeFile.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeFile.cs @@ -8,20 +8,26 @@ namespace NzbDrone.Core.MediaFiles { public class EpisodeFile : ModelBase { - public int SeriesId { get; set; } - public int SeasonNumber { get; set; } - public string Path { get; set; } - public long Size { get; set; } + public Int32 SeriesId { get; set; } + public Int32 SeasonNumber { get; set; } + public String RelativePath { get; set; } + public String Path { get; set; } + public Int64 Size { get; set; } public DateTime DateAdded { get; set; } - public string SceneName { get; set; } - public string ReleaseGroup { get; set; } + public String SceneName { get; set; } + public String ReleaseGroup { get; set; } public QualityModel Quality { get; set; } public MediaInfoModel MediaInfo { get; set; } public LazyList Episodes { get; set; } - public override string ToString() + public override String ToString() { - return String.Format("[{0}] {1}", Id, Path); + return String.Format("[{0}] {1}", Id, RelativePath); } +// +// public String Path(Series series) +// { +// return System.IO.Path.Combine(series.Path, RelativePath); +// } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs index 98d801144..bda2122f4 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs @@ -50,7 +50,7 @@ namespace NzbDrone.Core.MediaFiles { var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id); var newFileName = _buildFileNames.BuildFileName(episodes, series, episodeFile); - var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); + var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.RelativePath)); _logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath); @@ -60,7 +60,7 @@ namespace NzbDrone.Core.MediaFiles public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) { var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile); - var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); + var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path)); _logger.Debug("Moving episode file: {0} to {1}", episodeFile, filePath); @@ -70,27 +70,29 @@ namespace NzbDrone.Core.MediaFiles public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) { var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile); - var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path)); + var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path)); _logger.Debug("Copying episode file: {0} to {1}", episodeFile, filePath); return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, true); } - private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List episodes, string destinationFilename, bool copyOnly) + private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List episodes, String destinationFilename, Boolean copyOnly) { Ensure.That(episodeFile, () => episodeFile).IsNotNull(); Ensure.That(series,() => series).IsNotNull(); Ensure.That(destinationFilename, () => destinationFilename).IsValidPath(); - if (!_diskProvider.FileExists(episodeFile.Path)) + var episodeFilePath = episodeFile.Path ?? Path.Combine(series.Path, episodeFile.RelativePath); + + if (!_diskProvider.FileExists(episodeFilePath)) { - throw new FileNotFoundException("Episode file path does not exist", episodeFile.Path); + throw new FileNotFoundException("Episode file path does not exist", episodeFilePath); } - if (episodeFile.Path.PathEquals(destinationFilename)) + if (episodeFilePath.PathEquals(destinationFilename)) { - throw new SameFilenameException("File not moved, source and destination are the same", episodeFile.Path); + throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath); } var directoryName = new FileInfo(destinationFilename).DirectoryName; @@ -116,15 +118,16 @@ namespace NzbDrone.Core.MediaFiles if (copyOnly) { - _logger.Debug("Copying [{0}] > [{1}]", episodeFile.Path, destinationFilename); - _diskProvider.CopyFile(episodeFile.Path, destinationFilename); + _logger.Debug("Copying [{0}] > [{1}]", episodeFilePath, destinationFilename); + _diskProvider.CopyFile(episodeFilePath, destinationFilename); } else { - _logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, destinationFilename); - _diskProvider.MoveFile(episodeFile.Path, destinationFilename); + _logger.Debug("Moving [{0}] > [{1}]", episodeFilePath, destinationFilename); + _diskProvider.MoveFile(episodeFilePath, destinationFilename); } - episodeFile.Path = destinationFilename; + + episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilename); _updateEpisodeFileService.ChangeFileDateForFile(episodeFile, series, episodes); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index 5494717e8..a9cba1359 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -86,6 +86,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport oldFiles = moveResult.OldFiles; } + else + { + episodeFile.RelativePath = localEpisode.Series.Path.GetRelativePath(episodeFile.Path); + } + _mediaFileService.Add(episodeFile); imported.Add(importDecision); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs index 93382b430..0806825a8 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs @@ -44,7 +44,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport public List GetImportDecisions(List videoFiles, Series series, bool sceneSource, QualityModel quality = null) { - var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series.Id); + var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series); _logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count()); diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs index f45753929..5fef9e220 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; +using System.IO; using System.Linq; using NLog; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Events; using NzbDrone.Common; @@ -16,7 +18,7 @@ namespace NzbDrone.Core.MediaFiles List GetFilesBySeries(int seriesId); List GetFilesBySeason(int seriesId, int seasonNumber); List GetFilesWithoutMediaInfo(); - List FilterExistingFiles(List files, int seriesId); + List FilterExistingFiles(List files, Series series); EpisodeFile Get(int id); List Get(IEnumerable ids); } @@ -68,9 +70,9 @@ namespace NzbDrone.Core.MediaFiles return _mediaFileRepository.GetFilesWithoutMediaInfo(); } - public List FilterExistingFiles(List files, int seriesId) + public List FilterExistingFiles(List files, Series series) { - var seriesFiles = GetFilesBySeries(seriesId).Select(f => f.Path).ToList(); + var seriesFiles = GetFilesBySeries(series.Id).Select(f => Path.Combine(series.Path, f.RelativePath)).ToList(); if (!seriesFiles.Any()) return files; diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs index 750091d94..de27a5cbc 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using NLog; using NzbDrone.Common; @@ -39,25 +40,20 @@ namespace NzbDrone.Core.MediaFiles foreach (var episodeFile in seriesFile) { + var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath); + try { - if (!_diskProvider.FileExists(episodeFile.Path)) + if (!_diskProvider.FileExists(episodeFilePath)) { - _logger.Debug("File [{0}] no longer exists on disk, removing from db", episodeFile.Path); + _logger.Debug("File [{0}] no longer exists on disk, removing from db", episodeFilePath); _mediaFileService.Delete(episodeFile); continue; } - if (!series.Path.IsParentPath(episodeFile.Path)) - { - _logger.Debug("File [{0}] does not belong to this series, removing from db", episodeFile.Path); - _mediaFileService.Delete(episodeFile); - continue; - } - if (!episodes.Any(e => e.EpisodeFileId == episodeFile.Id)) { - _logger.Debug("File [{0}] is not assigned to any episodes, removing from db", episodeFile.Path); + _logger.Debug("File [{0}] is not assigned to any episodes, removing from db", episodeFilePath); _mediaFileService.Delete(episodeFile); continue; } diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/UpdateMediaInfoService.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/UpdateMediaInfoService.cs index cf3502dea..534010c22 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/UpdateMediaInfoService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/UpdateMediaInfoService.cs @@ -1,13 +1,11 @@ -using NLog; +using System.IO; +using NLog; using NzbDrone.Common.Disk; -using NzbDrone.Core.Lifecycle; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; -using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace NzbDrone.Core.MediaFiles.MediaInfo { @@ -29,11 +27,11 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo _logger = logger; } - private void UpdateMediaInfo(List mediaFiles) + private void UpdateMediaInfo(Series series, List mediaFiles) { foreach (var mediaFile in mediaFiles) { - var path = mediaFile.Path; + var path = Path.Combine(series.Path, mediaFile.RelativePath); if (!_diskProvider.FileExists(path)) { @@ -57,7 +55,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo .Where(c => c.MediaInfo == null) .ToList(); - UpdateMediaInfo(mediaFiles); + UpdateMediaInfo(message.Series, mediaFiles); } } } diff --git a/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs index a960cca5f..c50a27a3d 100644 --- a/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Core.Instrumentation.Extensions; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Commands; @@ -77,18 +77,19 @@ namespace NzbDrone.Core.MediaFiles { var file = f; var episodesInFile = episodes.Where(e => e.EpisodeFileId == file.Id).ToList(); + var episodeFilePath = Path.Combine(series.Path, file.RelativePath); if (!episodesInFile.Any()) { - _logger.Warn("File ({0}) is not linked to any episodes", file.Path); + _logger.Warn("File ({0}) is not linked to any episodes", episodeFilePath); continue; } var seasonNumber = episodesInFile.First().SeasonNumber; var newName = _filenameBuilder.BuildFileName(episodesInFile, series, file); - var newPath = _filenameBuilder.BuildFilePath(series, seasonNumber, newName, Path.GetExtension(file.Path)); + var newPath = _filenameBuilder.BuildFilePath(series, seasonNumber, newName, Path.GetExtension(episodeFilePath)); - if (!file.Path.PathEquals(newPath)) + if (!episodeFilePath.PathEquals(newPath)) { yield return new RenameEpisodeFilePreview { @@ -96,7 +97,7 @@ namespace NzbDrone.Core.MediaFiles SeasonNumber = seasonNumber, EpisodeNumbers = episodesInFile.Select(e => e.EpisodeNumber).ToList(), EpisodeFileId = file.Id, - ExistingPath = series.Path.GetRelativePath(file.Path), + ExistingPath = file.RelativePath, NewPath = series.Path.GetRelativePath(newPath) }; } @@ -109,6 +110,8 @@ namespace NzbDrone.Core.MediaFiles foreach (var episodeFile in episodeFiles) { + var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath); + try { _logger.Debug("Renaming episode file: {0}", episodeFile); @@ -125,7 +128,7 @@ namespace NzbDrone.Core.MediaFiles } catch (Exception ex) { - _logger.ErrorException("Failed to rename file: " + episodeFile.Path, ex); + _logger.ErrorException("Failed to rename file: " + episodeFilePath, ex); } } diff --git a/src/NzbDrone.Core/MediaFiles/UpdateEpisodeFileService.cs b/src/NzbDrone.Core/MediaFiles/UpdateEpisodeFileService.cs index 79eae53c2..7eca4f95d 100644 --- a/src/NzbDrone.Core/MediaFiles/UpdateEpisodeFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/UpdateEpisodeFileService.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Configuration; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; @@ -43,6 +44,8 @@ namespace NzbDrone.Core.MediaFiles private bool ChangeFileDate(EpisodeFile episodeFile, Series series, List episodes) { + var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath); + switch (_configService.FileDate) { case FileDateType.LocalAirDate: @@ -55,7 +58,7 @@ namespace NzbDrone.Core.MediaFiles return false; } - return ChangeFileDateToLocalAirDate(episodeFile.Path, airDate, airTime); + return ChangeFileDateToLocalAirDate(episodeFilePath, airDate, airTime); } case FileDateType.UtcAirDate: @@ -67,7 +70,7 @@ namespace NzbDrone.Core.MediaFiles return false; } - return ChangeFileDateToUtcAirDate(episodeFile.Path, airDateUtc.Value); + return ChangeFileDateToUtcAirDate(episodeFilePath, airDateUtc.Value); } } diff --git a/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs b/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs index 924d60b9c..6d5a94be2 100644 --- a/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.IO; +using System.Linq; using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; @@ -43,11 +44,12 @@ namespace NzbDrone.Core.MediaFiles foreach (var existingFile in existingFiles) { var file = existingFile.First(); + var episodeFilePath = Path.Combine(localEpisode.Series.Path, file.RelativePath); - if (_diskProvider.FileExists(file.Path)) + if (_diskProvider.FileExists(episodeFilePath)) { _logger.Debug("Removing existing episode file: {0}", file); - _recycleBinProvider.DeleteFile(file.Path); + _recycleBinProvider.DeleteFile(episodeFilePath); } moveFileResult.OldFiles.Add(file); diff --git a/src/NzbDrone.Core/Messaging/Commands/TestCommandExecutor.cs b/src/NzbDrone.Core/Messaging/Commands/TestCommandExecutor.cs index f6c6094b5..09c0f777d 100644 --- a/src/NzbDrone.Core/Messaging/Commands/TestCommandExecutor.cs +++ b/src/NzbDrone.Core/Messaging/Commands/TestCommandExecutor.cs @@ -1,6 +1,6 @@ using System.Threading; using NLog; -using NzbDrone.Core.Instrumentation.Extensions; +using NzbDrone.Common.Instrumentation.Extensions; namespace NzbDrone.Core.Messaging.Commands { diff --git a/src/NzbDrone.Core/MetaData/Consumers/Roksbox/RoksboxMetadata.cs b/src/NzbDrone.Core/MetaData/Consumers/Roksbox/RoksboxMetadata.cs index 0cc766fa8..4a735d96d 100644 --- a/src/NzbDrone.Core/MetaData/Consumers/Roksbox/RoksboxMetadata.cs +++ b/src/NzbDrone.Core/MetaData/Consumers/Roksbox/RoksboxMetadata.cs @@ -49,12 +49,12 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox if (metadataFile.Type == MetadataType.EpisodeImage) { - newFilename = GetEpisodeImageFilename(episodeFile.Path); + newFilename = GetEpisodeImageFilename(episodeFile.RelativePath); } else if (metadataFile.Type == MetadataType.EpisodeMetadata) { - newFilename = GetEpisodeMetadataFilename(episodeFile.Path); + newFilename = GetEpisodeMetadataFilename(episodeFile.RelativePath); } else @@ -64,6 +64,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox } var existingFilename = Path.Combine(series.Path, metadataFile.RelativePath); + newFilename = Path.Combine(series.Path, newFilename); if (!newFilename.PathEquals(existingFilename)) { @@ -159,8 +160,8 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox { return null; } - - _logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path); + + _logger.Debug("Generating Episode Metadata for: {0}", episodeFile.RelativePath); var xmlResult = String.Empty; foreach (var episode in episodeFile.Episodes.Value) @@ -191,7 +192,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox } } - return new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.Path), xmlResult.Trim(Environment.NewLine.ToCharArray())); + return new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray())); } public override List SeriesImages(Series series) @@ -244,7 +245,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox return new List(); } - return new List {new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url)}; + return new List {new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url)}; } private string GetEpisodeMetadataFilename(string episodeFilePath) diff --git a/src/NzbDrone.Core/MetaData/Consumers/Wdtv/WdtvMetadata.cs b/src/NzbDrone.Core/MetaData/Consumers/Wdtv/WdtvMetadata.cs index c48f07d01..c8070c6d3 100644 --- a/src/NzbDrone.Core/MetaData/Consumers/Wdtv/WdtvMetadata.cs +++ b/src/NzbDrone.Core/MetaData/Consumers/Wdtv/WdtvMetadata.cs @@ -48,12 +48,12 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv if (metadataFile.Type == MetadataType.EpisodeImage) { - newFilename = GetEpisodeImageFilename(episodeFile.Path); + newFilename = GetEpisodeImageFilename(episodeFile.RelativePath); } else if (metadataFile.Type == MetadataType.EpisodeMetadata) { - newFilename = GetEpisodeMetadataFilename(episodeFile.Path); + newFilename = GetEpisodeMetadataFilename(episodeFile.RelativePath); } else @@ -63,6 +63,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv } var existingFilename = Path.Combine(series.Path, metadataFile.RelativePath); + newFilename = Path.Combine(series.Path, newFilename); if (!newFilename.PathEquals(existingFilename)) { @@ -151,7 +152,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv return null; } - _logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path); + _logger.Debug("Generating Episode Metadata for: {0}", Path.Combine(series.Path, episodeFile.RelativePath)); var xmlResult = String.Empty; foreach (var episode in episodeFile.Episodes.Value) @@ -190,7 +191,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv } } - var filename = GetEpisodeMetadataFilename(episodeFile.Path); + var filename = GetEpisodeMetadataFilename(episodeFile.RelativePath); return new MetadataFileResult(filename, xmlResult.Trim(Environment.NewLine.ToCharArray())); } @@ -264,7 +265,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv return new List(); } - return new List{ new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url) }; + return new List{ new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url) }; } private string GetEpisodeMetadataFilename(string episodeFilePath) diff --git a/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs b/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs index 8aaca9a16..fec560745 100644 --- a/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs +++ b/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs @@ -50,12 +50,12 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc if (metadataFile.Type == MetadataType.EpisodeImage) { - newFilename = GetEpisodeImageFilename(episodeFile.Path); + newFilename = GetEpisodeImageFilename(episodeFile.RelativePath); } else if (metadataFile.Type == MetadataType.EpisodeMetadata) { - newFilename = GetEpisodeNfoFilename(episodeFile.Path); + newFilename = GetEpisodeNfoFilename(episodeFile.RelativePath); } else @@ -65,6 +65,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc } var existingFilename = Path.Combine(series.Path, metadataFile.RelativePath); + newFilename = Path.Combine(series.Path, newFilename); if (!newFilename.PathEquals(existingFilename)) { @@ -214,7 +215,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc return null; } - _logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path); + _logger.Debug("Generating Episode Metadata for: {0}", Path.Combine(series.Path, episodeFile.RelativePath)); var xmlResult = String.Empty; foreach (var episode in episodeFile.Episodes.Value) @@ -265,7 +266,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc } } - return new MetadataFileResult(GetEpisodeNfoFilename(episodeFile.Path), xmlResult.Trim(Environment.NewLine.ToCharArray())); + return new MetadataFileResult(GetEpisodeNfoFilename(episodeFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray())); } public override List SeriesImages(Series series) @@ -305,7 +306,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc return new List { - new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url) + new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url) }; } diff --git a/src/NzbDrone.Core/MetaData/MetadataService.cs b/src/NzbDrone.Core/MetaData/MetadataService.cs index f6794d6d3..a2d14ac92 100644 --- a/src/NzbDrone.Core/MetaData/MetadataService.cs +++ b/src/NzbDrone.Core/MetaData/MetadataService.cs @@ -174,18 +174,18 @@ namespace NzbDrone.Core.Metadata return null; } - var relativePath = series.Path.GetRelativePath(episodeMetadata.Path); + var fullPath = Path.Combine(series.Path, episodeMetadata.Path); var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeMetadata && c.EpisodeFileId == episodeFile.Id); if (existingMetadata != null) { - var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath); - if (!episodeMetadata.Path.PathEquals(fullPath)) + var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath); + if (!episodeMetadata.Path.PathEquals(existingFullPath)) { - _diskProvider.MoveFile(fullPath, episodeMetadata.Path); - existingMetadata.RelativePath = relativePath; + _diskProvider.MoveFile(existingFullPath, fullPath); + existingMetadata.RelativePath = episodeMetadata.Path; } } @@ -198,7 +198,7 @@ namespace NzbDrone.Core.Metadata EpisodeFileId = episodeFile.Id, Consumer = consumer.GetType().Name, Type = MetadataType.EpisodeMetadata, - RelativePath = relativePath + RelativePath = episodeMetadata.Path }; if (hash == metadata.Hash) @@ -206,8 +206,8 @@ namespace NzbDrone.Core.Metadata return null; } - _logger.Debug("Writing Episode Metadata to: {0}", episodeMetadata.Path); - _diskProvider.WriteAllText(episodeMetadata.Path, episodeMetadata.Contents); + _logger.Debug("Writing Episode Metadata to: {0}", fullPath); + _diskProvider.WriteAllText(fullPath, episodeMetadata.Contents); metadata.Hash = hash; @@ -289,24 +289,24 @@ namespace NzbDrone.Core.Metadata foreach (var image in consumer.EpisodeImages(series, episodeFile)) { + var fullPath = Path.Combine(series.Path, image.Path); + if (_diskProvider.FileExists(image.Path)) { _logger.Debug("Episode image already exists: {0}", image.Path); continue; } - var relativePath = series.Path.GetRelativePath(image.Path); - var existingMetadata = existingMetadataFiles.FirstOrDefault(c => c.Type == MetadataType.EpisodeImage && c.EpisodeFileId == episodeFile.Id); if (existingMetadata != null) { - var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath); - if (!image.Path.PathEquals(fullPath)) + var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath); + if (!fullPath.PathEquals(existingFullPath)) { - _diskProvider.MoveFile(fullPath, image.Path); - existingMetadata.RelativePath = relativePath; + _diskProvider.MoveFile(fullPath, fullPath); + existingMetadata.RelativePath = image.Path; return new List{ existingMetadata }; } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index a93414f85..812dd0d02 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -215,6 +215,8 @@ + + @@ -427,7 +429,6 @@ - @@ -701,6 +702,7 @@ + @@ -714,8 +716,10 @@ + + diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index e6b27419a..6cb9be82a 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -6,6 +6,7 @@ using System.Text.RegularExpressions; using NLog; using NzbDrone.Common; using NzbDrone.Common.Cache; +using NzbDrone.Common.EnsureThat; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Tv; @@ -77,7 +78,7 @@ namespace NzbDrone.Core.Organizer { if (episodeFile.SceneName.IsNullOrWhiteSpace()) { - return Path.GetFileNameWithoutExtension(episodeFile.Path); + return Path.GetFileNameWithoutExtension(episodeFile.RelativePath); } return episodeFile.SceneName; @@ -204,6 +205,8 @@ namespace NzbDrone.Core.Organizer public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension) { + Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace(); + string path = series.Path; if (series.SeasonFolder) diff --git a/src/NzbDrone.Core/Organizer/FilenameSampleService.cs b/src/NzbDrone.Core/Organizer/FilenameSampleService.cs index 80ca9083d..e0a495bca 100644 --- a/src/NzbDrone.Core/Organizer/FilenameSampleService.cs +++ b/src/NzbDrone.Core/Organizer/FilenameSampleService.cs @@ -93,7 +93,7 @@ namespace NzbDrone.Core.Organizer _singleEpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), - Path = @"C:\Test\Series.Title.S01E01.720p.HDTV.x264-EVOLVE.mkv", + RelativePath = "Series.Title.S01E01.720p.HDTV.x264-EVOLVE.mkv", ReleaseGroup = "RlsGrp", MediaInfo = mediaInfo }; @@ -101,7 +101,7 @@ namespace NzbDrone.Core.Organizer _multiEpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), - Path = @"C:\Test\Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE.mkv", + RelativePath = "Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE.mkv", ReleaseGroup = "RlsGrp", MediaInfo = mediaInfo }; @@ -109,7 +109,7 @@ namespace NzbDrone.Core.Organizer _dailyEpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), - Path = @"C:\Test\Series.Title.2013.10.30.HDTV.x264-EVOLVE.mkv", + RelativePath = "Series.Title.2013.10.30.HDTV.x264-EVOLVE.mkv", ReleaseGroup = "RlsGrp", MediaInfo = mediaInfo }; @@ -117,7 +117,7 @@ namespace NzbDrone.Core.Organizer _animeEpisodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), - Path = @"C:\Test\Series.Title.001.HDTV.x264-EVOLVE.mkv", + RelativePath = "Series.Title.001.HDTV.x264-EVOLVE.mkv", ReleaseGroup = "RlsGrp", MediaInfo = mediaInfoAnime }; diff --git a/src/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs b/src/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs index 13d4ae0de..e75b4587d 100644 --- a/src/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs +++ b/src/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs @@ -8,7 +8,6 @@ using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.ProgressMessaging { - public class ProgressMessageTarget : Target, IHandle { private readonly IEventAggregator _eventAggregator; diff --git a/src/NzbDrone.Core/Tv/Commands/MoveSeriesCommand.cs b/src/NzbDrone.Core/Tv/Commands/MoveSeriesCommand.cs new file mode 100644 index 000000000..59f0729bd --- /dev/null +++ b/src/NzbDrone.Core/Tv/Commands/MoveSeriesCommand.cs @@ -0,0 +1,13 @@ +using System; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Tv.Commands +{ + public class MoveSeriesCommand : Command + { + public Int32 SeriesId { get; set; } + public String SourcePath { get; set; } + public String DestinationPath { get; set; } + public String DestinationRootFolder { get; set; } + } +} diff --git a/src/NzbDrone.Core/Tv/EpisodeService.cs b/src/NzbDrone.Core/Tv/EpisodeService.cs index a7a2326f0..cd46c6b99 100644 --- a/src/NzbDrone.Core/Tv/EpisodeService.cs +++ b/src/NzbDrone.Core/Tv/EpisodeService.cs @@ -199,7 +199,7 @@ namespace NzbDrone.Core.Tv foreach (var episode in message.EpisodeFile.Episodes.Value) { _episodeRepository.SetFileId(episode.Id, message.EpisodeFile.Id); - _logger.Debug("Linking [{0}] > [{1}]", message.EpisodeFile.Path, episode); + _logger.Debug("Linking [{0}] > [{1}]", message.EpisodeFile.RelativePath, episode); } } } diff --git a/src/NzbDrone.Core/Tv/Events/SeriesMovedEvent.cs b/src/NzbDrone.Core/Tv/Events/SeriesMovedEvent.cs new file mode 100644 index 000000000..7d4b42654 --- /dev/null +++ b/src/NzbDrone.Core/Tv/Events/SeriesMovedEvent.cs @@ -0,0 +1,19 @@ +using System; +using NzbDrone.Common.Messaging; + +namespace NzbDrone.Core.Tv.Events +{ + public class SeriesMovedEvent : IEvent + { + public Series Series { get; set; } + public String SourcePath { get; set; } + public String DestinationPath { get; set; } + + public SeriesMovedEvent(Series series, string sourcePath, string destinationPath) + { + Series = series; + SourcePath = sourcePath; + DestinationPath = destinationPath; + } + } +} diff --git a/src/NzbDrone.Core/Tv/MoveSeriesService.cs b/src/NzbDrone.Core/Tv/MoveSeriesService.cs new file mode 100644 index 000000000..78f10c50f --- /dev/null +++ b/src/NzbDrone.Core/Tv/MoveSeriesService.cs @@ -0,0 +1,72 @@ +using System; +using System.IO; +using NLog; +using NzbDrone.Common; +using NzbDrone.Common.Disk; +using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Tv.Commands; +using NzbDrone.Core.Tv.Events; + +namespace NzbDrone.Core.Tv +{ + public class MoveSeriesService : IExecute + { + private readonly ISeriesService _seriesService; + private readonly IBuildFileNames _filenameBuilder; + private readonly IDiskProvider _diskProvider; + private readonly IEventAggregator _eventAggregator; + private readonly Logger _logger; + + public MoveSeriesService(ISeriesService seriesService, + IBuildFileNames filenameBuilder, + IDiskProvider diskProvider, + IEventAggregator eventAggregator, + Logger logger) + { + _seriesService = seriesService; + _filenameBuilder = filenameBuilder; + _diskProvider = diskProvider; + _eventAggregator = eventAggregator; + _logger = logger; + } + + public void Execute(MoveSeriesCommand message) + { + var series = _seriesService.GetSeries(message.SeriesId); + var source = message.SourcePath; + var destination = message.DestinationPath; + + if (!message.DestinationRootFolder.IsNullOrWhiteSpace()) + { + _logger.Debug("Buiding destination path using root folder: {0} and the series title", message.DestinationRootFolder); + destination = Path.Combine(message.DestinationRootFolder, _filenameBuilder.GetSeriesFolder(series)); + } + + _logger.ProgressInfo("Moving {0} from '{1}' to '{2}'", series.Title, source, destination); + + //TODO: Move to transactional disk operations + try + { + _diskProvider.MoveFolder(source, destination); + } + catch (IOException ex) + { + var errorMessage = String.Format("Unable to move series from '{0}' to '{1}'", source, destination); + + _logger.ErrorException(errorMessage, ex); + throw; + } + + _logger.ProgressInfo("{0} moved successfully to {1}", series.Title, series.Path); + + //Update the series path to the new path + series.Path = destination; + series = _seriesService.UpdateSeries(series); + + _eventAggregator.PublishEvent(new SeriesMovedEvent(series, source, destination)); + } + } +} diff --git a/src/NzbDrone.Core/Tv/RefreshSeriesService.cs b/src/NzbDrone.Core/Tv/RefreshSeriesService.cs index b7b4563b0..8ee9fff76 100644 --- a/src/NzbDrone.Core/Tv/RefreshSeriesService.cs +++ b/src/NzbDrone.Core/Tv/RefreshSeriesService.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using NLog; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.DataAugmentation.DailySeries; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; diff --git a/src/NzbDrone.Core/Tv/SeriesEditedService.cs b/src/NzbDrone.Core/Tv/SeriesEditedService.cs index faa6d6e35..02097444e 100644 --- a/src/NzbDrone.Core/Tv/SeriesEditedService.cs +++ b/src/NzbDrone.Core/Tv/SeriesEditedService.cs @@ -16,8 +16,6 @@ namespace NzbDrone.Core.Tv public void Handle(SeriesEditedEvent message) { - //TODO: Refresh if path has changed (also move files) - if (message.Series.SeriesType != message.OldSeries.SeriesType) { _commandExecutor.PublishCommandAsync(new RefreshSeriesCommand(message.Series.Id)); diff --git a/src/NzbDrone.Core/Update/InstallUpdateService.cs b/src/NzbDrone.Core/Update/InstallUpdateService.cs index 8b473f989..83d459f4e 100644 --- a/src/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/src/NzbDrone.Core/Update/InstallUpdateService.cs @@ -5,10 +5,10 @@ using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Http; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Processes; using NzbDrone.Core.Backup; using NzbDrone.Core.Configuration; -using NzbDrone.Core.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Update.Commands; diff --git a/src/NzbDrone.Core/Update/UpdateCheckService.cs b/src/NzbDrone.Core/Update/UpdateCheckService.cs index b7e787895..720580eb2 100644 --- a/src/NzbDrone.Core/Update/UpdateCheckService.cs +++ b/src/NzbDrone.Core/Update/UpdateCheckService.cs @@ -1,7 +1,7 @@ using NLog; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Configuration; -using NzbDrone.Core.Instrumentation.Extensions; namespace NzbDrone.Core.Update {