From e7e9e2b15453e0f22f1e73be12dc0cc21e3ed1c8 Mon Sep 17 00:00:00 2001 From: Qstick Date: Thu, 15 Feb 2018 13:39:01 +0100 Subject: [PATCH] Added: Importing extra files from downloaded movies and generate metadata such as .nfo (#2506) Fixes #121, Fixes #167, Fixes #2262 and Fixes #1104 --- .../Config/MediaManagementConfigResource.cs | 4 +- .../ExtraFiles/ExtraFileModule.cs | 45 +++ .../ExtraFiles/ExtraFileResource.cs | 84 ++++ src/NzbDrone.Api/NzbDrone.Api.csproj | 2 + .../Checks/DeleteBadMediaCovers.cs | 36 +- .../CleanupDuplicateMetadataFilesFixture.cs | 65 ++- .../CleanupOrphanedMetadataFilesFixture.cs | 64 +-- .../Roksbox/FindMetadataFileFixture.cs | 48 +-- .../Consumers/Wdtv/FindMetadataFileFixture.cs | 44 +- .../Configuration/ConfigService.cs | 11 +- .../Configuration/IConfigService.cs | 3 +- .../Datastore/Migration/142_movie_extras.cs | 44 ++ src/NzbDrone.Core/Datastore/TableMapping.cs | 40 +- .../Extras/ExistingExtraFileService.cs | 20 +- src/NzbDrone.Core/Extras/ExtraService.cs | 82 ++-- src/NzbDrone.Core/Extras/Files/ExtraFile.cs | 14 +- .../Extras/Files/ExtraFileManager.cs | 88 +++- .../Extras/Files/ExtraFileRepository.cs | 38 +- .../Extras/Files/ExtraFileService.cs | 46 +-- .../Extras/IImportExistingExtraFiles.cs | 4 +- .../Extras/ImportExistingExtraFilesBase.cs | 24 +- .../MediaBrowser/MediaBrowserMetadata.cs | 112 ++---- .../MediaBrowserMetadataSettings.cs | 8 +- .../Consumers/Roksbox/RoksboxMetadata.cs | 209 +++------- .../Roksbox/RoksboxMetadataSettings.cs | 22 +- .../Metadata/Consumers/Wdtv/WdtvMetadata.cs | 201 ++------- .../Consumers/Wdtv/WdtvMetadataSettings.cs | 22 +- .../Metadata/Consumers/Xbmc/XbmcMetadata.cs | 380 ++++++------------ .../Consumers/Xbmc/XbmcMetadataSettings.cs | 28 +- .../Metadata/ExistingMetadataImporter.cs | 33 +- .../Files/CleanMetadataFileService.cs | 12 +- .../Metadata/Files/MetadataFileService.cs | 6 +- .../Extras/Metadata/IMetadata.cs | 13 +- .../Extras/Metadata/MetadataBase.cs | 19 +- .../Extras/Metadata/MetadataService.cs | 270 +++---------- .../Extras/Metadata/MetadataType.cs | 9 +- .../Others/ExistingOtherExtraImporter.cs | 39 +- .../Extras/Others/OtherExtraFileService.cs | 6 +- .../Extras/Others/OtherExtraService.cs | 60 +-- .../Subtitles/ExistingSubtitleImporter.cs | 29 +- .../Subtitles/SubtitleFileExtensions.cs | 5 +- .../Extras/Subtitles/SubtitleFileService.cs | 6 +- .../Extras/Subtitles/SubtitleService.cs | 82 ++-- .../CleanupDuplicateMetadataFiles.cs | 34 +- .../CleanupOrphanedMetadataFiles.cs | 32 +- .../Housekeepers/DeleteBadMediaCovers.cs | 18 +- .../MediaFiles/DiskScanService.cs | 9 +- .../DownloadedMovieImportService.cs | 5 +- .../EpisodeImport/ImportApprovedEpisodes.cs | 4 +- .../EpisodeImport/ImportApprovedMovie.cs | 2 +- .../MediaFiles/MediaFileExtensions.cs | 7 +- .../MediaFiles/RenameMovieFileService.cs | 7 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 7 +- src/NzbDrone.Core/Tv/RefreshMovieService.cs | 27 +- src/UI/Cells/ExtraExtensionCell.js | 14 + src/UI/Cells/ExtraTypeCell.js | 19 + src/UI/Movies/Details/MoviesDetailsLayout.js | 72 ++-- .../Movies/Details/MoviesDetailsTemplate.hbs | 5 +- src/UI/Movies/Files/AllFilesLayout.js | 30 ++ .../Movies/Files/AllFilesLayoutTemplate.hbs | 5 + .../ExtraFileModel.js} | 0 .../Files/Extras/ExtraFilesCollection.js | 37 ++ .../Movies/Files/Extras/ExtraFilesLayout.js | 62 +++ .../Files/Extras/ExtraFilesLayoutTemplate.hbs | 1 + src/UI/Movies/Files/FilesLayout.js | 148 ------- src/UI/Movies/Files/FilesLayoutTemplate.hbs | 3 - .../Files/{ => Media}/DeleteFileCell.js | 0 .../{ => Media}/Edit/EditFileTemplate.hbs | 0 .../Files/{ => Media}/Edit/EditFileView.js | 14 +- .../Movies/Files/{ => Media}/EditFileCell.js | 0 src/UI/Movies/Files/Media/FileModel.js | 3 + .../Files/{ => Media}/FilesCollection.js | 2 +- src/UI/Movies/Files/Media/FilesLayout.js | 120 ++++++ .../Files/Media/FilesLayoutTemplate.hbs | 2 + .../MediaManagement/Sorting/SortingView.js | 29 +- .../Sorting/SortingViewTemplate.hbs | 41 +- .../Metadata/MetadataItemViewTemplate.hbs | 2 +- src/UI/Shared/Modal/ModalController.js | 2 +- 78 files changed, 1381 insertions(+), 1759 deletions(-) create mode 100644 src/NzbDrone.Api/ExtraFiles/ExtraFileModule.cs create mode 100644 src/NzbDrone.Api/ExtraFiles/ExtraFileResource.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/142_movie_extras.cs create mode 100644 src/UI/Cells/ExtraExtensionCell.js create mode 100644 src/UI/Cells/ExtraTypeCell.js create mode 100644 src/UI/Movies/Files/AllFilesLayout.js create mode 100644 src/UI/Movies/Files/AllFilesLayoutTemplate.hbs rename src/UI/Movies/Files/{FileModel.js => Extras/ExtraFileModel.js} (100%) create mode 100644 src/UI/Movies/Files/Extras/ExtraFilesCollection.js create mode 100644 src/UI/Movies/Files/Extras/ExtraFilesLayout.js create mode 100644 src/UI/Movies/Files/Extras/ExtraFilesLayoutTemplate.hbs delete mode 100644 src/UI/Movies/Files/FilesLayout.js delete mode 100644 src/UI/Movies/Files/FilesLayoutTemplate.hbs rename src/UI/Movies/Files/{ => Media}/DeleteFileCell.js (100%) rename src/UI/Movies/Files/{ => Media}/Edit/EditFileTemplate.hbs (100%) rename src/UI/Movies/Files/{ => Media}/Edit/EditFileView.js (74%) rename src/UI/Movies/Files/{ => Media}/EditFileCell.js (100%) create mode 100644 src/UI/Movies/Files/Media/FileModel.js rename src/UI/Movies/Files/{ => Media}/FilesCollection.js (89%) create mode 100644 src/UI/Movies/Files/Media/FilesLayout.js create mode 100644 src/UI/Movies/Files/Media/FilesLayoutTemplate.hbs diff --git a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs index 39b451050..e5147e601 100644 --- a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs +++ b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs @@ -1,4 +1,4 @@ -using NzbDrone.Api.REST; +using NzbDrone.Api.REST; using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles; @@ -22,6 +22,7 @@ public class MediaManagementConfigResource : RestResource public bool SkipFreeSpaceCheckWhenImporting { get; set; } public bool CopyUsingHardlinks { get; set; } + public bool ImportExtraFiles { get; set; } public string ExtraFileExtensions { get; set; } public bool EnableMediaInfo { get; set; } } @@ -48,6 +49,7 @@ public static MediaManagementConfigResource ToResource(IConfigService model) SkipFreeSpaceCheckWhenImporting = model.SkipFreeSpaceCheckWhenImporting, CopyUsingHardlinks = model.CopyUsingHardlinks, + ImportExtraFiles = model.ImportExtraFiles, ExtraFileExtensions = model.ExtraFileExtensions, EnableMediaInfo = model.EnableMediaInfo }; diff --git a/src/NzbDrone.Api/ExtraFiles/ExtraFileModule.cs b/src/NzbDrone.Api/ExtraFiles/ExtraFileModule.cs new file mode 100644 index 000000000..ed48df23b --- /dev/null +++ b/src/NzbDrone.Api/ExtraFiles/ExtraFileModule.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using NzbDrone.Api.REST; +using NzbDrone.Core.Extras.Files; +using NzbDrone.Core.Extras.Metadata.Files; +using NzbDrone.Core.Extras.Others; +using NzbDrone.Core.Extras.Subtitles; + +namespace NzbDrone.Api.ExtraFiles +{ + public class ExtraFileModule : NzbDroneRestModule + { + private readonly IExtraFileService _subtitleFileService; + private readonly IExtraFileService _metadataFileService; + private readonly IExtraFileService _otherFileService; + + public ExtraFileModule(IExtraFileService subtitleFileService, IExtraFileService metadataFileService, IExtraFileService otherExtraFileService) + : base("/extrafile") + { + _subtitleFileService = subtitleFileService; + _metadataFileService = metadataFileService; + _otherFileService = otherExtraFileService; + GetResourceAll = GetFiles; + } + + private List GetFiles() + { + if (!Request.Query.MovieId.HasValue) + { + throw new BadRequestException("MovieId is missing"); + } + + var extraFiles = new List(); + + List subtitleFiles = _subtitleFileService.GetFilesByMovie(Request.Query.MovieId); + List metadataFiles = _metadataFileService.GetFilesByMovie(Request.Query.MovieId); + List otherExtraFiles = _otherFileService.GetFilesByMovie(Request.Query.MovieId); + + extraFiles.AddRange(subtitleFiles.ToResource()); + extraFiles.AddRange(metadataFiles.ToResource()); + extraFiles.AddRange(otherExtraFiles.ToResource()); + + return extraFiles; + } + } +} diff --git a/src/NzbDrone.Api/ExtraFiles/ExtraFileResource.cs b/src/NzbDrone.Api/ExtraFiles/ExtraFileResource.cs new file mode 100644 index 000000000..e4f19475b --- /dev/null +++ b/src/NzbDrone.Api/ExtraFiles/ExtraFileResource.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Api.REST; +using NzbDrone.Core.Extras.Files; +using NzbDrone.Core.Extras.Metadata.Files; +using NzbDrone.Core.Extras.Others; +using NzbDrone.Core.Extras.Subtitles; + +namespace NzbDrone.Api.ExtraFiles +{ + public class ExtraFileResource : RestResource + { + public int MovieId { get; set; } + public int? MovieFileId { get; set; } + public string RelativePath { get; set; } + public string Extension { get; set; } + public ExtraFileType Type { get; set; } + } + + public static class ExtraFileResourceMapper + { + public static ExtraFileResource ToResource(this MetadataFile model) + { + if (model == null) return null; + + return new ExtraFileResource + { + Id = model.Id, + MovieId = model.MovieId, + MovieFileId = model.MovieFileId, + RelativePath = model.RelativePath, + Extension = model.Extension, + Type = ExtraFileType.Metadata + }; + } + + public static ExtraFileResource ToResource(this SubtitleFile model) + { + if (model == null) return null; + + return new ExtraFileResource + { + Id = model.Id, + MovieId = model.MovieId, + MovieFileId = model.MovieFileId, + RelativePath = model.RelativePath, + Extension = model.Extension, + Type = ExtraFileType.Subtitle + }; + } + + public static ExtraFileResource ToResource(this OtherExtraFile model) + { + if (model == null) return null; + + return new ExtraFileResource + { + Id = model.Id, + MovieId = model.MovieId, + MovieFileId = model.MovieFileId, + RelativePath = model.RelativePath, + Extension = model.Extension, + Type = ExtraFileType.Other + }; + } + + public static List ToResource(this IEnumerable movies) + { + return movies.Select(ToResource).ToList(); + } + + public static List ToResource(this IEnumerable movies) + { + return movies.Select(ToResource).ToList(); + } + + public static List ToResource(this IEnumerable movies) + { + return movies.Select(ToResource).ToList(); + } + + } +} diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index 7f9dc925c..c2970ddfd 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -115,10 +115,12 @@ + + diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/DeleteBadMediaCovers.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/DeleteBadMediaCovers.cs index 5b454ae3c..cc4b3ae95 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/DeleteBadMediaCovers.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/DeleteBadMediaCovers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -20,27 +20,27 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks public class DeleteBadMediaCoversFixture : CoreTest { private List _metadata; - private List _series; + private List _movies; [SetUp] public void Setup() { - _series = Builder.CreateListOfSize(1) + _movies = Builder.CreateListOfSize(1) .All() - .With(c => c.Path = "C:\\TV\\".AsOsAgnostic()) + .With(c => c.Path = "C:\\Movie\\".AsOsAgnostic()) .Build().ToList(); _metadata = Builder.CreateListOfSize(1) .Build().ToList(); - Mocker.GetMock() - .Setup(c => c.GetAllSeries()) - .Returns(_series); + Mocker.GetMock() + .Setup(c => c.GetAllMovies()) + .Returns(_movies); Mocker.GetMock() - .Setup(c => c.GetFilesBySeries(_series.First().Id)) + .Setup(c => c.GetFilesByMovie(_movies.First().Id)) .Returns(_metadata); @@ -51,8 +51,8 @@ public void Setup() [Test] public void should_not_process_non_image_files() { - _metadata.First().RelativePath = "season\\file.xml".AsOsAgnostic(); - _metadata.First().Type = MetadataType.EpisodeMetadata; + _metadata.First().RelativePath = "extrafiles\\file.xml".AsOsAgnostic(); + _metadata.First().Type = MetadataType.MovieMetadata; Subject.Clean(); @@ -101,10 +101,10 @@ public void should_set_clean_flag_to_false() public void should_delete_html_images() { - var imagePath = "C:\\TV\\Season\\image.jpg".AsOsAgnostic(); + var imagePath = "C:\\Movie\\image.jpg".AsOsAgnostic(); _metadata.First().LastUpdated = new DateTime(2014, 12, 29); - _metadata.First().RelativePath = "Season\\image.jpg".AsOsAgnostic(); - _metadata.First().Type = MetadataType.SeriesImage; + _metadata.First().RelativePath = "image.jpg".AsOsAgnostic(); + _metadata.First().Type = MetadataType.MovieImage; Mocker.GetMock() .Setup(c => c.OpenReadStream(imagePath)) @@ -123,10 +123,10 @@ public void should_delete_html_images() public void should_delete_empty_images() { - var imagePath = "C:\\TV\\Season\\image.jpg".AsOsAgnostic(); + var imagePath = "C:\\Movie\\image.jpg".AsOsAgnostic(); _metadata.First().LastUpdated = new DateTime(2014, 12, 29); - _metadata.First().Type = MetadataType.SeasonImage; - _metadata.First().RelativePath = "Season\\image.jpg".AsOsAgnostic(); + _metadata.First().Type = MetadataType.MovieImage; + _metadata.First().RelativePath = "image.jpg".AsOsAgnostic(); Mocker.GetMock() .Setup(c => c.OpenReadStream(imagePath)) @@ -144,9 +144,9 @@ public void should_delete_empty_images() public void should_not_delete_non_html_files() { - var imagePath = "C:\\TV\\Season\\image.jpg".AsOsAgnostic(); + var imagePath = "C:\\Movie\\image.jpg".AsOsAgnostic(); _metadata.First().LastUpdated = new DateTime(2014, 12, 29); - _metadata.First().RelativePath = "Season\\image.jpg".AsOsAgnostic(); + _metadata.First().RelativePath = "image.jpg".AsOsAgnostic(); Mocker.GetMock() .Setup(c => c.OpenReadStream(imagePath)) diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupDuplicateMetadataFilesFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupDuplicateMetadataFilesFixture.cs index 5bfeaefc0..e0f4b8078 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupDuplicateMetadataFilesFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupDuplicateMetadataFilesFixture.cs @@ -1,4 +1,4 @@ -using FizzWare.NBuilder; +using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Extras.Metadata; @@ -12,12 +12,12 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers public class CleanupDuplicateMetadataFilesFixture : DbTest { [Test] - public void should_not_delete_metadata_files_when_they_are_for_the_same_series_but_different_consumers() + public void should_not_delete_metadata_files_when_they_are_for_the_same_movie_but_different_consumers() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.SeriesMetadata) - .With(m => m.SeriesId = 1) + .With(m => m.Type = MetadataType.MovieMetadata) + .With(m => m.MovieId = 1) .BuildListOfNew(); Db.InsertMany(files); @@ -26,11 +26,11 @@ public void should_not_delete_metadata_files_when_they_are_for_the_same_series_b } [Test] - public void should_not_delete_metadata_files_for_different_series() + public void should_not_delete_metadata_files_for_different_movie() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.SeriesMetadata) + .With(m => m.Type = MetadataType.MovieMetadata) .With(m => m.Consumer = "XbmcMetadata") .BuildListOfNew(); @@ -40,12 +40,12 @@ public void should_not_delete_metadata_files_for_different_series() } [Test] - public void should_delete_metadata_files_when_they_are_for_the_same_series_and_consumer() + public void should_delete_metadata_files_when_they_are_for_the_same_movie_and_consumer() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.SeriesMetadata) - .With(m => m.SeriesId = 1) + .With(m => m.Type = MetadataType.MovieMetadata) + .With(m => m.MovieId = 1) .With(m => m.Consumer = "XbmcMetadata") .BuildListOfNew(); @@ -55,7 +55,7 @@ public void should_delete_metadata_files_when_they_are_for_the_same_series_and_c } [Test] - public void should_not_delete_metadata_files_when_there_is_only_one_for_that_series_and_consumer() + public void should_not_delete_metadata_files_when_there_is_only_one_for_that_movie_and_consumer() { var file = Builder.CreateNew() .BuildNew(); @@ -66,12 +66,12 @@ public void should_not_delete_metadata_files_when_there_is_only_one_for_that_ser } [Test] - public void should_not_delete_metadata_files_when_they_are_for_the_same_episode_but_different_consumers() + public void should_not_delete_metadata_files_when_they_are_for_the_same_movie_file_but_different_consumers() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.EpisodeMetadata) - .With(m => m.EpisodeFileId = 1) + .With(m => m.Type = MetadataType.MovieMetadata) + .With(m => m.MovieFileId = 1) .BuildListOfNew(); Db.InsertMany(files); @@ -80,11 +80,11 @@ public void should_not_delete_metadata_files_when_they_are_for_the_same_episode_ } [Test] - public void should_not_delete_metadata_files_for_different_episode() + public void should_not_delete_metadata_files_for_different_movie_file() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.EpisodeMetadata) + .With(m => m.Type = MetadataType.MovieMetadata) .With(m => m.Consumer = "XbmcMetadata") .BuildListOfNew(); @@ -94,12 +94,12 @@ public void should_not_delete_metadata_files_for_different_episode() } [Test] - public void should_delete_metadata_files_when_they_are_for_the_same_episode_and_consumer() + public void should_delete_metadata_files_when_they_are_for_the_same_movie_file_and_consumer() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.EpisodeMetadata) - .With(m => m.EpisodeFileId = 1) + .With(m => m.Type = MetadataType.MovieMetadata) + .With(m => m.MovieFileId = 1) .With(m => m.Consumer = "XbmcMetadata") .BuildListOfNew(); @@ -109,7 +109,7 @@ public void should_delete_metadata_files_when_they_are_for_the_same_episode_and_ } [Test] - public void should_not_delete_metadata_files_when_there_is_only_one_for_that_episode_and_consumer() + public void should_not_delete_metadata_files_when_there_is_only_one_for_that_movie_file_and_consumer() { var file = Builder.CreateNew() .BuildNew(); @@ -120,12 +120,12 @@ public void should_not_delete_metadata_files_when_there_is_only_one_for_that_epi } [Test] - public void should_not_delete_image_when_they_are_for_the_same_episode_but_different_consumers() + public void should_not_delete_image_when_they_are_for_the_same_movie_file_but_different_consumers() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.EpisodeImage) - .With(m => m.EpisodeFileId = 1) + .With(m => m.Type = MetadataType.MovieImage) + .With(m => m.MovieFileId = 1) .BuildListOfNew(); Db.InsertMany(files); @@ -134,11 +134,11 @@ public void should_not_delete_image_when_they_are_for_the_same_episode_but_diffe } [Test] - public void should_not_delete_image_for_different_episode() + public void should_not_delete_image_for_different_movie_file() { var files = Builder.CreateListOfSize(2) .All() - .With(m => m.Type = MetadataType.EpisodeImage) + .With(m => m.Type = MetadataType.MovieImage) .With(m => m.Consumer = "XbmcMetadata") .BuildListOfNew(); @@ -148,22 +148,7 @@ public void should_not_delete_image_for_different_episode() } [Test] - public void should_delete_image_when_they_are_for_the_same_episode_and_consumer() - { - var files = Builder.CreateListOfSize(2) - .All() - .With(m => m.Type = MetadataType.EpisodeImage) - .With(m => m.EpisodeFileId = 1) - .With(m => m.Consumer = "XbmcMetadata") - .BuildListOfNew(); - - Db.InsertMany(files); - Subject.Clean(); - AllStoredModels.Count.Should().Be(1); - } - - [Test] - public void should_not_delete_image_when_there_is_only_one_for_that_episode_and_consumer() + public void should_not_delete_image_when_there_is_only_one_for_that_movie_file_and_consumer() { var file = Builder.CreateNew() .BuildNew(); diff --git a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs index 27679d8d3..468e6b58a 100644 --- a/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs +++ b/src/NzbDrone.Core.Test/Housekeeping/Housekeepers/CleanupOrphanedMetadataFilesFixture.cs @@ -1,4 +1,4 @@ -using FizzWare.NBuilder; +using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Extras.Metadata; @@ -15,10 +15,10 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers public class CleanupOrphanedMetadataFilesFixture : DbTest { [Test] - public void should_delete_metadata_files_that_dont_have_a_coresponding_series() + public void should_delete_metadata_files_that_dont_have_a_coresponding_movie() { var metadataFile = Builder.CreateNew() - .With(m => m.EpisodeFileId = null) + .With(m => m.MovieFileId = null) .BuildNew(); Db.Insert(metadataFile); @@ -27,16 +27,16 @@ public void should_delete_metadata_files_that_dont_have_a_coresponding_series() } [Test] - public void should_not_delete_metadata_files_that_have_a_coresponding_series() + public void should_not_delete_metadata_files_that_have_a_coresponding_movie() { - var series = Builder.CreateNew() + var movie = Builder.CreateNew() .BuildNew(); - Db.Insert(series); + Db.Insert(movie); var metadataFile = Builder.CreateNew() - .With(m => m.SeriesId = series.Id) - .With(m => m.EpisodeFileId = null) + .With(m => m.MovieId = movie.Id) + .With(m => m.MovieFileId = null) .BuildNew(); Db.Insert(metadataFile); @@ -45,16 +45,16 @@ public void should_not_delete_metadata_files_that_have_a_coresponding_series() } [Test] - public void should_delete_metadata_files_that_dont_have_a_coresponding_episode_file() + public void should_delete_metadata_files_that_dont_have_a_coresponding_movie_file() { - var series = Builder.CreateNew() + var movie = Builder.CreateNew() .BuildNew(); - Db.Insert(series); + Db.Insert(movie); var metadataFile = Builder.CreateNew() - .With(m => m.SeriesId = series.Id) - .With(m => m.EpisodeFileId = 10) + .With(m => m.MovieId = movie.Id) + .With(m => m.MovieFileId = 10) .BuildNew(); Db.Insert(metadataFile); @@ -63,21 +63,21 @@ public void should_delete_metadata_files_that_dont_have_a_coresponding_episode_f } [Test] - public void should_not_delete_metadata_files_that_have_a_coresponding_episode_file() + public void should_not_delete_metadata_files_that_have_a_coresponding_movie_file() { - var series = Builder.CreateNew() + var movie = Builder.CreateNew() .BuildNew(); - var episodeFile = Builder.CreateNew() + var movieFile = Builder.CreateNew() .With(h => h.Quality = new QualityModel()) .BuildNew(); - Db.Insert(series); - Db.Insert(episodeFile); + Db.Insert(movie); + Db.Insert(movieFile); var metadataFile = Builder.CreateNew() - .With(m => m.SeriesId = series.Id) - .With(m => m.EpisodeFileId = episodeFile.Id) + .With(m => m.MovieId = movie.Id) + .With(m => m.MovieFileId = movieFile.Id) .BuildNew(); Db.Insert(metadataFile); @@ -86,17 +86,17 @@ public void should_not_delete_metadata_files_that_have_a_coresponding_episode_fi } [Test] - public void should_delete_episode_metadata_files_that_have_episodefileid_of_zero() + public void should_delete_movie_metadata_files_that_have_moviefileid_of_zero() { - var series = Builder.CreateNew() + var movie = Builder.CreateNew() .BuildNew(); - Db.Insert(series); + Db.Insert(movie); var metadataFile = Builder.CreateNew() - .With(m => m.SeriesId = series.Id) - .With(m => m.Type = MetadataType.EpisodeMetadata) - .With(m => m.EpisodeFileId = 0) + .With(m => m.MovieId = movie.Id) + .With(m => m.Type = MetadataType.MovieMetadata) + .With(m => m.MovieFileId = 0) .BuildNew(); Db.Insert(metadataFile); @@ -105,17 +105,17 @@ public void should_delete_episode_metadata_files_that_have_episodefileid_of_zero } [Test] - public void should_delete_episode_image_files_that_have_episodefileid_of_zero() + public void should_delete_movie_image_files_that_have_moviefileid_of_zero() { - var series = Builder.CreateNew() + var movie = Builder.CreateNew() .BuildNew(); - Db.Insert(series); + Db.Insert(movie); var metadataFile = Builder.CreateNew() - .With(m => m.SeriesId = series.Id) - .With(m => m.Type = MetadataType.EpisodeImage) - .With(m => m.EpisodeFileId = 0) + .With(m => m.MovieId = movie.Id) + .With(m => m.Type = MetadataType.MovieImage) + .With(m => m.MovieFileId = 0) .BuildNew(); Db.Insert(metadataFile); diff --git a/src/NzbDrone.Core.Test/Metadata/Consumers/Roksbox/FindMetadataFileFixture.cs b/src/NzbDrone.Core.Test/Metadata/Consumers/Roksbox/FindMetadataFileFixture.cs index 6d4328b32..99fa8549f 100644 --- a/src/NzbDrone.Core.Test/Metadata/Consumers/Roksbox/FindMetadataFileFixture.cs +++ b/src/NzbDrone.Core.Test/Metadata/Consumers/Roksbox/FindMetadataFileFixture.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; @@ -13,66 +13,56 @@ namespace NzbDrone.Core.Test.Metadata.Consumers.Roksbox [TestFixture] public class FindMetadataFileFixture : CoreTest { - private Series _series; + private Movie _movie; [SetUp] public void Setup() { - _series = Builder.CreateNew() - .With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic()) + _movie = Builder.CreateNew() + .With(s => s.Path = @"C:\Test\Movies\The.Movie.2011".AsOsAgnostic()) .Build(); } [Test] public void should_return_null_if_filename_is_not_handled() { - var path = Path.Combine(_series.Path, "file.jpg"); + var path = Path.Combine(_movie.Path, "file.jpg"); - Subject.FindMetadataFile(_series, path).Should().BeNull(); + Subject.FindMetadataFile(_movie, path).Should().BeNull(); } - [TestCase("Specials")] - [TestCase("specials")] - [TestCase("Season 1")] - public void should_return_season_image(string folder) + [TestCase(".xml", MetadataType.MovieMetadata)] + [TestCase(".jpg", MetadataType.MovieImage)] + public void should_return_metadata_for_movie_if_valid_file_for_movie(string extension, MetadataType type) { - var path = Path.Combine(_series.Path, folder, folder + ".jpg"); + var path = Path.Combine(_movie.Path, "the.movie.2011" + extension); - Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeasonImage); - } - - [TestCase(".xml", MetadataType.EpisodeMetadata)] - [TestCase(".jpg", MetadataType.EpisodeImage)] - public void should_return_metadata_for_episode_if_valid_file_for_episode(string extension, MetadataType type) - { - var path = Path.Combine(_series.Path, "the.series.s01e01.episode" + extension); - - Subject.FindMetadataFile(_series, path).Type.Should().Be(type); + Subject.FindMetadataFile(_movie, path).Type.Should().Be(type); } [TestCase(".xml")] [TestCase(".jpg")] - public void should_return_null_if_not_valid_file_for_episode(string extension) + public void should_return_null_if_not_valid_file_for_movie(string extension) { - var path = Path.Combine(_series.Path, "the.series.episode" + extension); + var path = Path.Combine(_movie.Path, "the.movie.here" + extension); - Subject.FindMetadataFile(_series, path).Should().BeNull(); + Subject.FindMetadataFile(_movie, path).Should().BeNull(); } [Test] public void should_not_return_metadata_if_image_file_is_a_thumb() { - var path = Path.Combine(_series.Path, "the.series.s01e01.episode-thumb.jpg"); + var path = Path.Combine(_movie.Path, "the.movie.2011-thumb.jpg"); - Subject.FindMetadataFile(_series, path).Should().BeNull(); + Subject.FindMetadataFile(_movie, path).Should().BeNull(); } [Test] - public void should_return_series_image_for_folder_jpg_in_series_folder() + public void should_return_movie_image_for_folder_jpg_in_movie_folder() { - var path = Path.Combine(_series.Path, new DirectoryInfo(_series.Path).Name + ".jpg"); + var path = Path.Combine(_movie.Path, new DirectoryInfo(_movie.Path).Name + ".jpg"); - Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeriesImage); + Subject.FindMetadataFile(_movie, path).Type.Should().Be(MetadataType.MovieImage); } } } diff --git a/src/NzbDrone.Core.Test/Metadata/Consumers/Wdtv/FindMetadataFileFixture.cs b/src/NzbDrone.Core.Test/Metadata/Consumers/Wdtv/FindMetadataFileFixture.cs index 078744ec8..9f73f68a0 100644 --- a/src/NzbDrone.Core.Test/Metadata/Consumers/Wdtv/FindMetadataFileFixture.cs +++ b/src/NzbDrone.Core.Test/Metadata/Consumers/Wdtv/FindMetadataFileFixture.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; @@ -13,58 +13,48 @@ namespace NzbDrone.Core.Test.Metadata.Consumers.Wdtv [TestFixture] public class FindMetadataFileFixture : CoreTest { - private Series _series; + private Movie _movie; [SetUp] public void Setup() { - _series = Builder.CreateNew() - .With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic()) + _movie = Builder.CreateNew() + .With(s => s.Path = @"C:\Test\Movies\The.Movie".AsOsAgnostic()) .Build(); } [Test] public void should_return_null_if_filename_is_not_handled() { - var path = Path.Combine(_series.Path, "file.jpg"); + var path = Path.Combine(_movie.Path, "file.jpg"); - Subject.FindMetadataFile(_series, path).Should().BeNull(); + Subject.FindMetadataFile(_movie, path).Should().BeNull(); } - [TestCase("Specials")] - [TestCase("specials")] - [TestCase("Season 1")] - public void should_return_season_image(string folder) + [TestCase(".xml", MetadataType.MovieMetadata)] + [TestCase(".metathumb", MetadataType.MovieImage)] + public void should_return_metadata_for_movie_if_valid_file_for_movie(string extension, MetadataType type) { - var path = Path.Combine(_series.Path, folder, "folder.jpg"); + var path = Path.Combine(_movie.Path, "the.movie.2011" + extension); - Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeasonImage); - } - - [TestCase(".xml", MetadataType.EpisodeMetadata)] - [TestCase(".metathumb", MetadataType.EpisodeImage)] - public void should_return_metadata_for_episode_if_valid_file_for_episode(string extension, MetadataType type) - { - var path = Path.Combine(_series.Path, "the.series.s01e01.episode" + extension); - - Subject.FindMetadataFile(_series, path).Type.Should().Be(type); + Subject.FindMetadataFile(_movie, path).Type.Should().Be(type); } [TestCase(".xml")] [TestCase(".metathumb")] - public void should_return_null_if_not_valid_file_for_episode(string extension) + public void should_return_null_if_not_valid_file_for_movie(string extension) { - var path = Path.Combine(_series.Path, "the.series.episode" + extension); + var path = Path.Combine(_movie.Path, "the.movie" + extension); - Subject.FindMetadataFile(_series, path).Should().BeNull(); + Subject.FindMetadataFile(_movie, path).Should().BeNull(); } [Test] - public void should_return_series_image_for_folder_jpg_in_series_folder() + public void should_return_movie_image_for_folder_jpg_in_movie_folder() { - var path = Path.Combine(_series.Path, "folder.jpg"); + var path = Path.Combine(_movie.Path, "folder.jpg"); - Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.SeriesImage); + Subject.FindMetadataFile(_movie, path).Type.Should().Be(MetadataType.MovieImage); } } } diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index ad7e3430a..06344c73f 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using NLog; @@ -294,9 +294,16 @@ public bool EnableMediaInfo set { SetValue("EnableMediaInfo", value); } } + public bool ImportExtraFiles + { + get { return GetValueBoolean("ImportExtraFiles", false); } + + set { SetValue("ImportExtraFiles", value); } + } + public string ExtraFileExtensions { - get { return GetValue("ExtraFileExtensions", ""); } + get { return GetValue("ExtraFileExtensions", "srt"); } set { SetValue("ExtraFileExtensions", value); } } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index 4292c0f89..3e0b67e56 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.MediaFiles; using NzbDrone.Common.Http.Proxy; using NzbDrone.Core.Parser; @@ -33,6 +33,7 @@ public interface IConfigService bool SkipFreeSpaceCheckWhenImporting { get; set; } bool CopyUsingHardlinks { get; set; } bool EnableMediaInfo { get; set; } + bool ImportExtraFiles { get; set; } string ExtraFileExtensions { get; set; } bool AutoRenameFolders { get; set; } bool PathsDefaultStatic { get; set; } diff --git a/src/NzbDrone.Core/Datastore/Migration/142_movie_extras.cs b/src/NzbDrone.Core/Datastore/Migration/142_movie_extras.cs new file mode 100644 index 000000000..709aea5f2 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/142_movie_extras.cs @@ -0,0 +1,44 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(142)] + public class movie_extras : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Delete.Table("ExtraFiles"); + Delete.Table("SubtitleFiles"); + Delete.Table("MetadataFiles"); + + Create.TableForModel("ExtraFiles") + .WithColumn("MovieId").AsInt32().NotNullable() + .WithColumn("MovieFileId").AsInt32().NotNullable() + .WithColumn("RelativePath").AsString().NotNullable() + .WithColumn("Extension").AsString().NotNullable() + .WithColumn("Added").AsDateTime().NotNullable() + .WithColumn("LastUpdated").AsDateTime().NotNullable(); + + Create.TableForModel("SubtitleFiles") + .WithColumn("MovieId").AsInt32().NotNullable() + .WithColumn("MovieFileId").AsInt32().NotNullable() + .WithColumn("RelativePath").AsString().NotNullable() + .WithColumn("Extension").AsString().NotNullable() + .WithColumn("Added").AsDateTime().NotNullable() + .WithColumn("LastUpdated").AsDateTime().NotNullable() + .WithColumn("Language").AsInt32().NotNullable(); + + Create.TableForModel("MetadataFiles") + .WithColumn("MovieId").AsInt32().NotNullable() + .WithColumn("Consumer").AsString().NotNullable() + .WithColumn("Type").AsInt32().NotNullable() + .WithColumn("RelativePath").AsString().NotNullable() + .WithColumn("LastUpdated").AsDateTime().NotNullable() + .WithColumn("MovieFileId").AsInt32().Nullable() + .WithColumn("Hash").AsString().Nullable() + .WithColumn("Added").AsDateTime().Nullable() + .WithColumn("Extension").AsString().NotNullable(); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 5874e4ba3..fe9333a4e 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -36,45 +36,7 @@ using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.NetImport; using NzbDrone.Core.NetImport.ImportExclusions; -using System; -using System.Collections.Generic; -using Marr.Data; -using Marr.Data.Mapping; -using NzbDrone.Common.Reflection; -using NzbDrone.Core.Blacklisting; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.DataAugmentation.Scene; -using NzbDrone.Core.Datastore.Converters; -using NzbDrone.Core.Datastore.Extensions; -using NzbDrone.Core.Download; -using NzbDrone.Core.Download.Pending; -using NzbDrone.Core.Indexers; -using NzbDrone.Core.Instrumentation; -using NzbDrone.Core.Jobs; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Profiles.Delay; -using NzbDrone.Core.RemotePathMappings; -using NzbDrone.Core.Notifications; -using NzbDrone.Core.Organizer; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Profiles; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Restrictions; -using NzbDrone.Core.RootFolders; -using NzbDrone.Core.SeriesStats; -using NzbDrone.Core.Tags; -using NzbDrone.Core.ThingiProvider; -using NzbDrone.Core.Tv; -using NzbDrone.Common.Disk; -using NzbDrone.Core.Authentication; -using NzbDrone.Core.Extras.Metadata; -using NzbDrone.Core.Extras.Metadata.Files; -using NzbDrone.Core.Extras.Others; -using NzbDrone.Core.Extras.Subtitles; -using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Movies.AlternativeTitles; -using NzbDrone.Core.NetImport; -using NzbDrone.Core.NetImport.ImportExclusions; namespace NzbDrone.Core.Datastore { @@ -252,4 +214,4 @@ private static void RegisterEmbeddedConverter() } } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Extras/ExistingExtraFileService.cs b/src/NzbDrone.Core/Extras/ExistingExtraFileService.cs index f2646d67e..4bfeea61e 100644 --- a/src/NzbDrone.Core/Extras/ExistingExtraFileService.cs +++ b/src/NzbDrone.Core/Extras/ExistingExtraFileService.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; @@ -10,7 +10,7 @@ namespace NzbDrone.Core.Extras { - public class ExistingExtraFileService : IHandle + public class ExistingExtraFileService : IHandle { private readonly IDiskProvider _diskProvider; private readonly IDiskScanService _diskScanService; @@ -28,29 +28,29 @@ public ExistingExtraFileService(IDiskProvider diskProvider, _logger = logger; } - public void Handle(SeriesScannedEvent message) + public void Handle(MovieScannedEvent message) { - var series = message.Series; + var movie = message.Movie; var extraFiles = new List(); - if (!_diskProvider.FolderExists(series.Path)) + if (!_diskProvider.FolderExists(movie.Path)) { return; } - _logger.Debug("Looking for existing extra files in {0}", series.Path); + _logger.Debug("Looking for existing extra files in {0}", movie.Path); - var filesOnDisk = _diskScanService.GetNonVideoFiles(series.Path); - var possibleExtraFiles = _diskScanService.FilterFiles(series, filesOnDisk); + var filesOnDisk = _diskScanService.GetNonVideoFiles(movie.Path); + var possibleExtraFiles = _diskScanService.FilterFiles(movie, filesOnDisk); var filteredFiles = possibleExtraFiles; var importedFiles = new List(); foreach (var existingExtraFileImporter in _existingExtraFileImporters) { - var imported = existingExtraFileImporter.ProcessFiles(series, filteredFiles, importedFiles); + var imported = existingExtraFileImporter.ProcessFiles(movie, filteredFiles, importedFiles); - importedFiles.AddRange(imported.Select(f => Path.Combine(series.Path, f.RelativePath))); + importedFiles.AddRange(imported.Select(f => Path.Combine(movie.Path, f.RelativePath))); } _logger.Info("Found {0} extra files", extraFiles.Count); diff --git a/src/NzbDrone.Core/Extras/ExtraService.cs b/src/NzbDrone.Core/Extras/ExtraService.cs index 3b3a1f7c7..d45ec6b52 100644 --- a/src/NzbDrone.Core/Extras/ExtraService.cs +++ b/src/NzbDrone.Core/Extras/ExtraService.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Marr.Data; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; @@ -18,50 +19,50 @@ namespace NzbDrone.Core.Extras { public interface IExtraService { - void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly); + void ImportExtraFiles(LocalMovie localMovie, MovieFile movieFile, bool isReadOnly); } public class ExtraService : IExtraService, IHandle, - IHandle, - IHandle + IHandle { private readonly IMediaFileService _mediaFileService; - private readonly IEpisodeService _episodeService; + private readonly IMovieService _movieService; private readonly IDiskProvider _diskProvider; private readonly IConfigService _configService; private readonly List _extraFileManagers; private readonly Logger _logger; public ExtraService(IMediaFileService mediaFileService, - IEpisodeService episodeService, + IMovieService movieService, IDiskProvider diskProvider, IConfigService configService, List extraFileManagers, Logger logger) { _mediaFileService = mediaFileService; - _episodeService = episodeService; + _movieService = movieService; _diskProvider = diskProvider; _configService = configService; _extraFileManagers = extraFileManagers.OrderBy(e => e.Order).ToList(); _logger = logger; } - public void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, bool isReadOnly) + public void ImportExtraFiles(LocalMovie localMovie, MovieFile movieFile, bool isReadOnly) { - var series = localEpisode.Series; + var movie = localMovie.Movie; foreach (var extraFileManager in _extraFileManagers) { - extraFileManager.CreateAfterEpisodeImport(series, episodeFile); + extraFileManager.CreateAfterMovieImport(movie, movieFile); } - // TODO: Remove - // Not importing files yet, testing that parsing is working properly first - return; + if (!_configService.ImportExtraFiles) + { + return; + } - var sourcePath = localEpisode.Path; + var sourcePath = localMovie.Path; var sourceFolder = _diskProvider.GetParentFolder(sourcePath); var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath); var files = _diskProvider.GetFiles(sourceFolder, SearchOption.TopDirectoryOnly); @@ -70,7 +71,7 @@ public void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, .Select(e => e.Trim(' ', '.')) .ToList(); - var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName)); + var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)); foreach (var matchingFilename in matchingFilenames) { @@ -85,7 +86,8 @@ public void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, { foreach (var extraFileManager in _extraFileManagers) { - var extraFile = extraFileManager.Import(series, episodeFile, matchingFilename, matchingExtension, isReadOnly); + var extension = Path.GetExtension(matchingFilename); + var extraFile = extraFileManager.Import(movie, movieFile, matchingFilename, extension, isReadOnly); if (extraFile != null) { @@ -102,60 +104,36 @@ public void ImportExtraFiles(LocalEpisode localEpisode, EpisodeFile episodeFile, public void Handle(MediaCoversUpdatedEvent message) { - //var series = message.Series; - //var episodeFiles = GetEpisodeFiles(series.Id); - - //foreach (var extraFileManager in _extraFileManagers) - //{ - // extraFileManager.CreateAfterSeriesScan(series, episodeFiles); - //} - } - - //TODO: Implementing this will fix a lot of our warning exceptions - //public void Handle(MediaCoversUpdatedEvent message) - //{ - // var movie = message.Movie; - // var movieFiles = GetMovieFiles(movie.Id); - - // foreach (var extraFileManager in _extraFileManagers) - // { - // extraFileManager.CreateAfterMovieScan(movie, movieFiles); - // } - //} - - public void Handle(EpisodeFolderCreatedEvent message) - { - var series = message.Series; + var movie = message.Movie; + var movieFiles = GetMovieFiles(movie.Id); foreach (var extraFileManager in _extraFileManagers) { - extraFileManager.CreateAfterEpisodeImport(series, message.SeriesFolder, message.SeasonFolder); + extraFileManager.CreateAfterMovieScan(movie, movieFiles); } } - public void Handle(SeriesRenamedEvent message) + public void Handle(MovieRenamedEvent message) { - var series = message.Series; - var episodeFiles = GetEpisodeFiles(series.Id); + var movie = message.Movie; + var movieFiles = GetMovieFiles(movie.Id); foreach (var extraFileManager in _extraFileManagers) { - extraFileManager.MoveFilesAfterRename(series, episodeFiles); + extraFileManager.MoveFilesAfterRename(movie, movieFiles); } } - private List GetEpisodeFiles(int seriesId) + private List GetMovieFiles(int movieId) { - var episodeFiles = _mediaFileService.GetFilesBySeries(seriesId); - var episodes = _episodeService.GetEpisodeBySeries(seriesId); + var movieFiles = _mediaFileService.GetFilesByMovie(movieId); - foreach (var episodeFile in episodeFiles) + foreach (var movieFile in movieFiles) { - var localEpisodeFile = episodeFile; - episodeFile.Episodes = new LazyList(episodes.Where(e => e.EpisodeFileId == localEpisodeFile.Id)); + movieFile.Movie = new LazyLoaded(_movieService.GetMovie(movieId)); } - return episodeFiles; + return movieFiles; } } } diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFile.cs b/src/NzbDrone.Core/Extras/Files/ExtraFile.cs index 036eaec33..c13b0a881 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFile.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFile.cs @@ -1,16 +1,22 @@ -using System; +using System; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Extras.Files { public abstract class ExtraFile : ModelBase { - public int SeriesId { get; set; } - public int? EpisodeFileId { get; set; } - public int? SeasonNumber { get; set; } + public int MovieId { get; set; } + public int? MovieFileId { get; set; } public string RelativePath { get; set; } public DateTime Added { get; set; } public DateTime LastUpdated { get; set; } public string Extension { get; set; } } + + public enum ExtraFileType + { + Subtitle = 0, + Metadata = 1, + Other = 2 + } } diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileManager.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileManager.cs index 3b48f1cb1..13b2dd222 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileManager.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileManager.cs @@ -1,5 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; +using System.Text; +using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; @@ -11,11 +14,10 @@ namespace NzbDrone.Core.Extras.Files public interface IManageExtraFiles { int Order { get; } - IEnumerable CreateAfterSeriesScan(Series series, List episodeFiles); - IEnumerable CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile); - IEnumerable CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder); - IEnumerable MoveFilesAfterRename(Series series, List episodeFiles); - ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly); + IEnumerable CreateAfterMovieScan(Movie movie, List movieFiles); + IEnumerable CreateAfterMovieImport(Movie movie, MovieFile movieFile); + IEnumerable MoveFilesAfterRename(Movie movie, List movieFiles); + ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly); } public abstract class ExtraFileManager : IManageExtraFiles @@ -23,29 +25,40 @@ public abstract class ExtraFileManager : IManageExtraFiles { private readonly IConfigService _configService; + private readonly IDiskProvider _diskProvider; private readonly IDiskTransferService _diskTransferService; - private readonly IExtraFileService _extraFileService; + private readonly Logger _logger; public ExtraFileManager(IConfigService configService, + IDiskProvider diskProvider, IDiskTransferService diskTransferService, - IExtraFileService extraFileService) + Logger logger) { _configService = configService; + _diskProvider = diskProvider; _diskTransferService = diskTransferService; - _extraFileService = extraFileService; + _logger = logger; } public abstract int Order { get; } - public abstract IEnumerable CreateAfterSeriesScan(Series series, List episodeFiles); - public abstract IEnumerable CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile); - public abstract IEnumerable CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder); - public abstract IEnumerable MoveFilesAfterRename(Series series, List episodeFiles); - public abstract ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly); + public abstract IEnumerable CreateAfterMovieScan(Movie movie, List movieFiles); + public abstract IEnumerable CreateAfterMovieImport(Movie movie, MovieFile movieFile); + public abstract IEnumerable MoveFilesAfterRename(Movie movie, List movieFiles); + public abstract ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly); - protected TExtraFile ImportFile(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly) + protected TExtraFile ImportFile(Movie movie, MovieFile movieFile, string path, bool readOnly, string extension, string fileNameSuffix = null) { - var newFileName = Path.Combine(series.Path, Path.ChangeExtension(episodeFile.RelativePath, extension)); + var newFolder = Path.GetDirectoryName(Path.Combine(movie.Path, movieFile.RelativePath)); + var filenameBuilder = new StringBuilder(Path.GetFileNameWithoutExtension(movieFile.RelativePath)); + if (fileNameSuffix.IsNotNullOrWhiteSpace()) + { + filenameBuilder.Append(fileNameSuffix); + } + + filenameBuilder.Append(extension); + + var newFileName = Path.Combine(newFolder, filenameBuilder.ToString()); var transferMode = TransferMode.Move; if (readOnly) @@ -57,12 +70,45 @@ protected TExtraFile ImportFile(Series series, EpisodeFile episodeFile, string p return new TExtraFile { - SeriesId = series.Id, - SeasonNumber = episodeFile.SeasonNumber, - EpisodeFileId = episodeFile.Id, - RelativePath = series.Path.GetRelativePath(newFileName), - Extension = Path.GetExtension(path) + MovieId = movie.Id, + MovieFileId = movieFile.Id, + RelativePath = movie.Path.GetRelativePath(newFileName), + Extension = extension }; } + + protected TExtraFile MoveFile(Movie movie, MovieFile movieFile, TExtraFile extraFile, string fileNameSuffix = null) + { + var newFolder = Path.GetDirectoryName(Path.Combine(movie.Path, movieFile.RelativePath)); + var filenameBuilder = new StringBuilder(Path.GetFileNameWithoutExtension(movieFile.RelativePath)); + + if (fileNameSuffix.IsNotNullOrWhiteSpace()) + { + filenameBuilder.Append(fileNameSuffix); + } + + filenameBuilder.Append(extraFile.Extension); + + var existingFileName = Path.Combine(movie.Path, extraFile.RelativePath); + var newFileName = Path.Combine(newFolder, filenameBuilder.ToString()); + + if (newFileName.PathNotEquals(existingFileName)) + { + try + { + _diskProvider.MoveFile(existingFileName, newFileName); + extraFile.RelativePath = movie.Path.GetRelativePath(newFileName); + + return extraFile; + } + catch (Exception ex) + { + _logger.Warn(ex, "Unable to move file after rename: {0}", existingFileName); + } + } + + return null; + } } } + diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs index 7cb4644c3..57a66e016 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileRepository.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; @@ -7,12 +7,10 @@ namespace NzbDrone.Core.Extras.Files { public interface IExtraFileRepository : IBasicRepository where TExtraFile : ExtraFile, new() { - void DeleteForSeries(int seriesId); - void DeleteForSeason(int seriesId, int seasonNumber); - void DeleteForEpisodeFile(int episodeFileId); - List GetFilesBySeries(int seriesId); - List GetFilesBySeason(int seriesId, int seasonNumber); - List GetFilesByEpisodeFile(int episodeFileId); + void DeleteForMovie(int movieId); + void DeleteForMovieFile(int movieFileId); + List GetFilesByMovie(int movieId); + List GetFilesByMovieFile(int movieFileId); TExtraFile FindByPath(string path); } @@ -24,34 +22,24 @@ public ExtraFileRepository(IMainDatabase database, IEventAggregator eventAggrega { } - public void DeleteForSeries(int seriesId) + public void DeleteForMovie(int movieId) { - Delete(c => c.SeriesId == seriesId); + Delete(c => c.MovieId == movieId); } - public void DeleteForSeason(int seriesId, int seasonNumber) + public void DeleteForMovieFile(int movieFileId) { - Delete(c => c.SeriesId == seriesId && c.SeasonNumber == seasonNumber); + Delete(c => c.MovieFileId == movieFileId); } - public void DeleteForEpisodeFile(int episodeFileId) + public List GetFilesByMovie(int movieId) { - Delete(c => c.EpisodeFileId == episodeFileId); + return Query.Where(c => c.MovieId == movieId); } - public List GetFilesBySeries(int seriesId) + public List GetFilesByMovieFile(int movieFileId) { - return Query.Where(c => c.SeriesId == seriesId); - } - - public List GetFilesBySeason(int seriesId, int seasonNumber) - { - return Query.Where(c => c.SeriesId == seriesId && c.SeasonNumber == seasonNumber); - } - - public List GetFilesByEpisodeFile(int episodeFileId) - { - return Query.Where(c => c.EpisodeFileId == episodeFileId); + return Query.Where(c => c.MovieFileId == movieFileId); } public TExtraFile FindByPath(string path) diff --git a/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs b/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs index 54d86e908..ad314b1f1 100644 --- a/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs +++ b/src/NzbDrone.Core/Extras/Files/ExtraFileService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,8 +15,8 @@ namespace NzbDrone.Core.Extras.Files public interface IExtraFileService where TExtraFile : ExtraFile, new() { - List GetFilesBySeries(int seriesId); - List GetFilesByEpisodeFile(int episodeFileId); + List GetFilesByMovie(int movieId); + List GetFilesByMovieFile(int movieFileId); TExtraFile FindByPath(string path); void Upsert(TExtraFile extraFile); void Upsert(List extraFiles); @@ -25,24 +25,24 @@ public interface IExtraFileService } public abstract class ExtraFileService : IExtraFileService, - IHandleAsync, - IHandleAsync + IHandleAsync, + IHandleAsync where TExtraFile : ExtraFile, new() { private readonly IExtraFileRepository _repository; - private readonly ISeriesService _seriesService; + private readonly IMovieService _movieService; private readonly IDiskProvider _diskProvider; private readonly IRecycleBinProvider _recycleBinProvider; private readonly Logger _logger; public ExtraFileService(IExtraFileRepository repository, - ISeriesService seriesService, + IMovieService movieService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger) { _repository = repository; - _seriesService = seriesService; + _movieService = movieService; _diskProvider = diskProvider; _recycleBinProvider = recycleBinProvider; _logger = logger; @@ -50,14 +50,14 @@ public ExtraFileService(IExtraFileRepository repository, public virtual bool PermanentlyDelete => false; - public List GetFilesBySeries(int seriesId) + public List GetFilesByMovie(int movieId) { - return _repository.GetFilesBySeries(seriesId); + return _repository.GetFilesByMovie(movieId); } - public List GetFilesByEpisodeFile(int episodeFileId) + public List GetFilesByMovieFile(int movieFileId) { - return _repository.GetFilesByEpisodeFile(episodeFileId); + return _repository.GetFilesByMovieFile(movieFileId); } public TExtraFile FindByPath(string path) @@ -96,28 +96,28 @@ public void DeleteMany(IEnumerable ids) _repository.DeleteMany(ids); } - public void HandleAsync(SeriesDeletedEvent message) + public void HandleAsync(MovieDeletedEvent message) { - _logger.Debug("Deleting Extra from database for series: {0}", message.Series); - _repository.DeleteForSeries(message.Series.Id); + _logger.Debug("Deleting Extra from database for movie: {0}", message.Movie); + _repository.DeleteForMovie(message.Movie.Id); } - public void HandleAsync(EpisodeFileDeletedEvent message) + public void HandleAsync(MovieFileDeletedEvent message) { - var episodeFile = message.EpisodeFile; + var movieFile = message.MovieFile; if (message.Reason == DeleteMediaFileReason.NoLinkedEpisodes) { - _logger.Debug("Removing episode file from DB as part of cleanup routine, not deleting extra files from disk."); + _logger.Debug("Removing movie file from DB as part of cleanup routine, not deleting extra files from disk."); } else { - var series = _seriesService.GetSeries(message.EpisodeFile.SeriesId); + var movie = _movieService.GetMovie(message.MovieFile.MovieId); - foreach (var extra in _repository.GetFilesByEpisodeFile(episodeFile.Id)) + foreach (var extra in _repository.GetFilesByMovieFile(movieFile.Id)) { - var path = Path.Combine(series.Path, extra.RelativePath); + var path = Path.Combine(movie.Path, extra.RelativePath); if (_diskProvider.FileExists(path)) { @@ -135,8 +135,8 @@ public void HandleAsync(EpisodeFileDeletedEvent message) } } - _logger.Debug("Deleting Extra from database for episode file: {0}", episodeFile); - _repository.DeleteForEpisodeFile(episodeFile.Id); + _logger.Debug("Deleting Extra from database for movie file: {0}", movieFile); + _repository.DeleteForMovieFile(movieFile.Id); } } } diff --git a/src/NzbDrone.Core/Extras/IImportExistingExtraFiles.cs b/src/NzbDrone.Core/Extras/IImportExistingExtraFiles.cs index ad14b60a5..d638125de 100644 --- a/src/NzbDrone.Core/Extras/IImportExistingExtraFiles.cs +++ b/src/NzbDrone.Core/Extras/IImportExistingExtraFiles.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.Extras.Files; using NzbDrone.Core.Tv; @@ -7,6 +7,6 @@ namespace NzbDrone.Core.Extras public interface IImportExistingExtraFiles { int Order { get; } - IEnumerable ProcessFiles(Series series, List filesOnDisk, List importedFiles); + IEnumerable ProcessFiles(Movie movie, List filesOnDisk, List importedFiles); } } diff --git a/src/NzbDrone.Core/Extras/ImportExistingExtraFilesBase.cs b/src/NzbDrone.Core/Extras/ImportExistingExtraFilesBase.cs index a2dddaa69..1b96e5a8b 100644 --- a/src/NzbDrone.Core/Extras/ImportExistingExtraFilesBase.cs +++ b/src/NzbDrone.Core/Extras/ImportExistingExtraFilesBase.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using NzbDrone.Common; @@ -19,21 +19,21 @@ public ImportExistingExtraFilesBase(IExtraFileService extraFileServi } public abstract int Order { get; } - public abstract IEnumerable ProcessFiles(Series series, List filesOnDisk, List importedFiles); + public abstract IEnumerable ProcessFiles(Movie movie, List filesOnDisk, List importedFiles); - public virtual ImportExistingExtraFileFilterResult FilterAndClean(Series series, List filesOnDisk, List importedFiles) + public virtual ImportExistingExtraFileFilterResult FilterAndClean(Movie movie, List filesOnDisk, List importedFiles) { - var seriesFiles = _extraFileService.GetFilesBySeries(series.Id); + var movieFiles = _extraFileService.GetFilesByMovie(movie.Id); - Clean(series, filesOnDisk, importedFiles, seriesFiles); + Clean(movie, filesOnDisk, importedFiles, movieFiles); - return Filter(series, filesOnDisk, importedFiles, seriesFiles); + return Filter(movie, filesOnDisk, importedFiles, movieFiles); } - private ImportExistingExtraFileFilterResult Filter(Series series, List filesOnDisk, List importedFiles, List seriesFiles) + private ImportExistingExtraFileFilterResult Filter(Movie movie, List filesOnDisk, List importedFiles, List movieFiles) { - var previouslyImported = seriesFiles.IntersectBy(s => Path.Combine(series.Path, s.RelativePath), filesOnDisk, f => f, PathEqualityComparer.Instance).ToList(); - var filteredFiles = filesOnDisk.Except(previouslyImported.Select(f => Path.Combine(series.Path, f.RelativePath)).ToList(), PathEqualityComparer.Instance) + var previouslyImported = movieFiles.IntersectBy(s => Path.Combine(movie.Path, s.RelativePath), filesOnDisk, f => f, PathEqualityComparer.Instance).ToList(); + var filteredFiles = filesOnDisk.Except(previouslyImported.Select(f => Path.Combine(movie.Path, f.RelativePath)).ToList(), PathEqualityComparer.Instance) .Except(importedFiles, PathEqualityComparer.Instance) .ToList(); @@ -42,12 +42,12 @@ private ImportExistingExtraFileFilterResult Filter(Series series, Li return new ImportExistingExtraFileFilterResult(previouslyImported, filteredFiles); } - private void Clean(Series series, List filesOnDisk, List importedFiles, List seriesFiles) + private void Clean(Movie movie, List filesOnDisk, List importedFiles, List movieFiles) { - var alreadyImportedFileIds = seriesFiles.IntersectBy(f => Path.Combine(series.Path, f.RelativePath), importedFiles, i => i, PathEqualityComparer.Instance) + var alreadyImportedFileIds = movieFiles.IntersectBy(f => Path.Combine(movie.Path, f.RelativePath), importedFiles, i => i, PathEqualityComparer.Instance) .Select(f => f.Id); - var deletedFiles = seriesFiles.ExceptBy(f => Path.Combine(series.Path, f.RelativePath), filesOnDisk, i => i, PathEqualityComparer.Instance) + var deletedFiles = movieFiles.ExceptBy(f => Path.Combine(movie.Path, f.RelativePath), filesOnDisk, i => i, PathEqualityComparer.Instance) .Select(f => f.Id); _extraFileService.DeleteMany(alreadyImportedFileIds); diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadata.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadata.cs index d2ea82bae..6f910aa3a 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadata.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadata.cs @@ -25,7 +25,7 @@ public MediaBrowserMetadata( public override string Name => "Emby (Legacy)"; - public override MetadataFile FindMetadataFile(Series series, string path) + public override MetadataFile FindMetadataFile(Movie movie, string path) { var filename = Path.GetFileName(path); @@ -33,28 +33,28 @@ public override MetadataFile FindMetadataFile(Series series, string path) var metadata = new MetadataFile { - SeriesId = series.Id, + MovieId = movie.Id, Consumer = GetType().Name, - RelativePath = series.Path.GetRelativePath(path) + RelativePath = movie.Path.GetRelativePath(path) }; - if (filename.Equals("series.xml", StringComparison.InvariantCultureIgnoreCase)) + if (filename.Equals("movie.xml", StringComparison.InvariantCultureIgnoreCase)) { - metadata.Type = MetadataType.SeriesMetadata; + metadata.Type = MetadataType.MovieMetadata; return metadata; } return null; } - public override MetadataFileResult SeriesMetadata(Series series) + public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile) { - if (!Settings.SeriesMetadata) + if (!Settings.MovieMetadata) { return null; } - _logger.Debug("Generating series.xml for: {0}", series.Title); + _logger.Debug("Generating movie.xml for: {0}", movie.Title); var sb = new StringBuilder(); var xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; @@ -62,97 +62,39 @@ public override MetadataFileResult SeriesMetadata(Series series) using (var xw = XmlWriter.Create(sb, xws)) { - var tvShow = new XElement("Series"); + var movieElement = new XElement("Movie"); - tvShow.Add(new XElement("id", series.TvdbId)); - tvShow.Add(new XElement("Status", series.Status)); - tvShow.Add(new XElement("Network", series.Network)); - tvShow.Add(new XElement("Airs_Time", series.AirTime)); + movieElement.Add(new XElement("id", movie.ImdbId)); + movieElement.Add(new XElement("Status", movie.Status)); - if (series.FirstAired.HasValue) - { - tvShow.Add(new XElement("FirstAired", series.FirstAired.Value.ToString("yyyy-MM-dd"))); - } + movieElement.Add(new XElement("Added", movie.Added.ToString("MM/dd/yyyy HH:mm:ss tt"))); + movieElement.Add(new XElement("LockData", "false")); + movieElement.Add(new XElement("Overview", movie.Overview)); + movieElement.Add(new XElement("LocalTitle", movie.Title)); - tvShow.Add(new XElement("ContentRating", series.Certification)); - tvShow.Add(new XElement("Added", series.Added.ToString("MM/dd/yyyy HH:mm:ss tt"))); - tvShow.Add(new XElement("LockData", "false")); - tvShow.Add(new XElement("Overview", series.Overview)); - tvShow.Add(new XElement("LocalTitle", series.Title)); + movieElement.Add(new XElement("Rating", movie.Ratings.Value)); + movieElement.Add(new XElement("ProductionYear", movie.Year)); + movieElement.Add(new XElement("RunningTime", movie.Runtime)); + movieElement.Add(new XElement("IMDB", movie.ImdbId)); + movieElement.Add(new XElement("Genres", movie.Genres.Select(genre => new XElement("Genre", genre)))); - if (series.FirstAired.HasValue) - { - tvShow.Add(new XElement("PremiereDate", series.FirstAired.Value.ToString("yyyy-MM-dd"))); - } - - tvShow.Add(new XElement("Rating", series.Ratings.Value)); - tvShow.Add(new XElement("ProductionYear", series.Year)); - tvShow.Add(new XElement("RunningTime", series.Runtime)); - tvShow.Add(new XElement("IMDB", series.ImdbId)); - tvShow.Add(new XElement("TVRageId", series.TvRageId)); - tvShow.Add(new XElement("Genres", series.Genres.Select(genre => new XElement("Genre", genre)))); - - var persons = new XElement("Persons"); - - foreach (var person in series.Actors) - { - persons.Add(new XElement("Person", - new XElement("Name", person.Name), - new XElement("Type", "Actor"), - new XElement("Role", person.Character) - )); - } - - tvShow.Add(persons); - - - var doc = new XDocument(tvShow); + var doc = new XDocument(movieElement); doc.Save(xw); - _logger.Debug("Saving series.xml for {0}", series.Title); + _logger.Debug("Saving movie.xml for {0}", movie.Title); - return new MetadataFileResult("series.xml", doc.ToString()); + return new MetadataFileResult("movie.xml", doc.ToString()); } } - - public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile) - { - return null; - } - - public override List SeriesImages(Series series) + + public override List MovieImages(Movie movie, MovieFile movieFile) { return new List(); } - public override List SeasonImages(Series series, Season season) + private IEnumerable ProcessMovieImages(Movie movie) { return new List(); } - - public override List EpisodeImages(Series series, EpisodeFile episodeFile) - { - return new List(); - } - - private IEnumerable ProcessSeriesImages(Series series) - { - return new List(); - } - - private IEnumerable ProcessSeasonImages(Series series, Season season) - { - return new List(); - } - - private string GetEpisodeNfoFilename(string episodeFilePath) - { - return null; - } - - private string GetEpisodeImageFilename(string episodeFilePath) - { - return null; - } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadataSettings.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadataSettings.cs index 11899124f..fd64deb61 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadataSettings.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/MediaBrowser/MediaBrowserMetadataSettings.cs @@ -1,4 +1,4 @@ -using FluentValidation; +using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -18,11 +18,11 @@ public class MediaBrowserMetadataSettings : IProviderConfig public MediaBrowserMetadataSettings() { - SeriesMetadata = true; + MovieMetadata = true; } - [FieldDefinition(0, Label = "Series Metadata", Type = FieldType.Checkbox)] - public bool SeriesMetadata { get; set; } + [FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)] + public bool MovieMetadata { get; set; } public bool IsValid => true; diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadata.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadata.cs index cf5d5e61d..2984f265d 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadata.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadata.cs @@ -31,30 +31,30 @@ public RoksboxMetadata(IMapCoversToLocal mediaCoverService, _logger = logger; } - private static List ValidCertification = new List { "G", "NC-17", "PG", "PG-13", "R", "UR", "UNRATED", "NR", "TV-Y", "TV-Y7", "TV-Y7-FV", "TV-G", "TV-PG", "TV-14", "TV-MA" }; - private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?\d+))|(?specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + //Re-enable when/if we store and use mpaa certification + //private static List ValidCertification = new List { "G", "NC-17", "PG", "PG-13", "R", "UR", "UNRATED", "NR", "TV-Y", "TV-Y7", "TV-Y7-FV", "TV-G", "TV-PG", "TV-14", "TV-MA" }; public override string Name => "Roksbox"; - public override string GetFilenameAfterMove(Series series, EpisodeFile episodeFile, MetadataFile metadataFile) + public override string GetFilenameAfterMove(Movie movie, MovieFile movieFile, MetadataFile metadataFile) { - var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath); + var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath); - if (metadataFile.Type == MetadataType.EpisodeImage) + if (metadataFile.Type == MetadataType.MovieImage) { - return GetEpisodeImageFilename(episodeFilePath); + return GetMovieFileImageFilename(movieFilePath); } - if (metadataFile.Type == MetadataType.EpisodeMetadata) + if (metadataFile.Type == MetadataType.MovieMetadata) { - return GetEpisodeMetadataFilename(episodeFilePath); + return GetMovieFileMetadataFilename(movieFilePath); } - _logger.Debug("Unknown episode file metadata: {0}", metadataFile.RelativePath); - return Path.Combine(series.Path, metadataFile.RelativePath); + _logger.Debug("Unknown movie file metadata: {0}", metadataFile.RelativePath); + return Path.Combine(movie.Path, metadataFile.RelativePath); } - public override MetadataFile FindMetadataFile(Series series, string path) + public override MetadataFile FindMetadataFile(Movie movie, string path) { var filename = Path.GetFileName(path); @@ -63,81 +63,47 @@ public override MetadataFile FindMetadataFile(Series series, string path) var metadata = new MetadataFile { - SeriesId = series.Id, + MovieId = movie.Id, Consumer = GetType().Name, - RelativePath = series.Path.GetRelativePath(path) + RelativePath = movie.Path.GetRelativePath(path) }; - //Series and season images are both named folder.jpg, only season ones sit in season folders - if (Path.GetFileNameWithoutExtension(filename).Equals(parentdir.Name, StringComparison.InvariantCultureIgnoreCase)) - { - var seasonMatch = SeasonImagesRegex.Match(parentdir.Name); + var parseResult = Parser.Parser.ParseMovieTitle(filename, false); - if (seasonMatch.Success) - { - metadata.Type = MetadataType.SeasonImage; - - if (seasonMatch.Groups["specials"].Success) - { - metadata.SeasonNumber = 0; - } - - else - { - metadata.SeasonNumber = Convert.ToInt32(seasonMatch.Groups["season"].Value); - } - - return metadata; - } - - metadata.Type = MetadataType.SeriesImage; - return metadata; - } - - var parseResult = Parser.Parser.ParseTitle(filename); - - if (parseResult != null && - !parseResult.FullSeason) + if (parseResult != null) { var extension = Path.GetExtension(filename).ToLowerInvariant(); if (extension == ".xml") { - metadata.Type = MetadataType.EpisodeMetadata; + metadata.Type = MetadataType.MovieMetadata; return metadata; } if (extension == ".jpg") { - if (!Path.GetFileNameWithoutExtension(filename).EndsWith("-thumb")) + if (Path.GetFileNameWithoutExtension(filename).Equals(parentdir.Name, StringComparison.InvariantCultureIgnoreCase)) { - metadata.Type = MetadataType.EpisodeImage; + metadata.Type = MetadataType.MovieImage; return metadata; } - } + } } return null; } - public override MetadataFileResult SeriesMetadata(Series series) + public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile) { - //Series metadata is not supported - return null; - } - - public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile) - { - if (!Settings.EpisodeMetadata) + if (!Settings.MovieMetadata) { return null; } - - _logger.Debug("Generating Episode Metadata for: {0}", episodeFile.RelativePath); + + _logger.Debug("Generating Movie File Metadata for: {0}", movieFile.RelativePath); var xmlResult = string.Empty; - foreach (var episode in episodeFile.Episodes.Value) - { + var sb = new StringBuilder(); var xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; @@ -148,24 +114,11 @@ public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile ep var doc = new XDocument(); var details = new XElement("video"); - details.Add(new XElement("title", string.Format("{0} - {1}x{2} - {3}", series.Title, episode.SeasonNumber, episode.EpisodeNumber, episode.Title))); - details.Add(new XElement("year", episode.AirDate)); - details.Add(new XElement("genre", string.Join(" / ", series.Genres))); - var actors = string.Join(" , ", series.Actors.ConvertAll(c => c.Name + " - " + c.Character).GetRange(0, Math.Min(3, series.Actors.Count))); - details.Add(new XElement("actors", actors)); - details.Add(new XElement("description", episode.Overview)); - details.Add(new XElement("length", series.Runtime)); + details.Add(new XElement("title", movie.Title)); - if (series.Certification.IsNotNullOrWhiteSpace() && - ValidCertification.Contains(series.Certification.ToUpperInvariant())) - { - details.Add(new XElement("mpaa", series.Certification.ToUpperInvariant())); - } - - else - { - details.Add(new XElement("mpaa", "UNRATED")); - } + details.Add(new XElement("genre", string.Join(" / ", movie.Genres))); + details.Add(new XElement("description", movie.Overview)); + details.Add(new XElement("length", movie.Runtime)); doc.Add(details); doc.Save(xw); @@ -173,111 +126,39 @@ public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile ep xmlResult += doc.ToString(); xmlResult += Environment.NewLine; } - } - return new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray())); + + return new MetadataFileResult(GetMovieFileMetadataFilename(movieFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray())); } - public override List SeriesImages(Series series) + public override List MovieImages(Movie movie, MovieFile movieFile) { - var image = series.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? series.Images.FirstOrDefault(); + if (!Settings.MovieImages) + { + return new List(); + } + + var image = movie.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? movie.Images.FirstOrDefault(); if (image == null) { - _logger.Trace("Failed to find suitable Series image for series {0}.", series.Title); + _logger.Trace("Failed to find suitable Movie image for movie {0}.", movie.Title); return null; } - var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType); - var destination = Path.GetFileName(series.Path) + Path.GetExtension(source); + var source = _mediaCoverService.GetCoverPath(movie.Id, image.CoverType); + var destination = Path.GetFileName(movie.Path) + Path.GetExtension(source); - return new List{ new ImageFileResult(destination, source) }; + return new List { new ImageFileResult(destination, source) }; } - public override List SeasonImages(Series series, Season season) + private string GetMovieFileMetadataFilename(string movieFilePath) { - var seasonFolders = GetSeasonFolders(series); - - string seasonFolder; - if (!seasonFolders.TryGetValue(season.SeasonNumber, out seasonFolder)) - { - _logger.Trace("Failed to find season folder for series {0}, season {1}.", series.Title, season.SeasonNumber); - return new List(); - } - - //Roksbox only supports one season image, so first of all try for poster otherwise just use whatever is first in the collection - var image = season.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? season.Images.FirstOrDefault(); - if (image == null) - { - _logger.Trace("Failed to find suitable season image for series {0}, season {1}.", series.Title, season.SeasonNumber); - return new List(); - } - - var filename = Path.GetFileName(seasonFolder) + ".jpg"; - var path = series.Path.GetRelativePath(Path.Combine(series.Path, seasonFolder, filename)); - - return new List { new ImageFileResult(path, image.Url) }; + return Path.ChangeExtension(movieFilePath, "xml"); } - public override List EpisodeImages(Series series, EpisodeFile episodeFile) + private string GetMovieFileImageFilename(string movieFilePath) { - var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot); - - if (screenshot == null) - { - _logger.Trace("Episode screenshot not available"); - return new List(); - } - - return new List {new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url)}; - } - - private string GetEpisodeMetadataFilename(string episodeFilePath) - { - return Path.ChangeExtension(episodeFilePath, "xml"); - } - - private string GetEpisodeImageFilename(string episodeFilePath) - { - return Path.ChangeExtension(episodeFilePath, "jpg"); - } - - private Dictionary GetSeasonFolders(Series series) - { - var seasonFolderMap = new Dictionary(); - - foreach (var folder in _diskProvider.GetDirectories(series.Path)) - { - var directoryinfo = new DirectoryInfo(folder); - var seasonMatch = SeasonImagesRegex.Match(directoryinfo.Name); - - if (seasonMatch.Success) - { - var seasonNumber = seasonMatch.Groups["season"].Value; - - if (seasonNumber.Contains("specials")) - { - seasonFolderMap[0] = folder; - } - else - { - int matchedSeason; - if (int.TryParse(seasonNumber, out matchedSeason)) - { - seasonFolderMap[matchedSeason] = folder; - } - else - { - _logger.Debug("Failed to parse season number from {0} for series {1}.", folder, series.Title); - } - } - } - else - { - _logger.Debug("Rejecting folder {0} for series {1}.", Path.GetDirectoryName(folder), series.Title); - } - } - - return seasonFolderMap; + return Path.ChangeExtension(movieFilePath, "jpg"); } } } diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadataSettings.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadataSettings.cs index f0da481bf..238125817 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadataSettings.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/Roksbox/RoksboxMetadataSettings.cs @@ -1,4 +1,4 @@ -using FluentValidation; +using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -18,23 +18,15 @@ public class RoksboxMetadataSettings : IProviderConfig public RoksboxMetadataSettings() { - EpisodeMetadata = true; - SeriesImages = true; - SeasonImages = true; - EpisodeImages = true; + MovieMetadata = true; + MovieImages = true; } - [FieldDefinition(0, Label = "Episode Metadata", Type = FieldType.Checkbox)] - public bool EpisodeMetadata { get; set; } + [FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)] + public bool MovieMetadata { get; set; } - [FieldDefinition(1, Label = "Series Images", Type = FieldType.Checkbox)] - public bool SeriesImages { get; set; } - - [FieldDefinition(2, Label = "Season Images", Type = FieldType.Checkbox)] - public bool SeasonImages { get; set; } - - [FieldDefinition(3, Label = "Episode Images", Type = FieldType.Checkbox)] - public bool EpisodeImages { get; set; } + [FieldDefinition(1, Label = "Movie Images", Type = FieldType.Checkbox)] + public bool MovieImages { get; set; } public bool IsValid => true; diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadata.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadata.cs index d1846c963..877cc0cf2 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadata.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadata.cs @@ -31,30 +31,28 @@ public WdtvMetadata(IMapCoversToLocal mediaCoverService, _logger = logger; } - private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?\d+))|(?specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase); - public override string Name => "WDTV"; - public override string GetFilenameAfterMove(Series series, EpisodeFile episodeFile, MetadataFile metadataFile) + public override string GetFilenameAfterMove(Movie movie, MovieFile movieFile, MetadataFile metadataFile) { - var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath); + var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath); - if (metadataFile.Type == MetadataType.EpisodeImage) + if (metadataFile.Type == MetadataType.MovieImage) { - return GetEpisodeImageFilename(episodeFilePath); + return GetMovieFileImageFilename(movieFilePath); } - if (metadataFile.Type == MetadataType.EpisodeMetadata) + if (metadataFile.Type == MetadataType.MovieMetadata) { - return GetEpisodeMetadataFilename(episodeFilePath); + return GetMovieFileMetadataFilename(movieFilePath); } - _logger.Debug("Unknown episode file metadata: {0}", metadataFile.RelativePath); - return Path.Combine(series.Path, metadataFile.RelativePath); + _logger.Debug("Unknown movie file metadata: {0}", metadataFile.RelativePath); + return Path.Combine(movie.Path, metadataFile.RelativePath); } - public override MetadataFile FindMetadataFile(Series series, string path) + public override MetadataFile FindMetadataFile(Movie movie, string path) { var filename = Path.GetFileName(path); @@ -62,75 +60,47 @@ public override MetadataFile FindMetadataFile(Series series, string path) var metadata = new MetadataFile { - SeriesId = series.Id, + MovieId = movie.Id, Consumer = GetType().Name, - RelativePath = series.Path.GetRelativePath(path) + RelativePath = movie.Path.GetRelativePath(path) }; - //Series and season images are both named folder.jpg, only season ones sit in season folders if (Path.GetFileName(filename).Equals("folder.jpg", StringComparison.InvariantCultureIgnoreCase)) { - var parentdir = Directory.GetParent(path); - var seasonMatch = SeasonImagesRegex.Match(parentdir.Name); - if (seasonMatch.Success) - { - metadata.Type = MetadataType.SeasonImage; - - if (seasonMatch.Groups["specials"].Success) - { - metadata.SeasonNumber = 0; - } - - else - { - metadata.SeasonNumber = Convert.ToInt32(seasonMatch.Groups["season"].Value); - } - - return metadata; - } - - metadata.Type = MetadataType.SeriesImage; + metadata.Type = MetadataType.MovieImage; return metadata; } - var parseResult = Parser.Parser.ParseTitle(filename); + var parseResult = Parser.Parser.ParseMovieTitle(filename, false); - if (parseResult != null && - !parseResult.FullSeason) + if (parseResult != null) { switch (Path.GetExtension(filename).ToLowerInvariant()) { case ".xml": - metadata.Type = MetadataType.EpisodeMetadata; + metadata.Type = MetadataType.MovieMetadata; return metadata; case ".metathumb": - metadata.Type = MetadataType.EpisodeImage; + metadata.Type = MetadataType.MovieImage; return metadata; } - + } return null; } - public override MetadataFileResult SeriesMetadata(Series series) + public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile) { - //Series metadata is not supported - return null; - } - - public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile) - { - if (!Settings.EpisodeMetadata) + if (!Settings.MovieMetadata) { return null; } - _logger.Debug("Generating Episode Metadata for: {0}", Path.Combine(series.Path, episodeFile.RelativePath)); + _logger.Debug("Generating Movie File Metadata for: {0}", Path.Combine(movie.Path, movieFile.RelativePath)); var xmlResult = string.Empty; - foreach (var episode in episodeFile.Episodes.Value) - { + var sb = new StringBuilder(); var xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; @@ -141,21 +111,10 @@ public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile ep var doc = new XDocument(); var details = new XElement("details"); - details.Add(new XElement("id", series.Id)); - details.Add(new XElement("title", string.Format("{0} - {1}x{2:00} - {3}", series.Title, episode.SeasonNumber, episode.EpisodeNumber, episode.Title))); - details.Add(new XElement("series_name", series.Title)); - details.Add(new XElement("episode_name", episode.Title)); - details.Add(new XElement("season_number", episode.SeasonNumber.ToString("00"))); - details.Add(new XElement("episode_number", episode.EpisodeNumber.ToString("00"))); - details.Add(new XElement("firstaired", episode.AirDate)); - details.Add(new XElement("genre", string.Join(" / ", series.Genres))); - details.Add(new XElement("actor", string.Join(" / ", series.Actors.ConvertAll(c => c.Name + " - " + c.Character)))); - details.Add(new XElement("overview", episode.Overview)); - - - //Todo: get guest stars, writer and director - //details.Add(new XElement("credits", tvdbEpisode.Writer.FirstOrDefault())); - //details.Add(new XElement("director", tvdbEpisode.Directors.FirstOrDefault())); + details.Add(new XElement("id", movie.Id)); + details.Add(new XElement("title", movie.Title)); + details.Add(new XElement("genre", string.Join(" / ", movie.Genres))); + details.Add(new XElement("overview", movie.Overview)); doc.Add(details); doc.Save(xw); @@ -163,29 +122,29 @@ public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile ep xmlResult += doc.ToString(); xmlResult += Environment.NewLine; } - } - var filename = GetEpisodeMetadataFilename(episodeFile.RelativePath); + + var filename = GetMovieFileMetadataFilename(movieFile.RelativePath); return new MetadataFileResult(filename, xmlResult.Trim(Environment.NewLine.ToCharArray())); } - public override List SeriesImages(Series series) + public override List MovieImages(Movie movie, MovieFile moviefile) { - if (!Settings.SeriesImages) + if (!Settings.MovieImages) { return new List(); } //Because we only support one image, attempt to get the Poster type, then if that fails grab the first - var image = series.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? series.Images.FirstOrDefault(); + var image = movie.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? movie.Images.FirstOrDefault(); if (image == null) { - _logger.Trace("Failed to find suitable Series image for series {0}.", series.Title); + _logger.Trace("Failed to find suitable Movie image for movie {0}.", movie.Title); return new List(); } - var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType); + var source = _mediaCoverService.GetCoverPath(movie.Id, image.CoverType); var destination = "folder" + Path.GetExtension(source); return new List @@ -194,102 +153,14 @@ public override List SeriesImages(Series series) }; } - public override List SeasonImages(Series series, Season season) + private string GetMovieFileMetadataFilename(string movieFilePath) { - if (!Settings.SeasonImages) - { - return new List(); - } - - var seasonFolders = GetSeasonFolders(series); - - //Work out the path to this season - if we don't have a matching path then skip this season. - string seasonFolder; - if (!seasonFolders.TryGetValue(season.SeasonNumber, out seasonFolder)) - { - _logger.Trace("Failed to find season folder for series {0}, season {1}.", series.Title, season.SeasonNumber); - return new List(); - } - - //WDTV only supports one season image, so first of all try for poster otherwise just use whatever is first in the collection - var image = season.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? season.Images.FirstOrDefault(); - if (image == null) - { - _logger.Trace("Failed to find suitable season image for series {0}, season {1}.", series.Title, season.SeasonNumber); - return new List(); - } - - var path = Path.Combine(seasonFolder, "folder.jpg"); - - return new List{ new ImageFileResult(path, image.Url) }; + return Path.ChangeExtension(movieFilePath, "xml"); } - public override List EpisodeImages(Series series, EpisodeFile episodeFile) + private string GetMovieFileImageFilename(string movieFilePath) { - if (!Settings.EpisodeImages) - { - return new List(); - } - - var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot); - - if (screenshot == null) - { - _logger.Trace("Episode screenshot not available"); - return new List(); - } - - return new List{ new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url) }; - } - - private string GetEpisodeMetadataFilename(string episodeFilePath) - { - return Path.ChangeExtension(episodeFilePath, "xml"); - } - - private string GetEpisodeImageFilename(string episodeFilePath) - { - return Path.ChangeExtension(episodeFilePath, "metathumb"); - } - - private Dictionary GetSeasonFolders(Series series) - { - var seasonFolderMap = new Dictionary(); - - foreach (var folder in _diskProvider.GetDirectories(series.Path)) - { - var directoryinfo = new DirectoryInfo(folder); - var seasonMatch = SeasonImagesRegex.Match(directoryinfo.Name); - - if (seasonMatch.Success) - { - var seasonNumber = seasonMatch.Groups["season"].Value; - - if (seasonNumber.Contains("specials")) - { - seasonFolderMap[0] = folder; - } - else - { - int matchedSeason; - if (int.TryParse(seasonNumber, out matchedSeason)) - { - seasonFolderMap[matchedSeason] = folder; - } - else - { - _logger.Debug("Failed to parse season number from {0} for series {1}.", folder, series.Title); - } - } - } - - else - { - _logger.Debug("Rejecting folder {0} for series {1}.", Path.GetDirectoryName(folder), series.Title); - } - } - - return seasonFolderMap; + return Path.ChangeExtension(movieFilePath, "metathumb"); } } } diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadataSettings.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadataSettings.cs index e010ff7e5..2d5a354d6 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadataSettings.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/Wdtv/WdtvMetadataSettings.cs @@ -1,4 +1,4 @@ -using FluentValidation; +using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -18,23 +18,15 @@ public class WdtvMetadataSettings : IProviderConfig public WdtvMetadataSettings() { - EpisodeMetadata = true; - SeriesImages = true; - SeasonImages = true; - EpisodeImages = true; + MovieMetadata = true; + MovieImages = true; } - [FieldDefinition(0, Label = "Episode Metadata", Type = FieldType.Checkbox)] - public bool EpisodeMetadata { get; set; } + [FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)] + public bool MovieMetadata { get; set; } - [FieldDefinition(1, Label = "Series Images", Type = FieldType.Checkbox)] - public bool SeriesImages { get; set; } - - [FieldDefinition(2, Label = "Season Images", Type = FieldType.Checkbox)] - public bool SeasonImages { get; set; } - - [FieldDefinition(3, Label = "Episode Images", Type = FieldType.Checkbox)] - public bool EpisodeImages { get; set; } + [FieldDefinition(1, Label = "Movie Images", Type = FieldType.Checkbox)] + public bool MovieImages { get; set; } public bool IsValid => true; diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadata.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadata.cs index 9e9d472ab..b273429df 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadata.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadata.cs @@ -27,357 +27,227 @@ public XbmcMetadata(IMapCoversToLocal mediaCoverService, _logger = logger; } - private static readonly Regex SeriesImagesRegex = new Regex(@"^(?poster|banner|fanart)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private static readonly Regex SeasonImagesRegex = new Regex(@"^season(?\d{2,}|-all|-specials)-(?poster|banner|fanart)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private static readonly Regex EpisodeImageRegex = new Regex(@"-thumb\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex MovieImagesRegex = new Regex(@"^(?poster|banner|fanart|clearart|disc|landscape|logo)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex MovieFileImageRegex = new Regex(@"(?-thumb|-poster|-banner|-fanart)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); public override string Name => "Kodi (XBMC) / Emby"; - public override string GetFilenameAfterMove(Series series, EpisodeFile episodeFile, MetadataFile metadataFile) + public override string GetFilenameAfterMove(Movie movie, MovieFile movieFile, MetadataFile metadataFile) { - var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath); + var movieFilePath = Path.Combine(movie.Path, movieFile.RelativePath); + var metadataPath = Path.Combine(movie.Path, metadataFile.RelativePath); - if (metadataFile.Type == MetadataType.EpisodeImage) + if (metadataFile.Type == MetadataType.MovieMetadata) { - return GetEpisodeImageFilename(episodeFilePath); + return GetMovieMetadataFilename(movieFilePath); } - if (metadataFile.Type == MetadataType.EpisodeMetadata) + if (metadataFile.Type == MetadataType.MovieImage) { - return GetEpisodeMetadataFilename(episodeFilePath); + return GetMovieImageFilename(movieFilePath, metadataPath); } - _logger.Debug("Unknown episode file metadata: {0}", metadataFile.RelativePath); - return Path.Combine(series.Path, metadataFile.RelativePath); + _logger.Debug("Unknown movie file metadata: {0}", metadataFile.RelativePath); + return Path.Combine(movie.Path, metadataFile.RelativePath); } - public override MetadataFile FindMetadataFile(Series series, string path) + public override MetadataFile FindMetadataFile(Movie movie, string path) { var filename = Path.GetFileName(path); if (filename == null) return null; var metadata = new MetadataFile - { - SeriesId = series.Id, - Consumer = GetType().Name, - RelativePath = series.Path.GetRelativePath(path) - }; - - if (SeriesImagesRegex.IsMatch(filename)) { - metadata.Type = MetadataType.SeriesImage; + MovieId = movie.Id, + Consumer = GetType().Name, + RelativePath = movie.Path.GetRelativePath(path) + }; + + if (MovieImagesRegex.IsMatch(filename)) + { + metadata.Type = MetadataType.MovieImage; return metadata; } - var seasonMatch = SeasonImagesRegex.Match(filename); - - if (seasonMatch.Success) + if (MovieFileImageRegex.IsMatch(filename)) { - metadata.Type = MetadataType.SeasonImage; - - var seasonNumberMatch = seasonMatch.Groups["season"].Value; - int seasonNumber; - - if (seasonNumberMatch.Contains("specials")) - { - metadata.SeasonNumber = 0; - } - - else if (int.TryParse(seasonNumberMatch, out seasonNumber)) - { - metadata.SeasonNumber = seasonNumber; - } - - else - { - return null; - } - + metadata.Type = MetadataType.MovieImage; return metadata; } - if (EpisodeImageRegex.IsMatch(filename)) + if (filename.Equals("movie.nfo", StringComparison.OrdinalIgnoreCase)) { - metadata.Type = MetadataType.EpisodeImage; + metadata.Type = MetadataType.MovieMetadata; return metadata; } - if (filename.Equals("tvshow.nfo", StringComparison.InvariantCultureIgnoreCase)) - { - metadata.Type = MetadataType.SeriesMetadata; - return metadata; - } - - var parseResult = Parser.Parser.ParseTitle(filename); + var parseResult = Parser.Parser.ParseMovieTitle(filename, false); if (parseResult != null && - !parseResult.FullSeason && - Path.GetExtension(filename) == ".nfo") + Path.GetExtension(filename).Equals(".nfo", StringComparison.OrdinalIgnoreCase)) { - metadata.Type = MetadataType.EpisodeMetadata; + metadata.Type = MetadataType.MovieMetadata; return metadata; } return null; } - public override MetadataFileResult SeriesMetadata(Series series) + public override MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile) { - if (!Settings.SeriesMetadata) + if (!Settings.MovieMetadata) { return null; } - _logger.Debug("Generating tvshow.nfo for: {0}", series.Title); + _logger.Debug("Generating Movie Metadata for: {0}", Path.Combine(movie.Path, movieFile.RelativePath)); + + var xmlResult = string.Empty; + var sb = new StringBuilder(); var xws = new XmlWriterSettings(); xws.OmitXmlDeclaration = true; xws.Indent = false; - var episodeGuideUrl = string.Format("http://www.thetvdb.com/api/1D62F2F90030C444/series/{0}/all/en.zip", series.TvdbId); - using (var xw = XmlWriter.Create(sb, xws)) { - var tvShow = new XElement("tvshow"); + var doc = new XDocument(); + var image = movie.Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot); - tvShow.Add(new XElement("title", series.Title)); + var details = new XElement("movie"); - if (series.Ratings != null && series.Ratings.Votes > 0) + details.Add(new XElement("title", movie.Title)); + + if (movie.Ratings != null && movie.Ratings.Votes > 0) { - tvShow.Add(new XElement("rating", series.Ratings.Value)); + details.Add(new XElement("rating", movie.Ratings.Value)); } - tvShow.Add(new XElement("plot", series.Overview)); - tvShow.Add(new XElement("episodeguide", new XElement("url", episodeGuideUrl))); - tvShow.Add(new XElement("episodeguideurl", episodeGuideUrl)); - tvShow.Add(new XElement("mpaa", series.Certification)); - tvShow.Add(new XElement("id", series.TvdbId)); + details.Add(new XElement("plot", movie.Overview)); + details.Add(new XElement("id", movie.ImdbId)); + details.Add(new XElement("year", movie.Year)); - foreach (var genre in series.Genres) + if (movie.InCinemas.HasValue) { - tvShow.Add(new XElement("genre", genre)); + details.Add(new XElement("premiered", movie.InCinemas.Value.ToString())); } - if (series.FirstAired.HasValue) + foreach (var genre in movie.Genres) { - tvShow.Add(new XElement("premiered", series.FirstAired.Value.ToString("yyyy-MM-dd"))); + details.Add(new XElement("genre", genre)); } - tvShow.Add(new XElement("studio", series.Network)); + details.Add(new XElement("studio", movie.Studio)); - foreach (var actor in series.Actors) + if (image == null) { - var xmlActor = new XElement("actor", - new XElement("name", actor.Name), - new XElement("role", actor.Character)); + details.Add(new XElement("thumb")); + } - if (actor.Images.Any()) + else + { + details.Add(new XElement("thumb", image.Url)); + } + + details.Add(new XElement("watched", "false")); + + if (movieFile.MediaInfo != null) + { + var fileInfo = new XElement("fileinfo"); + var streamDetails = new XElement("streamdetails"); + + var video = new XElement("video"); + video.Add(new XElement("aspect", (float)movieFile.MediaInfo.Width / (float)movieFile.MediaInfo.Height)); + video.Add(new XElement("bitrate", movieFile.MediaInfo.VideoBitrate)); + video.Add(new XElement("codec", movieFile.MediaInfo.VideoCodec)); + video.Add(new XElement("framerate", movieFile.MediaInfo.VideoFps)); + video.Add(new XElement("height", movieFile.MediaInfo.Height)); + video.Add(new XElement("scantype", movieFile.MediaInfo.ScanType)); + video.Add(new XElement("width", movieFile.MediaInfo.Width)); + + if (movieFile.MediaInfo.RunTime != null) { - xmlActor.Add(new XElement("thumb", actor.Images.First().Url)); + video.Add(new XElement("duration", movieFile.MediaInfo.RunTime.TotalMinutes)); + video.Add(new XElement("durationinseconds", movieFile.MediaInfo.RunTime.TotalSeconds)); } - tvShow.Add(xmlActor); + streamDetails.Add(video); + + var audio = new XElement("audio"); + audio.Add(new XElement("bitrate", movieFile.MediaInfo.AudioBitrate)); + audio.Add(new XElement("channels", movieFile.MediaInfo.AudioChannels)); + audio.Add(new XElement("codec", GetAudioCodec(movieFile.MediaInfo.AudioFormat))); + audio.Add(new XElement("language", movieFile.MediaInfo.AudioLanguages)); + streamDetails.Add(audio); + + if (movieFile.MediaInfo.Subtitles != null && movieFile.MediaInfo.Subtitles.Length > 0) + { + var subtitle = new XElement("subtitle"); + subtitle.Add(new XElement("language", movieFile.MediaInfo.Subtitles)); + streamDetails.Add(subtitle); + } + + fileInfo.Add(streamDetails); + details.Add(fileInfo); } - var doc = new XDocument(tvShow); + doc.Add(details); doc.Save(xw); - _logger.Debug("Saving tvshow.nfo for {0}", series.Title); + xmlResult += doc.ToString(); + xmlResult += Environment.NewLine; - return new MetadataFileResult("tvshow.nfo", doc.ToString()); } + + var metadataFileName = GetMovieMetadataFilename(movieFile.RelativePath); + + if (Settings.UseMovieNfo) + { + metadataFileName = "movie.nfo"; + } + + return new MetadataFileResult(metadataFileName, xmlResult.Trim(Environment.NewLine.ToCharArray())); } - public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile) + public override List MovieImages(Movie movie, MovieFile movieFile) { - if (!Settings.EpisodeMetadata) - { - return null; - } - - _logger.Debug("Generating Episode Metadata for: {0}", Path.Combine(series.Path, episodeFile.RelativePath)); - - var xmlResult = string.Empty; - foreach (var episode in episodeFile.Episodes.Value) - { - var sb = new StringBuilder(); - var xws = new XmlWriterSettings(); - xws.OmitXmlDeclaration = true; - xws.Indent = false; - - using (var xw = XmlWriter.Create(sb, xws)) - { - var doc = new XDocument(); - var image = episode.Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot); - - var details = new XElement("episodedetails"); - details.Add(new XElement("title", episode.Title)); - details.Add(new XElement("season", episode.SeasonNumber)); - details.Add(new XElement("episode", episode.EpisodeNumber)); - details.Add(new XElement("aired", episode.AirDate)); - details.Add(new XElement("plot", episode.Overview)); - - //If trakt ever gets airs before information for specials we should add set it - details.Add(new XElement("displayseason")); - details.Add(new XElement("displayepisode")); - - if (image == null) - { - details.Add(new XElement("thumb")); - } - - else - { - details.Add(new XElement("thumb", image.Url)); - } - - details.Add(new XElement("watched", "false")); - - if (episode.Ratings != null && episode.Ratings.Votes > 0) - { - details.Add(new XElement("rating", episode.Ratings.Value)); - } - - if (episodeFile.MediaInfo != null) - { - var fileInfo = new XElement("fileinfo"); - var streamDetails = new XElement("streamdetails"); - - var video = new XElement("video"); - video.Add(new XElement("aspect", (float) episodeFile.MediaInfo.Width / (float) episodeFile.MediaInfo.Height)); - video.Add(new XElement("bitrate", episodeFile.MediaInfo.VideoBitrate)); - video.Add(new XElement("codec", episodeFile.MediaInfo.VideoCodec)); - video.Add(new XElement("framerate", episodeFile.MediaInfo.VideoFps)); - video.Add(new XElement("height", episodeFile.MediaInfo.Height)); - video.Add(new XElement("scantype", episodeFile.MediaInfo.ScanType)); - video.Add(new XElement("width", episodeFile.MediaInfo.Height)); - - if (episodeFile.MediaInfo.RunTime != null) - { - video.Add(new XElement("duration", episodeFile.MediaInfo.RunTime.TotalMinutes)); - video.Add(new XElement("durationinseconds", episodeFile.MediaInfo.RunTime.TotalSeconds)); - } - - streamDetails.Add(video); - - var audio = new XElement("audio"); - audio.Add(new XElement("bitrate", episodeFile.MediaInfo.AudioBitrate)); - audio.Add(new XElement("channels", episodeFile.MediaInfo.AudioChannels)); - audio.Add(new XElement("codec", GetAudioCodec(episodeFile.MediaInfo.AudioFormat))); - audio.Add(new XElement("language", episodeFile.MediaInfo.AudioLanguages)); - streamDetails.Add(audio); - - if (episodeFile.MediaInfo.Subtitles != null && episodeFile.MediaInfo.Subtitles.Length > 0) - { - var subtitle = new XElement("subtitle"); - subtitle.Add(new XElement("language", episodeFile.MediaInfo.Subtitles)); - streamDetails.Add(subtitle); - } - - fileInfo.Add(streamDetails); - details.Add(fileInfo); - } - - //Todo: get guest stars, writer and director - //details.Add(new XElement("credits", tvdbEpisode.Writer.FirstOrDefault())); - //details.Add(new XElement("director", tvdbEpisode.Directors.FirstOrDefault())); - - doc.Add(details); - doc.Save(xw); - - xmlResult += doc.ToString(); - xmlResult += Environment.NewLine; - } - } - - return new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray())); - } - - public override List SeriesImages(Series series) - { - if (!Settings.SeriesImages) + if (!Settings.MovieImages) { return new List(); } - return ProcessSeriesImages(series).ToList(); + return ProcessMovieImages(movie).ToList(); } - public override List SeasonImages(Series series, Season season) + private IEnumerable ProcessMovieImages(Movie movie) { - if (!Settings.SeasonImages) + foreach (var image in movie.Images) { - return new List(); - } - - return ProcessSeasonImages(series, season).ToList(); - } - - public override List EpisodeImages(Series series, EpisodeFile episodeFile) - { - if (!Settings.EpisodeImages) - { - return new List(); - } - - try - { - var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot); - - if (screenshot == null) - { - _logger.Debug("Episode screenshot not available"); - return new List(); - } - - return new List - { - new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url) - }; - } - catch (Exception ex) - { - _logger.Error(ex, "Unable to process episode image for file: " + Path.Combine(series.Path, episodeFile.RelativePath)); - - return new List(); - } - } - - private IEnumerable ProcessSeriesImages(Series series) - { - foreach (var image in series.Images) - { - var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType); - var destination = image.CoverType.ToString().ToLowerInvariant() + Path.GetExtension(source); + var source = _mediaCoverService.GetCoverPath(movie.Id, image.CoverType); + var destination = Path.ChangeExtension(movie.MovieFile.RelativePath,"").TrimEnd(".") + "-" + image.CoverType.ToString().ToLowerInvariant() + Path.GetExtension(source); yield return new ImageFileResult(destination, source); } } - private IEnumerable ProcessSeasonImages(Series series, Season season) + private string GetMovieMetadataFilename(string movieFilePath) { - foreach (var image in season.Images) + return Path.ChangeExtension(movieFilePath, "nfo"); + } + + private string GetMovieImageFilename(string movieFilePath, string existingImageName) + { + var fileExtention = Path.GetExtension(existingImageName); + var match = MovieFileImageRegex.Matches(existingImageName); + + if (match.Count > 0) { - var filename = string.Format("season{0:00}-{1}.jpg", season.SeasonNumber, image.CoverType.ToString().ToLower()); - - if (season.SeasonNumber == 0) - { - filename = string.Format("season-specials-{0}.jpg", image.CoverType.ToString().ToLower()); - } - - yield return new ImageFileResult(filename, image.Url); + var imageType = match[0].Groups["type"].Value; + return Parser.Parser.RemoveFileExtension(movieFilePath) + imageType + fileExtention; } - } - private string GetEpisodeMetadataFilename(string episodeFilePath) - { - return Path.ChangeExtension(episodeFilePath, "nfo"); - } - - private string GetEpisodeImageFilename(string episodeFilePath) - { - return Path.ChangeExtension(episodeFilePath, "").Trim('.') + "-thumb.jpg"; + return existingImageName; } private string GetAudioCodec(string audioCodec) diff --git a/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadataSettings.cs b/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadataSettings.cs index cd4b833ae..cc8959243 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadataSettings.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Consumers/Xbmc/XbmcMetadataSettings.cs @@ -1,4 +1,4 @@ -using FluentValidation; +using FluentValidation; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -18,28 +18,20 @@ public class XbmcMetadataSettings : IProviderConfig public XbmcMetadataSettings() { - SeriesMetadata = true; - EpisodeMetadata = true; - SeriesImages = true; - SeasonImages = true; - EpisodeImages = true; + MovieMetadata = true; + MovieImages = true; + UseMovieNfo = false; } - [FieldDefinition(0, Label = "Series Metadata", Type = FieldType.Checkbox)] - public bool SeriesMetadata { get; set; } + [FieldDefinition(0, Label = "Movie Metadata", Type = FieldType.Checkbox)] + public bool MovieMetadata { get; set; } - [FieldDefinition(1, Label = "Episode Metadata", Type = FieldType.Checkbox)] - public bool EpisodeMetadata { get; set; } + [FieldDefinition(1, Label = "Movie Images", Type = FieldType.Checkbox)] + public bool MovieImages { get; set; } - [FieldDefinition(2, Label = "Series Images", Type = FieldType.Checkbox)] - public bool SeriesImages { get; set; } + [FieldDefinition(2, Label = "Use Movie.nfo", Type = FieldType.Checkbox, HelpText = "Radarr will write metadata to movie.nfo instead of the default .nfo")] + public bool UseMovieNfo { get; set; } - [FieldDefinition(3, Label = "Season Images", Type = FieldType.Checkbox)] - public bool SeasonImages { get; set; } - - [FieldDefinition(4, Label = "Episode Images", Type = FieldType.Checkbox)] - public bool EpisodeImages { get; set; } - public bool IsValid => true; public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs b/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs index fa271f575..0b29f89c1 100644 --- a/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs +++ b/src/NzbDrone.Core/Extras/Metadata/ExistingMetadataImporter.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; @@ -32,12 +32,12 @@ public ExistingMetadataImporter(IExtraFileService metadataFileServ public override int Order => 0; - public override IEnumerable ProcessFiles(Series series, List filesOnDisk, List importedFiles) + public override IEnumerable ProcessFiles(Movie movie, List filesOnDisk, List importedFiles) { - _logger.Debug("Looking for existing metadata in {0}", series.Path); + _logger.Debug("Looking for existing metadata in {0}", movie.Path); var metadataFiles = new List(); - var filterResult = FilterAndClean(series, filesOnDisk, importedFiles); + var filterResult = FilterAndClean(movie, filesOnDisk, importedFiles); foreach (var possibleMetadataFile in filterResult.FilesOnDisk) { @@ -50,38 +50,31 @@ public override IEnumerable ProcessFiles(Series series, List foreach (var consumer in _consumers) { - var metadata = consumer.FindMetadataFile(series, possibleMetadataFile); + var metadata = consumer.FindMetadataFile(movie, possibleMetadataFile); if (metadata == null) { continue; } - if (metadata.Type == MetadataType.EpisodeImage || - metadata.Type == MetadataType.EpisodeMetadata) + if (metadata.Type == MetadataType.MovieImage || + metadata.Type == MetadataType.MovieMetadata) { - var localEpisode = _parsingService.GetLocalEpisode(possibleMetadataFile, series); + var localMovie = _parsingService.GetLocalMovie(possibleMetadataFile, movie); - if (localEpisode == null) + if (localMovie == null) { _logger.Debug("Unable to parse extra file: {0}", possibleMetadataFile); continue; } - if (localEpisode.Episodes.Empty()) + if (localMovie.Movie == null) { - _logger.Debug("Cannot find related episodes for: {0}", possibleMetadataFile); + _logger.Debug("Cannot find related movie for: {0}", possibleMetadataFile); continue; } - - if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1) - { - _logger.Debug("Extra file: {0} does not match existing files.", possibleMetadataFile); - continue; - } - - metadata.SeasonNumber = localEpisode.SeasonNumber; - metadata.EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId; + + metadata.MovieFileId = localMovie.Movie.MovieFileId; } metadata.Extension = Path.GetExtension(possibleMetadataFile); diff --git a/src/NzbDrone.Core/Extras/Metadata/Files/CleanMetadataFileService.cs b/src/NzbDrone.Core/Extras/Metadata/Files/CleanMetadataFileService.cs index 6166ae20b..e731c781c 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Files/CleanMetadataFileService.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Files/CleanMetadataFileService.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Tv; @@ -7,7 +7,7 @@ namespace NzbDrone.Core.Extras.Metadata.Files { public interface ICleanMetadataService { - void Clean(Series series); + void Clean(Movie movie); } public class CleanExtraFileService : ICleanMetadataService @@ -25,15 +25,15 @@ public CleanExtraFileService(IMetadataFileService metadataFileService, _logger = logger; } - public void Clean(Series series) + public void Clean(Movie movie) { - _logger.Debug("Cleaning missing metadata files for series: {0}", series.Title); + _logger.Debug("Cleaning missing metadata files for movie: {0}", movie.Title); - var metadataFiles = _metadataFileService.GetFilesBySeries(series.Id); + var metadataFiles = _metadataFileService.GetFilesByMovie(movie.Id); foreach (var metadataFile in metadataFiles) { - if (!_diskProvider.FileExists(Path.Combine(series.Path, metadataFile.RelativePath))) + if (!_diskProvider.FileExists(Path.Combine(movie.Path, metadataFile.RelativePath))) { _logger.Debug("Deleting metadata file from database: {0}", metadataFile.RelativePath); _metadataFileService.Delete(metadataFile.Id); diff --git a/src/NzbDrone.Core/Extras/Metadata/Files/MetadataFileService.cs b/src/NzbDrone.Core/Extras/Metadata/Files/MetadataFileService.cs index f5fc2ba69..c0dfdb52c 100644 --- a/src/NzbDrone.Core/Extras/Metadata/Files/MetadataFileService.cs +++ b/src/NzbDrone.Core/Extras/Metadata/Files/MetadataFileService.cs @@ -1,4 +1,4 @@ -using NLog; +using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Extras.Files; using NzbDrone.Core.MediaFiles; @@ -12,8 +12,8 @@ public interface IMetadataFileService : IExtraFileService public class MetadataFileService : ExtraFileService, IMetadataFileService { - public MetadataFileService(IExtraFileRepository repository, ISeriesService seriesService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger) - : base(repository, seriesService, diskProvider, recycleBinProvider, logger) + public MetadataFileService(IExtraFileRepository repository, IMovieService movieService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger) + : base(repository, movieService, diskProvider, recycleBinProvider, logger) { } diff --git a/src/NzbDrone.Core/Extras/Metadata/IMetadata.cs b/src/NzbDrone.Core/Extras/Metadata/IMetadata.cs index b631425e6..3fa89b7ed 100644 --- a/src/NzbDrone.Core/Extras/Metadata/IMetadata.cs +++ b/src/NzbDrone.Core/Extras/Metadata/IMetadata.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.Extras.Metadata.Files; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.ThingiProvider; @@ -8,12 +8,9 @@ namespace NzbDrone.Core.Extras.Metadata { public interface IMetadata : IProvider { - string GetFilenameAfterMove(Series series, EpisodeFile episodeFile, MetadataFile metadataFile); - MetadataFile FindMetadataFile(Series series, string path); - MetadataFileResult SeriesMetadata(Series series); - MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile); - List SeriesImages(Series series); - List SeasonImages(Series series, Season season); - List EpisodeImages(Series series, EpisodeFile episodeFile); + string GetFilenameAfterMove(Movie movie, MovieFile movieFile, MetadataFile metadataFile); + MetadataFile FindMetadataFile(Movie movie, string path); + MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile); + List MovieImages(Movie movie, MovieFile movieFile); } } diff --git a/src/NzbDrone.Core/Extras/Metadata/MetadataBase.cs b/src/NzbDrone.Core/Extras/Metadata/MetadataBase.cs index 7f05cbe16..76049c886 100644 --- a/src/NzbDrone.Core/Extras/Metadata/MetadataBase.cs +++ b/src/NzbDrone.Core/Extras/Metadata/MetadataBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using FluentValidation.Results; @@ -29,22 +29,19 @@ public ValidationResult Test() return new ValidationResult(); } - public virtual string GetFilenameAfterMove(Series series, EpisodeFile episodeFile, MetadataFile metadataFile) + public virtual string GetFilenameAfterMove(Movie movie, MovieFile movieFile, MetadataFile metadataFile) { - var existingFilename = Path.Combine(series.Path, metadataFile.RelativePath); + var existingFilename = Path.Combine(movie.Path, metadataFile.RelativePath); var extension = Path.GetExtension(existingFilename).TrimStart('.'); - var newFileName = Path.ChangeExtension(Path.Combine(series.Path, episodeFile.RelativePath), extension); + var newFileName = Path.ChangeExtension(Path.Combine(movie.Path, movieFile.RelativePath), extension); return newFileName; } - public abstract MetadataFile FindMetadataFile(Series series, string path); - - public abstract MetadataFileResult SeriesMetadata(Series series); - public abstract MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile); - public abstract List SeriesImages(Series series); - public abstract List SeasonImages(Series series, Season season); - public abstract List EpisodeImages(Series series, EpisodeFile episodeFile); + public abstract MetadataFile FindMetadataFile(Movie movie, string path); + + public abstract MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile); + public abstract List MovieImages(Movie movie, MovieFile movieFile); public virtual object RequestAction(string action, IDictionary query) { return null; } diff --git a/src/NzbDrone.Core/Extras/Metadata/MetadataService.cs b/src/NzbDrone.Core/Extras/Metadata/MetadataService.cs index 95198f2f0..f3ebf0593 100644 --- a/src/NzbDrone.Core/Extras/Metadata/MetadataService.cs +++ b/src/NzbDrone.Core/Extras/Metadata/MetadataService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -19,23 +19,23 @@ public class MetadataService : ExtraFileManager { private readonly IMetadataFactory _metadataFactory; private readonly ICleanMetadataService _cleanMetadataService; - private readonly IDiskTransferService _diskTransferService; private readonly IDiskProvider _diskProvider; + private readonly IDiskTransferService _diskTransferService; private readonly IHttpClient _httpClient; private readonly IMediaFileAttributeService _mediaFileAttributeService; private readonly IMetadataFileService _metadataFileService; private readonly Logger _logger; public MetadataService(IConfigService configService, + IDiskProvider diskProvider, IDiskTransferService diskTransferService, IMetadataFactory metadataFactory, ICleanMetadataService cleanMetadataService, - IDiskProvider diskProvider, IHttpClient httpClient, IMediaFileAttributeService mediaFileAttributeService, IMetadataFileService metadataFileService, Logger logger) - : base(configService, diskTransferService, metadataFileService) + : base(configService, diskProvider, diskTransferService, logger) { _metadataFactory = metadataFactory; _cleanMetadataService = cleanMetadataService; @@ -49,14 +49,14 @@ public MetadataService(IConfigService configService, public override int Order => 0; - public override IEnumerable CreateAfterSeriesScan(Series series, List episodeFiles) + public override IEnumerable CreateAfterMovieScan(Movie movie, List movieFiles) { - var metadataFiles = _metadataFileService.GetFilesBySeries(series.Id); - _cleanMetadataService.Clean(series); + var metadataFiles = _metadataFileService.GetFilesByMovie(movie.Id); + _cleanMetadataService.Clean(movie); - if (!_diskProvider.FolderExists(series.Path)) + if (!_diskProvider.FolderExists(movie.Path)) { - _logger.Info("Series folder does not exist, skipping metadata creation"); + _logger.Info("Movie folder does not exist, skipping metadata creation"); return Enumerable.Empty(); } @@ -66,14 +66,10 @@ public override IEnumerable CreateAfterSeriesScan(Series series, List { var consumerFiles = GetMetadataFilesForConsumer(consumer, metadataFiles); - files.AddIfNotNull(ProcessSeriesMetadata(consumer, series, consumerFiles)); - files.AddRange(ProcessSeriesImages(consumer, series, consumerFiles)); - files.AddRange(ProcessSeasonImages(consumer, series, consumerFiles)); - - foreach (var episodeFile in episodeFiles) + foreach (var episodeFile in movieFiles) { - files.AddIfNotNull(ProcessEpisodeMetadata(consumer, series, episodeFile, consumerFiles)); - files.AddRange(ProcessEpisodeImages(consumer, series, episodeFile, consumerFiles)); + files.AddIfNotNull(ProcessMovieMetadata(consumer, movie, episodeFile, consumerFiles)); + files.AddRange(ProcessMovieImages(consumer, movie, episodeFile, consumerFiles)); } } @@ -82,15 +78,15 @@ public override IEnumerable CreateAfterSeriesScan(Series series, List return files; } - public override IEnumerable CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile) + public override IEnumerable CreateAfterMovieImport(Movie movie, MovieFile movieFile) { var files = new List(); foreach (var consumer in _metadataFactory.Enabled()) { - files.AddIfNotNull(ProcessEpisodeMetadata(consumer, series, episodeFile, new List())); - files.AddRange(ProcessEpisodeImages(consumer, series, episodeFile, new List())); + files.AddIfNotNull(ProcessMovieMetadata(consumer, movie, movieFile, new List())); + files.AddRange(ProcessMovieImages(consumer, movie, movieFile, new List())); } _metadataFileService.Upsert(files); @@ -98,41 +94,9 @@ public override IEnumerable CreateAfterEpisodeImport(Series series, E return files; } - public override IEnumerable CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder) + public override IEnumerable MoveFilesAfterRename(Movie movie, List movieFiles) { - var metadataFiles = _metadataFileService.GetFilesBySeries(series.Id); - - if (seriesFolder.IsNullOrWhiteSpace() && seasonFolder.IsNullOrWhiteSpace()) - { - return new List(); - } - - var files = new List(); - - foreach (var consumer in _metadataFactory.Enabled()) - { - var consumerFiles = GetMetadataFilesForConsumer(consumer, metadataFiles); - - if (seriesFolder.IsNotNullOrWhiteSpace()) - { - files.AddIfNotNull(ProcessSeriesMetadata(consumer, series, consumerFiles)); - files.AddRange(ProcessSeriesImages(consumer, series, consumerFiles)); - } - - if (seasonFolder.IsNotNullOrWhiteSpace()) - { - files.AddRange(ProcessSeasonImages(consumer, series, consumerFiles)); - } - } - - _metadataFileService.Upsert(files); - - return files; - } - - public override IEnumerable MoveFilesAfterRename(Series series, List episodeFiles) - { - var metadataFiles = _metadataFileService.GetFilesBySeries(series.Id); + var metadataFiles = _metadataFileService.GetFilesByMovie(movie.Id); var movedFiles = new List(); // TODO: Move EpisodeImage and EpisodeMetadata metadata files, instead of relying on consumers to do it @@ -140,26 +104,26 @@ public override IEnumerable MoveFilesAfterRename(Series series, List< foreach (var consumer in _metadataFactory.GetAvailableProviders()) { - foreach (var episodeFile in episodeFiles) + foreach (var movieFile in movieFiles) { - var metadataFilesForConsumer = GetMetadataFilesForConsumer(consumer, metadataFiles).Where(m => m.EpisodeFileId == episodeFile.Id).ToList(); + var metadataFilesForConsumer = GetMetadataFilesForConsumer(consumer, metadataFiles).Where(m => m.MovieFileId == movieFile.Id).ToList(); foreach (var metadataFile in metadataFilesForConsumer) { - var newFileName = consumer.GetFilenameAfterMove(series, episodeFile, metadataFile); - var existingFileName = Path.Combine(series.Path, metadataFile.RelativePath); + var newFileName = consumer.GetFilenameAfterMove(movie, movieFile, metadataFile); + var existingFileName = Path.Combine(movie.Path, metadataFile.RelativePath); if (newFileName.PathNotEquals(existingFileName)) { try { _diskProvider.MoveFile(existingFileName, newFileName); - metadataFile.RelativePath = series.Path.GetRelativePath(newFileName); + metadataFile.RelativePath = movie.Path.GetRelativePath(newFileName); movedFiles.Add(metadataFile); } catch (Exception ex) { - _logger.Warn(ex, "Unable to move metadata file: {0}", existingFileName); + _logger.Warn(ex, "Unable to move metadata file after rename: {0}", existingFileName); } } } @@ -171,94 +135,50 @@ public override IEnumerable MoveFilesAfterRename(Series series, List< return movedFiles; } - public override ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly) + public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly) { return null; } - private List GetMetadataFilesForConsumer(IMetadata consumer, List seriesMetadata) + private List GetMetadataFilesForConsumer(IMetadata consumer, List movieMetadata) { - return seriesMetadata.Where(c => c.Consumer == consumer.GetType().Name).ToList(); + return movieMetadata.Where(c => c.Consumer == consumer.GetType().Name).ToList(); } - private MetadataFile ProcessSeriesMetadata(IMetadata consumer, Series series, List existingMetadataFiles) + private MetadataFile ProcessMovieMetadata(IMetadata consumer, Movie movie, MovieFile movieFile, List existingMetadataFiles) { - var seriesMetadata = consumer.SeriesMetadata(series); + var movieFileMetadata = consumer.MovieMetadata(movie, movieFile); - if (seriesMetadata == null) + if (movieFileMetadata == null) { return null; } - var hash = seriesMetadata.Contents.SHA256Hash(); + var fullPath = Path.Combine(movie.Path, movieFileMetadata.RelativePath); - var metadata = GetMetadataFile(series, existingMetadataFiles, e => e.Type == MetadataType.SeriesMetadata) ?? - new MetadataFile - { - SeriesId = series.Id, - Consumer = consumer.GetType().Name, - Type = MetadataType.SeriesMetadata - }; - - if (hash == metadata.Hash) - { - if (seriesMetadata.RelativePath != metadata.RelativePath) - { - metadata.RelativePath = seriesMetadata.RelativePath; - - return metadata; - } - - return null; - } - - var fullPath = Path.Combine(series.Path, seriesMetadata.RelativePath); - - _logger.Debug("Writing Series Metadata to: {0}", fullPath); - SaveMetadataFile(fullPath, seriesMetadata.Contents); - - metadata.Hash = hash; - metadata.RelativePath = seriesMetadata.RelativePath; - metadata.Extension = Path.GetExtension(fullPath); - - return metadata; - } - - private MetadataFile ProcessEpisodeMetadata(IMetadata consumer, Series series, EpisodeFile episodeFile, List existingMetadataFiles) - { - var episodeMetadata = consumer.EpisodeMetadata(series, episodeFile); - - if (episodeMetadata == null) - { - return null; - } - - var fullPath = Path.Combine(series.Path, episodeMetadata.RelativePath); - - var existingMetadata = GetMetadataFile(series, existingMetadataFiles, c => c.Type == MetadataType.EpisodeMetadata && - c.EpisodeFileId == episodeFile.Id); + var existingMetadata = GetMetadataFile(movie, existingMetadataFiles, c => c.Type == MetadataType.MovieMetadata && + c.MovieFileId == movieFile.Id); if (existingMetadata != null) { - var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath); + var existingFullPath = Path.Combine(movie.Path, existingMetadata.RelativePath); if (fullPath.PathNotEquals(existingFullPath)) { _diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move); - existingMetadata.RelativePath = episodeMetadata.RelativePath; + existingMetadata.RelativePath = movieFileMetadata.RelativePath; } } - var hash = episodeMetadata.Contents.SHA256Hash(); + var hash = movieFileMetadata.Contents.SHA256Hash(); var metadata = existingMetadata ?? new MetadataFile { - SeriesId = series.Id, - SeasonNumber = episodeFile.SeasonNumber, - EpisodeFileId = episodeFile.Id, + MovieId = movie.Id, + MovieFileId = movieFile.Id, Consumer = consumer.GetType().Name, - Type = MetadataType.EpisodeMetadata, - RelativePath = episodeMetadata.RelativePath, + Type = MetadataType.MovieMetadata, + RelativePath = movieFileMetadata.RelativePath, Extension = Path.GetExtension(fullPath) }; @@ -267,105 +187,34 @@ private MetadataFile ProcessEpisodeMetadata(IMetadata consumer, Series series, E return null; } - _logger.Debug("Writing Episode Metadata to: {0}", fullPath); - SaveMetadataFile(fullPath, episodeMetadata.Contents); + _logger.Debug("Writing Movie File Metadata to: {0}", fullPath); + SaveMetadataFile(fullPath, movieFileMetadata.Contents); metadata.Hash = hash; return metadata; } - - private List ProcessSeriesImages(IMetadata consumer, Series series, List existingMetadataFiles) + + private List ProcessMovieImages(IMetadata consumer, Movie movie, MovieFile movieFile, List existingMetadataFiles) { var result = new List(); - foreach (var image in consumer.SeriesImages(series)) + foreach (var image in consumer.MovieImages(movie, movieFile)) { - var fullPath = Path.Combine(series.Path, image.RelativePath); + var fullPath = Path.Combine(movie.Path, image.RelativePath); if (_diskProvider.FileExists(fullPath)) { - _logger.Debug("Series image already exists: {0}", fullPath); + _logger.Debug("Movie image already exists: {0}", fullPath); continue; } - var metadata = GetMetadataFile(series, existingMetadataFiles, c => c.Type == MetadataType.SeriesImage && - c.RelativePath == image.RelativePath) ?? - new MetadataFile - { - SeriesId = series.Id, - Consumer = consumer.GetType().Name, - Type = MetadataType.SeriesImage, - RelativePath = image.RelativePath, - Extension = Path.GetExtension(fullPath) - }; - - DownloadImage(series, image); - - result.Add(metadata); - } - - return result; - } - - private List ProcessSeasonImages(IMetadata consumer, Series series, List existingMetadataFiles) - { - var result = new List(); - - foreach (var season in series.Seasons) - { - foreach (var image in consumer.SeasonImages(series, season)) - { - var fullPath = Path.Combine(series.Path, image.RelativePath); - - if (_diskProvider.FileExists(fullPath)) - { - _logger.Debug("Season image already exists: {0}", fullPath); - continue; - } - - var metadata = GetMetadataFile(series, existingMetadataFiles, c => c.Type == MetadataType.SeasonImage && - c.SeasonNumber == season.SeasonNumber && - c.RelativePath == image.RelativePath) ?? - new MetadataFile - { - SeriesId = series.Id, - SeasonNumber = season.SeasonNumber, - Consumer = consumer.GetType().Name, - Type = MetadataType.SeasonImage, - RelativePath = image.RelativePath, - Extension = Path.GetExtension(fullPath) - }; - - DownloadImage(series, image); - - result.Add(metadata); - } - } - - return result; - } - - private List ProcessEpisodeImages(IMetadata consumer, Series series, EpisodeFile episodeFile, List existingMetadataFiles) - { - var result = new List(); - - foreach (var image in consumer.EpisodeImages(series, episodeFile)) - { - var fullPath = Path.Combine(series.Path, image.RelativePath); - - if (_diskProvider.FileExists(fullPath)) - { - _logger.Debug("Episode image already exists: {0}", fullPath); - continue; - } - - var existingMetadata = GetMetadataFile(series, existingMetadataFiles, c => c.Type == MetadataType.EpisodeImage && - c.EpisodeFileId == episodeFile.Id); + var existingMetadata = GetMetadataFile(movie, existingMetadataFiles, c => c.Type == MetadataType.MovieImage && + c.RelativePath == image.RelativePath); if (existingMetadata != null) { - var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath); + var existingFullPath = Path.Combine(movie.Path, existingMetadata.RelativePath); if (fullPath.PathNotEquals(existingFullPath)) { _diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move); @@ -378,16 +227,15 @@ private List ProcessEpisodeImages(IMetadata consumer, Series serie var metadata = existingMetadata ?? new MetadataFile { - SeriesId = series.Id, - SeasonNumber = episodeFile.SeasonNumber, - EpisodeFileId = episodeFile.Id, + MovieId = movie.Id, + MovieFileId = movieFile.Id, Consumer = consumer.GetType().Name, - Type = MetadataType.EpisodeImage, + Type = MetadataType.MovieImage, RelativePath = image.RelativePath, Extension = Path.GetExtension(fullPath) }; - DownloadImage(series, image); + DownloadImage(movie, image); result.Add(metadata); } @@ -395,9 +243,9 @@ private List ProcessEpisodeImages(IMetadata consumer, Series serie return result; } - private void DownloadImage(Series series, ImageFileResult image) + private void DownloadImage(Movie movie, ImageFileResult image) { - var fullPath = Path.Combine(series.Path, image.RelativePath); + var fullPath = Path.Combine(movie.Path, image.RelativePath); try { @@ -413,11 +261,11 @@ private void DownloadImage(Series series, ImageFileResult image) } catch (WebException ex) { - _logger.Warn(ex, "Couldn't download image {0} for {1}. {2}", image.Url, series, ex.Message); + _logger.Warn(ex, "Couldn't download image {0} for {1}. {2}", image.Url, movie, ex.Message); } catch (Exception ex) { - _logger.Error(ex, "Couldn't download image {0} for {1}. {2}", image.Url, series, ex.Message); + _logger.Error(ex, "Couldn't download image {0} for {1}. {2}", image.Url, movie, ex.Message); } } @@ -427,7 +275,7 @@ private void SaveMetadataFile(string path, string contents) _mediaFileAttributeService.SetFilePermissions(path); } - private MetadataFile GetMetadataFile(Series series, List existingMetadataFiles, Func predicate) + private MetadataFile GetMetadataFile(Movie movie, List existingMetadataFiles, Func predicate) { var matchingMetadataFiles = existingMetadataFiles.Where(predicate).ToList(); @@ -439,7 +287,7 @@ private MetadataFile GetMetadataFile(Series series, List existingM //Remove duplicate metadata files from DB and disk foreach (var file in matchingMetadataFiles.Skip(1)) { - var path = Path.Combine(series.Path, file.RelativePath); + var path = Path.Combine(movie.Path, file.RelativePath); _logger.Debug("Removing duplicate Metadata file: {0}", path); diff --git a/src/NzbDrone.Core/Extras/Metadata/MetadataType.cs b/src/NzbDrone.Core/Extras/Metadata/MetadataType.cs index 849bc31dd..bd20a8f9d 100644 --- a/src/NzbDrone.Core/Extras/Metadata/MetadataType.cs +++ b/src/NzbDrone.Core/Extras/Metadata/MetadataType.cs @@ -1,12 +1,9 @@ -namespace NzbDrone.Core.Extras.Metadata +namespace NzbDrone.Core.Extras.Metadata { public enum MetadataType { Unknown = 0, - SeriesMetadata = 1, - EpisodeMetadata = 2, - SeriesImage = 3, - SeasonImage = 4, - EpisodeImage = 5 + MovieMetadata = 1, + MovieImage = 2 } } diff --git a/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs b/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs index 6315daeb1..908ea5063 100644 --- a/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs +++ b/src/NzbDrone.Core/Extras/Others/ExistingOtherExtraImporter.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; @@ -27,42 +27,43 @@ public ExistingOtherExtraImporter(IExtraFileService otherExtraFi public override int Order => 2; - public override IEnumerable ProcessFiles(Series series, List filesOnDisk, List importedFiles) + public override IEnumerable ProcessFiles(Movie movie, List filesOnDisk, List importedFiles) { - _logger.Debug("Looking for existing extra files in {0}", series.Path); + _logger.Debug("Looking for existing extra files in {0}", movie.Path); var extraFiles = new List(); - var filterResult = FilterAndClean(series, filesOnDisk, importedFiles); + var filterResult = FilterAndClean(movie, filesOnDisk, importedFiles); foreach (var possibleExtraFile in filterResult.FilesOnDisk) { - var localEpisode = _parsingService.GetLocalEpisode(possibleExtraFile, series); + var extension = Path.GetExtension(possibleExtraFile); - if (localEpisode == null) + if (extension.IsNullOrWhiteSpace()) + { + _logger.Debug("No extension for file: {0}", possibleExtraFile); + continue; + } + + var localMovie = _parsingService.GetLocalMovie(possibleExtraFile, movie); + + if (localMovie == null) { _logger.Debug("Unable to parse extra file: {0}", possibleExtraFile); continue; } - if (localEpisode.Episodes.Empty()) + if (localMovie.Movie == null) { - _logger.Debug("Cannot find related episodes for: {0}", possibleExtraFile); - continue; - } - - if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1) - { - _logger.Debug("Extra file: {0} does not match existing files.", possibleExtraFile); + _logger.Debug("Cannot find related movie for: {0}", possibleExtraFile); continue; } var extraFile = new OtherExtraFile { - SeriesId = series.Id, - SeasonNumber = localEpisode.SeasonNumber, - EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId, - RelativePath = series.Path.GetRelativePath(possibleExtraFile), - Extension = Path.GetExtension(possibleExtraFile) + MovieId = movie.Id, + MovieFileId = localMovie.Movie.MovieFileId, + RelativePath = movie.Path.GetRelativePath(possibleExtraFile), + Extension = extension }; extraFiles.Add(extraFile); diff --git a/src/NzbDrone.Core/Extras/Others/OtherExtraFileService.cs b/src/NzbDrone.Core/Extras/Others/OtherExtraFileService.cs index ceeb15ff8..f54913df3 100644 --- a/src/NzbDrone.Core/Extras/Others/OtherExtraFileService.cs +++ b/src/NzbDrone.Core/Extras/Others/OtherExtraFileService.cs @@ -1,4 +1,4 @@ -using NLog; +using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Extras.Files; using NzbDrone.Core.MediaFiles; @@ -12,8 +12,8 @@ public interface IOtherExtraFileService : IExtraFileService public class OtherExtraFileService : ExtraFileService, IOtherExtraFileService { - public OtherExtraFileService(IExtraFileRepository repository, ISeriesService seriesService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger) - : base(repository, seriesService, diskProvider, recycleBinProvider, logger) + public OtherExtraFileService(IExtraFileRepository repository, IMovieService movieService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger) + : base(repository, movieService, diskProvider, recycleBinProvider, logger) { } } diff --git a/src/NzbDrone.Core/Extras/Others/OtherExtraService.cs b/src/NzbDrone.Core/Extras/Others/OtherExtraService.cs index 71b635710..8e7dbdc59 100644 --- a/src/NzbDrone.Core/Extras/Others/OtherExtraService.cs +++ b/src/NzbDrone.Core/Extras/Others/OtherExtraService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,71 +15,41 @@ namespace NzbDrone.Core.Extras.Others public class OtherExtraService : ExtraFileManager { private readonly IOtherExtraFileService _otherExtraFileService; - private readonly IDiskProvider _diskProvider; - private readonly Logger _logger; public OtherExtraService(IConfigService configService, + IDiskProvider diskProvider, IDiskTransferService diskTransferService, IOtherExtraFileService otherExtraFileService, - IDiskProvider diskProvider, Logger logger) - : base(configService, diskTransferService, otherExtraFileService) + : base(configService, diskProvider, diskTransferService, logger) { _otherExtraFileService = otherExtraFileService; - _diskProvider = diskProvider; - _logger = logger; } public override int Order => 2; - public override IEnumerable CreateAfterSeriesScan(Series series, List episodeFiles) + public override IEnumerable CreateAfterMovieScan(Movie movie, List movieFiles) { return Enumerable.Empty(); } - public override IEnumerable CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile) + public override IEnumerable CreateAfterMovieImport(Movie movie, MovieFile movieFile) { return Enumerable.Empty(); } - public override IEnumerable CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder) - { - return Enumerable.Empty(); - } - - public override IEnumerable MoveFilesAfterRename(Series series, List episodeFiles) - { - // TODO: Remove - // We don't want to move files after rename yet. - - return Enumerable.Empty(); - - var extraFiles = _otherExtraFileService.GetFilesBySeries(series.Id); + public override IEnumerable MoveFilesAfterRename(Movie movie, List movieFiles) + { + var extraFiles = _otherExtraFileService.GetFilesByMovie(movie.Id); var movedFiles = new List(); - foreach (var episodeFile in episodeFiles) + foreach (var movieFile in movieFiles) { - var extraFilesForEpisodeFile = extraFiles.Where(m => m.EpisodeFileId == episodeFile.Id).ToList(); + var extraFilesForMovieFile = extraFiles.Where(m => m.MovieFileId == movieFile.Id).ToList(); - foreach (var extraFile in extraFilesForEpisodeFile) + foreach (var extraFile in extraFilesForMovieFile) { - var existingFileName = Path.Combine(series.Path, extraFile.RelativePath); - var extension = Path.GetExtension(existingFileName).TrimStart('.'); - var newFileName = Path.ChangeExtension(Path.Combine(series.Path, episodeFile.RelativePath), extension); - - if (newFileName.PathNotEquals(existingFileName)) - { - try - { - _diskProvider.MoveFile(existingFileName, newFileName); - extraFile.RelativePath = series.Path.GetRelativePath(newFileName); - movedFiles.Add(extraFile); - } - catch (Exception ex) - { - _logger.Warn(ex, "Unable to move extra file: {0}", existingFileName); - } - } + movedFiles.AddIfNotNull(MoveFile(movie, movieFile, extraFile)); } } @@ -88,15 +58,15 @@ public override IEnumerable MoveFilesAfterRename(Series series, List< return movedFiles; } - public override ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly) + public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly) { // If the extension is .nfo we need to change it to .nfo-orig - if (Path.GetExtension(path).Equals(".nfo")) + if (Path.GetExtension(path).Equals(".nfo", StringComparison.OrdinalIgnoreCase)) { extension += "-orig"; } - var extraFile = ImportFile(series, episodeFile, path, extension, readOnly); + var extraFile = ImportFile(movie, movieFile, path, readOnly, extension, null); _otherExtraFileService.Upsert(extraFile); diff --git a/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs b/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs index d3ae8d46b..73a36e8c8 100644 --- a/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs +++ b/src/NzbDrone.Core/Extras/Subtitles/ExistingSubtitleImporter.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; @@ -27,12 +27,12 @@ public ExistingSubtitleImporter(IExtraFileService subtitleFileServ public override int Order => 1; - public override IEnumerable ProcessFiles(Series series, List filesOnDisk, List importedFiles) + public override IEnumerable ProcessFiles(Movie movie, List filesOnDisk, List importedFiles) { - _logger.Debug("Looking for existing subtitle files in {0}", series.Path); + _logger.Debug("Looking for existing subtitle files in {0}", movie.Path); var subtitleFiles = new List(); - var filterResult = FilterAndClean(series, filesOnDisk, importedFiles); + var filterResult = FilterAndClean(movie, filesOnDisk, importedFiles); foreach (var possibleSubtitleFile in filterResult.FilesOnDisk) { @@ -40,32 +40,25 @@ public override IEnumerable ProcessFiles(Series series, List if (SubtitleFileExtensions.Extensions.Contains(extension)) { - var localEpisode = _parsingService.GetLocalEpisode(possibleSubtitleFile, series); + var localMovie = _parsingService.GetLocalMovie(possibleSubtitleFile, movie); - if (localEpisode == null) + if (localMovie == null) { _logger.Debug("Unable to parse subtitle file: {0}", possibleSubtitleFile); continue; } - if (localEpisode.Episodes.Empty()) + if (localMovie.Movie == null) { - _logger.Debug("Cannot find related episodes for: {0}", possibleSubtitleFile); - continue; - } - - if (localEpisode.Episodes.DistinctBy(e => e.EpisodeFileId).Count() > 1) - { - _logger.Debug("Subtitle file: {0} does not match existing files.", possibleSubtitleFile); + _logger.Debug("Cannot find related movie for: {0}", possibleSubtitleFile); continue; } var subtitleFile = new SubtitleFile { - SeriesId = series.Id, - SeasonNumber = localEpisode.SeasonNumber, - EpisodeFileId = localEpisode.Episodes.First().EpisodeFileId, - RelativePath = series.Path.GetRelativePath(possibleSubtitleFile), + MovieId = movie.Id, + MovieFileId = localMovie.Movie.MovieFileId, + RelativePath = movie.Path.GetRelativePath(possibleSubtitleFile), Language = LanguageParser.ParseSubtitleLanguage(possibleSubtitleFile), Extension = extension }; diff --git a/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileExtensions.cs b/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileExtensions.cs index 423d14656..69d91e028 100644 --- a/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileExtensions.cs +++ b/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace NzbDrone.Core.Extras.Subtitles { @@ -8,7 +9,7 @@ public static class SubtitleFileExtensions static SubtitleFileExtensions() { - _fileExtensions = new HashSet + _fileExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { ".aqt", ".ass", diff --git a/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileService.cs b/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileService.cs index ac7d4da2b..47fc550ae 100644 --- a/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileService.cs +++ b/src/NzbDrone.Core/Extras/Subtitles/SubtitleFileService.cs @@ -1,4 +1,4 @@ -using NLog; +using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Extras.Files; using NzbDrone.Core.MediaFiles; @@ -12,8 +12,8 @@ public interface ISubtitleFileService : IExtraFileService public class SubtitleFileService : ExtraFileService, ISubtitleFileService { - public SubtitleFileService(IExtraFileRepository repository, ISeriesService seriesService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger) - : base(repository, seriesService, diskProvider, recycleBinProvider, logger) + public SubtitleFileService(IExtraFileRepository repository, IMovieService movieService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger) + : base(repository, movieService, diskProvider, recycleBinProvider, logger) { } } diff --git a/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs b/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs index 639775048..23b63a145 100644 --- a/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs +++ b/src/NzbDrone.Core/Extras/Subtitles/SubtitleService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,83 +17,56 @@ namespace NzbDrone.Core.Extras.Subtitles public class SubtitleService : ExtraFileManager { private readonly ISubtitleFileService _subtitleFileService; - private readonly IDiskProvider _diskProvider; private readonly Logger _logger; public SubtitleService(IConfigService configService, + IDiskProvider diskProvider, IDiskTransferService diskTransferService, ISubtitleFileService subtitleFileService, - IDiskProvider diskProvider, Logger logger) - : base(configService, diskTransferService, subtitleFileService) + : base(configService, diskProvider, diskTransferService, logger) { _subtitleFileService = subtitleFileService; - _diskProvider = diskProvider; _logger = logger; } public override int Order => 1; - public override IEnumerable CreateAfterSeriesScan(Series series, List episodeFiles) + public override IEnumerable CreateAfterMovieScan(Movie movie, List movieFiles) { return Enumerable.Empty(); } - public override IEnumerable CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile) + public override IEnumerable CreateAfterMovieImport(Movie movie, MovieFile movieFile) { return Enumerable.Empty(); } - public override IEnumerable CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder) + public override IEnumerable MoveFilesAfterRename(Movie movie, List movieFiles) { - return Enumerable.Empty(); - } - - public override IEnumerable MoveFilesAfterRename(Series series, List episodeFiles) - { - // TODO: Remove - // We don't want to move files after rename yet. - - return Enumerable.Empty(); - - var subtitleFiles = _subtitleFileService.GetFilesBySeries(series.Id); + var subtitleFiles = _subtitleFileService.GetFilesByMovie(movie.Id); var movedFiles = new List(); - foreach (var episodeFile in episodeFiles) + foreach (var movieFile in movieFiles) { - var groupedExtraFilesForEpisodeFile = subtitleFiles.Where(m => m.EpisodeFileId == episodeFile.Id) + var groupedExtraFilesForMovieFile = subtitleFiles.Where(m => m.MovieFileId == movieFile.Id) .GroupBy(s => s.Language + s.Extension).ToList(); - foreach (var group in groupedExtraFilesForEpisodeFile) + foreach (var group in groupedExtraFilesForMovieFile) { var groupCount = group.Count(); var copy = 1; if (groupCount > 1) { - _logger.Warn("Multiple subtitle files found with the same language and extension for {0}", Path.Combine(series.Path, episodeFile.RelativePath)); + _logger.Warn("Multiple subtitle files found with the same language and extension for {0}", Path.Combine(movie.Path, movieFile.RelativePath)); } - foreach (var extraFile in group) + foreach (var subtitleFile in group) { - var existingFileName = Path.Combine(series.Path, extraFile.RelativePath); - var extension = GetExtension(extraFile, existingFileName, copy, groupCount > 1); - var newFileName = Path.ChangeExtension(Path.Combine(series.Path, episodeFile.RelativePath), extension); - - if (newFileName.PathNotEquals(existingFileName)) - { - try - { - _diskProvider.MoveFile(existingFileName, newFileName); - extraFile.RelativePath = series.Path.GetRelativePath(newFileName); - movedFiles.Add(extraFile); - } - catch (Exception ex) - { - _logger.Warn(ex, "Unable to move subtitle file: {0}", existingFileName); - } - } + var suffix = GetSuffix(subtitleFile.Language, copy, groupCount > 1); + movedFiles.AddIfNotNull(MoveFile(movie, movieFile, subtitleFile, suffix)); copy++; } @@ -105,12 +78,14 @@ public override IEnumerable MoveFilesAfterRename(Series series, List< return movedFiles; } - public override ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly) + public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly) { if (SubtitleFileExtensions.Extensions.Contains(Path.GetExtension(path))) { - var subtitleFile = ImportFile(series, episodeFile, path, extension, readOnly); - subtitleFile.Language = LanguageParser.ParseSubtitleLanguage(path); + var language = LanguageParser.ParseSubtitleLanguage(path); + var suffix = GetSuffix(language, 1, false); + var subtitleFile = ImportFile(movie, movieFile, path, readOnly, extension, suffix); + subtitleFile.Language = language; _subtitleFileService.Upsert(subtitleFile); @@ -120,26 +95,23 @@ public override ExtraFile Import(Series series, EpisodeFile episodeFile, string return null; } - private string GetExtension(SubtitleFile extraFile, string existingFileName, int copy, bool multipleCopies = false) + private string GetSuffix(Language language, int copy, bool multipleCopies = false) { - var fileExtension = Path.GetExtension(existingFileName); - var extensionBuilder = new StringBuilder(); + var suffixBuilder = new StringBuilder(); if (multipleCopies) { - extensionBuilder.Append(copy); - extensionBuilder.Append("."); + suffixBuilder.Append("."); + suffixBuilder.Append(copy); } - if (extraFile.Language != Language.Unknown) + if (language != Language.Unknown) { - extensionBuilder.Append(IsoLanguages.Get(extraFile.Language).TwoLetterCode); - extensionBuilder.Append("."); + suffixBuilder.Append("."); + suffixBuilder.Append(IsoLanguages.Get(language).TwoLetterCode); } - extensionBuilder.Append(fileExtension.TrimStart('.')); - - return extensionBuilder.ToString(); + return suffixBuilder.ToString(); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs index e65a117a1..3373fb4b7 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupDuplicateMetadataFiles.cs @@ -1,4 +1,4 @@ -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,12 +13,11 @@ public CleanupDuplicateMetadataFiles(IMainDatabase database) public void Clean() { - DeleteDuplicateSeriesMetadata(); - DeleteDuplicateEpisodeMetadata(); - DeleteDuplicateEpisodeImages(); + DeleteDuplicateMovieMetadata(); + DeleteDuplicateMovieFileMetadata(); } - private void DeleteDuplicateSeriesMetadata() + private void DeleteDuplicateMovieMetadata() { var mapper = _database.GetDataMapper(); @@ -26,34 +25,21 @@ private void DeleteDuplicateSeriesMetadata() WHERE Id IN ( SELECT Id FROM MetadataFiles WHERE Type = 1 - GROUP BY SeriesId, Consumer - HAVING COUNT(SeriesId) > 1 + GROUP BY MovieId, Consumer + HAVING COUNT(MovieId) > 1 )"); } - private void DeleteDuplicateEpisodeMetadata() + private void DeleteDuplicateMovieFileMetadata() { var mapper = _database.GetDataMapper(); mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT Id FROM MetadataFiles - WHERE Type = 2 - GROUP BY EpisodeFileId, Consumer - HAVING COUNT(EpisodeFileId) > 1 - )"); - } - - private void DeleteDuplicateEpisodeImages() - { - var mapper = _database.GetDataMapper(); - - mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles - WHERE Id IN ( - SELECT Id FROM MetadataFiles - WHERE Type = 5 - GROUP BY EpisodeFileId, Consumer - HAVING COUNT(EpisodeFileId) > 1 + WHERE Type = 1 + GROUP BY MovieFileId, Consumer + HAVING COUNT(MovieFileId) > 1 )"); } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs index 05ab54ea1..ae6b48495 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedMetadataFiles.cs @@ -1,4 +1,4 @@ -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Housekeeping.Housekeepers { @@ -13,45 +13,45 @@ public CleanupOrphanedMetadataFiles(IMainDatabase database) public void Clean() { - DeleteOrphanedBySeries(); - DeleteOrphanedByEpisodeFile(); - DeleteWhereEpisodeFileIsZero(); + DeleteOrphanedByMovie(); + DeleteOrphanedByMovieFile(); + DeleteWhereMovieFileIsZero(); } - private void DeleteOrphanedBySeries() + private void DeleteOrphanedByMovie() { var mapper = _database.GetDataMapper(); mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT MetadataFiles.Id FROM MetadataFiles - LEFT OUTER JOIN Series - ON MetadataFiles.SeriesId = Series.Id - WHERE Series.Id IS NULL)"); + LEFT OUTER JOIN Movies + ON MetadataFiles.MovieId = Movies.Id + WHERE Movies.Id IS NULL)"); } - private void DeleteOrphanedByEpisodeFile() + private void DeleteOrphanedByMovieFile() { var mapper = _database.GetDataMapper(); mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT MetadataFiles.Id FROM MetadataFiles - LEFT OUTER JOIN EpisodeFiles - ON MetadataFiles.EpisodeFileId = EpisodeFiles.Id - WHERE MetadataFiles.EpisodeFileId > 0 - AND EpisodeFiles.Id IS NULL)"); + LEFT OUTER JOIN MovieFiles + ON MetadataFiles.MovieFileId = MovieFiles.Id + WHERE MetadataFiles.MovieFileId > 0 + AND MovieFiles.Id IS NULL)"); } - private void DeleteWhereEpisodeFileIsZero() + private void DeleteWhereMovieFileIsZero() { var mapper = _database.GetDataMapper(); mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles WHERE Id IN ( SELECT Id FROM MetadataFiles - WHERE Type IN (2, 5) - AND EpisodeFileId = 0)"); + WHERE Type IN (1, 2) + AND MovieFileId = 0)"); } } } diff --git a/src/NzbDrone.Core/Housekeeping/Housekeepers/DeleteBadMediaCovers.cs b/src/NzbDrone.Core/Housekeeping/Housekeepers/DeleteBadMediaCovers.cs index f1744abec..ccec4d7ff 100644 --- a/src/NzbDrone.Core/Housekeeping/Housekeepers/DeleteBadMediaCovers.cs +++ b/src/NzbDrone.Core/Housekeeping/Housekeepers/DeleteBadMediaCovers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using NLog; @@ -12,19 +12,19 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers public class DeleteBadMediaCovers : IHousekeepingTask { private readonly IMetadataFileService _metaFileService; - private readonly ISeriesService _seriesService; + private readonly IMovieService _movieService; private readonly IDiskProvider _diskProvider; private readonly IConfigService _configService; private readonly Logger _logger; public DeleteBadMediaCovers(IMetadataFileService metaFileService, - ISeriesService seriesService, + IMovieService movieService, IDiskProvider diskProvider, IConfigService configService, Logger logger) { _metaFileService = metaFileService; - _seriesService = seriesService; + _movieService = movieService; _diskProvider = diskProvider; _configService = configService; _logger = logger; @@ -34,18 +34,18 @@ public void Clean() { if (!_configService.CleanupMetadataImages) return; - var series = _seriesService.GetAllSeries(); + var movies = _movieService.GetAllMovies(); - foreach (var show in series) + foreach (var movie in movies) { - var images = _metaFileService.GetFilesBySeries(show.Id) + var images = _metaFileService.GetFilesByMovie(movie.Id) .Where(c => c.LastUpdated > new DateTime(2014, 12, 27) && c.RelativePath.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase)); foreach (var image in images) { try { - var path = Path.Combine(show.Path, image.RelativePath); + var path = Path.Combine(movie.Path, image.RelativePath); if (!IsValid(path)) { _logger.Debug("Deleting invalid image file " + path); @@ -84,4 +84,4 @@ private bool IsValid(string path) return !text.ToLowerInvariant().Contains("html"); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs index a4a326ad7..0dd8ec87f 100644 --- a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs +++ b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -26,6 +26,7 @@ public interface IDiskScanService string[] GetVideoFiles(string path, bool allDirectories = true); string[] GetNonVideoFiles(string path, bool allDirectories = true); List FilterFiles(Series series, IEnumerable files); + List FilterFiles(Movie series, IEnumerable files); } public class DiskScanService : @@ -213,7 +214,7 @@ public string[] GetVideoFiles(string path, bool allDirectories = true) var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; var filesOnDisk = _diskProvider.GetFiles(path, searchOption); - var mediaFileList = filesOnDisk.Where(file => MediaFileExtensions.Extensions.Contains(Path.GetExtension(file).ToLower())) + var mediaFileList = filesOnDisk.Where(file => MediaFileExtensions.Extensions.Contains(Path.GetExtension(file))) .ToList(); _logger.Debug("{0} video files were found in {1}", mediaFileList.Count, path); @@ -227,7 +228,7 @@ public string[] GetNonVideoFiles(string path, bool allDirectories = true) var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; var filesOnDisk = _diskProvider.GetFiles(path, searchOption); - var mediaFileList = filesOnDisk.Where(file => !MediaFileExtensions.Extensions.Contains(Path.GetExtension(file).ToLower())) + var mediaFileList = filesOnDisk.Where(file => !MediaFileExtensions.Extensions.Contains(Path.GetExtension(file))) .ToList(); _logger.Debug("{0} non-video files were found in {1}", mediaFileList.Count, path); @@ -267,7 +268,7 @@ private void SetPermissions(string path) _logger.Warn(ex, "Unable to apply permissions to: " + path); _logger.Debug(ex, ex.Message); } - } + } public void Handle(SeriesUpdatedEvent message) { diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs index 7a1a3bcca..35f3451e1 100644 --- a/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/DownloadedMovieImportService.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; @@ -106,7 +107,7 @@ public List ProcessPath(string path, ImportMode importMode = Impor public bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie) { var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName); - var rarFiles = _diskProvider.GetFiles(directoryInfo.FullName, SearchOption.AllDirectories).Where(f => Path.GetExtension(f) == ".rar"); + var rarFiles = _diskProvider.GetFiles(directoryInfo.FullName, SearchOption.AllDirectories).Where(f => Path.GetExtension(f).Equals(".rar", StringComparison.OrdinalIgnoreCase)); foreach (var videoFile in videoFiles) { diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index cdfd289db..6c9a435f6 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -117,7 +117,7 @@ public List Import(List decisions, bool newDownloa if (newDownload) { - _extraService.ImportExtraFiles(localEpisode, episodeFile, copyOnly); + // _extraService.ImportExtraFiles(localEpisode, episodeFile, copyOnly); } if (downloadClientItem != null) diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs index 10941e185..266e72069 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedMovie.cs @@ -120,7 +120,7 @@ public List Import(List decisions, bool newDownloa if (newDownload) { - //_extraService.ImportExtraFiles(localMovie, episodeFile, copyOnly); TODO update for movie + _extraService.ImportExtraFiles(localMovie, movieFile, copyOnly); } if (downloadClientItem != null) diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs b/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs index 6a951a3b9..c4ba2649c 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using NzbDrone.Core.Qualities; @@ -10,7 +11,7 @@ public static class MediaFileExtensions static MediaFileExtensions() { - _fileExtensions = new Dictionary + _fileExtensions = new Dictionary(StringComparer.OrdinalIgnoreCase) { //Unknown { ".webm", Quality.Unknown }, @@ -70,7 +71,7 @@ static MediaFileExtensions() }; } - public static HashSet Extensions => new HashSet(_fileExtensions.Keys); + public static HashSet Extensions => new HashSet(_fileExtensions.Keys, StringComparer.OrdinalIgnoreCase); public static Quality GetQualityForExtension(string extension) { diff --git a/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs index ad402c789..c32df8487 100644 --- a/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data.SQLite; using System.Linq; @@ -128,6 +128,11 @@ private void RenameFiles(List movieFiles, Movie movie, string oldMovi { _logger.Error(ex, "Failed to rename file: " + oldMovieFilePath); } + + if (renamed.Any()) + { + _eventAggregator.PublishEvent(new MovieRenamedEvent(movie)); + } } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 9fa82b371..bf3bb5215 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -125,6 +125,7 @@ + @@ -1378,11 +1379,7 @@ - - - - - + diff --git a/src/NzbDrone.Core/Tv/RefreshMovieService.cs b/src/NzbDrone.Core/Tv/RefreshMovieService.cs index 84c6f1ec5..3f2de1377 100644 --- a/src/NzbDrone.Core/Tv/RefreshMovieService.cs +++ b/src/NzbDrone.Core/Tv/RefreshMovieService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -24,33 +24,30 @@ public class RefreshMovieService : IExecute private readonly IProvideMovieInfo _movieInfo; private readonly IMovieService _movieService; private readonly IAlternativeTitleService _titleService; - private readonly IRefreshEpisodeService _refreshEpisodeService; private readonly IEventAggregator _eventAggregator; - private readonly IManageCommandQueue _commandQueueManager; + private readonly IManageCommandQueue _commandQueueManager; private readonly IDiskScanService _diskScanService; private readonly ICheckIfMovieShouldBeRefreshed _checkIfMovieShouldBeRefreshed; private readonly IRadarrAPIClient _apiClient; - + private readonly Logger _logger; public RefreshMovieService(IProvideMovieInfo movieInfo, IMovieService movieService, IAlternativeTitleService titleService, - IRefreshEpisodeService refreshEpisodeService, IEventAggregator eventAggregator, IDiskScanService diskScanService, IRadarrAPIClient apiClient, ICheckIfMovieShouldBeRefreshed checkIfMovieShouldBeRefreshed, - IManageCommandQueue commandQueue, + IManageCommandQueue commandQueue, Logger logger) { _movieInfo = movieInfo; _movieService = movieService; _titleService = titleService; - _refreshEpisodeService = refreshEpisodeService; _eventAggregator = eventAggregator; _apiClient = apiClient; - _commandQueueManager = commandQueue; + _commandQueueManager = commandQueue; _diskScanService = diskScanService; _checkIfMovieShouldBeRefreshed = checkIfMovieShouldBeRefreshed; _logger = logger; @@ -61,7 +58,7 @@ private void RefreshMovieInfo(Movie movie) _logger.ProgressInfo("Updating Info for {0}", movie.Title); Movie movieInfo; - + try { movieInfo = _movieInfo.GetMovieInfo(movie.TmdbId, movie.Profile, movie.HasPreDBEntry); @@ -99,7 +96,7 @@ private void RefreshMovieInfo(Movie movie) movie.PhysicalRelease = movieInfo.PhysicalRelease; movie.YouTubeTrailerId = movieInfo.YouTubeTrailerId; movie.Studio = movieInfo.Studio; - movie.HasPreDBEntry = movieInfo.HasPreDBEntry; + movie.HasPreDBEntry = movieInfo.HasPreDBEntry; try { @@ -121,12 +118,12 @@ private void RefreshMovieInfo(Movie movie) var mappingsTitles = mappings.Item1; movie.AlternativeTitles.AddRange(_titleService.AddAltTitles(movieInfo.AlternativeTitles, movie)); - + _titleService.DeleteNotEnoughVotes(mappingsTitles); mappingsTitles = mappingsTitles.ExceptBy(t => t.CleanTitle, movie.AlternativeTitles, t => t.CleanTitle, EqualityComparer.Default).ToList(); - + mappingsTitles = mappingsTitles.Where(t => t.Votes > 3).ToList(); @@ -151,7 +148,7 @@ private void RefreshMovieInfo(Movie movie) { _logger.Info(ex, "Unable to communicate with Mappings Server."); } - + _movieService.UpdateMovie(movie); @@ -177,7 +174,7 @@ public void Execute(RefreshMovieCommand message) if (message.MovieId.HasValue) { var movie = _movieService.GetMovie(message.MovieId.Value); - RefreshMovieInfo(movie); + RefreshMovieInfo(movie); } else { @@ -202,7 +199,7 @@ public void Execute(RefreshMovieCommand message) try { _logger.Info("Skipping refresh of movie: {0}", movie.Title); - _commandQueueManager.Push(new RenameMovieFolderCommand(new List{movie.Id})); + _commandQueueManager.Push(new RenameMovieFolderCommand(new List { movie.Id })); _diskScanService.Scan(movie); } catch (Exception e) diff --git a/src/UI/Cells/ExtraExtensionCell.js b/src/UI/Cells/ExtraExtensionCell.js new file mode 100644 index 000000000..dd85c20e8 --- /dev/null +++ b/src/UI/Cells/ExtraExtensionCell.js @@ -0,0 +1,14 @@ +var NzbDroneCell = require('./NzbDroneCell'); + +module.exports = NzbDroneCell.extend({ + className : 'extra-extension-cell', + + render : function() { + this.$el.empty(); + + var title = this.model.get('extension'); + this.$el.html(title); + + return this; + } +}); diff --git a/src/UI/Cells/ExtraTypeCell.js b/src/UI/Cells/ExtraTypeCell.js new file mode 100644 index 000000000..7841fa070 --- /dev/null +++ b/src/UI/Cells/ExtraTypeCell.js @@ -0,0 +1,19 @@ +var NzbDroneCell = require('./NzbDroneCell'); + +module.exports = NzbDroneCell.extend({ + className : 'extra-type-cell', + + render : function() { + this.$el.empty(); + + var title = this.model.get('type'); + this.$el.html(this.toTitleCase(title)); + + return this; + }, + + toTitleCase : function(str) + { + return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); + } +}); diff --git a/src/UI/Movies/Details/MoviesDetailsLayout.js b/src/UI/Movies/Details/MoviesDetailsLayout.js index 26c84115c..3fdb85bec 100644 --- a/src/UI/Movies/Details/MoviesDetailsLayout.js +++ b/src/UI/Movies/Details/MoviesDetailsLayout.js @@ -11,7 +11,7 @@ var LoadingView = require('../../Shared/LoadingView'); var EpisodeFileEditorLayout = require('../../EpisodeFile/Editor/EpisodeFileEditorLayout'); var HistoryLayout = require('../History/MovieHistoryLayout'); var SearchLayout = require('../Search/MovieSearchLayout'); -var FilesLayout = require("../Files/FilesLayout"); +var AllFilesLayout = require("../Files/AllFilesLayout"); var TitlesLayout = require("../Titles/TitlesLayout"); require('backstrech'); require('../../Mixins/backbone.signalr.mixin'); @@ -21,28 +21,27 @@ module.exports = Marionette.Layout.extend({ template : 'Movies/Details/MoviesDetailsTemplate', regions : { - seasons : '#seasons', - info : '#info', - search : '#movie-search', - history : '#movie-history', - files : "#movie-files", - titles: "#movie-titles", + info : '#info', + search : '#movie-search', + history : '#movie-history', + filesTabs : '#movie-files-tabs', + titles : "#movie-titles", }, ui : { - header : '.x-header', - monitored : '.x-monitored', - edit : '.x-edit', - refresh : '.x-refresh', - rename : '.x-rename', - searchAuto : '.x-search', - poster : '.x-movie-poster', - manualSearch : '.x-manual-search', - history : '.x-movie-history', - search : '.x-movie-search', - files : ".x-movie-files", - titles: ".x-movie-titles", + header : '.x-header', + monitored : '.x-monitored', + edit : '.x-edit', + refresh : '.x-refresh', + rename : '.x-rename', + searchAuto : '.x-search', + poster : '.x-movie-poster', + manualSearch: '.x-manual-search', + history : '.x-movie-history', + search : '.x-movie-search', + filesTabs : '.x-movie-files-tabs', + titles : ".x-movie-titles", }, events : { @@ -53,10 +52,10 @@ module.exports = Marionette.Layout.extend({ 'click .x-rename' : '_renameMovies', 'click .x-search' : '_moviesSearch', 'click .x-manual-search' : '_showSearch', - 'click .x-movie-history' : '_showHistory', - 'click .x-movie-search' : '_showSearch', - "click .x-movie-files" : "_showFiles", - "click .x-movie-titles" : "_showTitles", + 'click .x-movie-history' : '_showHistory', + 'click .x-movie-search' : '_showSearch', + 'click .x-movie-files-tabs' : '_showFileTabs', + "click .x-movie-titles" : "_showTitles", }, initialize : function() { @@ -79,26 +78,20 @@ module.exports = Marionette.Layout.extend({ }, _refreshFiles : function() { - this._showFiles(); + this._showFileTabs(); }, onShow : function() { this.searchLayout = new SearchLayout({ model : this.model }); this.searchLayout.startManualSearch = true; - - this.filesLayout = new FilesLayout({ model : this.model }); + this.allFilesLayout = new AllFilesLayout({ model : this.model }); this.titlesLayout = new TitlesLayout({ model : this.model }); this._showBackdrop(); this._showSeasons(); this._setMonitoredState(); this._showInfo(); - if (this.model.get("movieFile")) { - this._showFiles(); - } else { - this._showHistory(); - } - + this._showHistory(); }, onRender : function() { @@ -166,13 +159,13 @@ module.exports = Marionette.Layout.extend({ this.search.show(this.searchLayout); }, - _showFiles : function(e) { - if (e) { - e.preventDefault(); - } + _showFileTabs : function(e) { + if (e) { + e.preventDefault(); + } - this.ui.files.tab('show'); - this.files.show(this.filesLayout); + this.ui.filesTabs.tab('show'); + this.filesTabs.show(this.allFilesLayout); }, _showTitles : function(e) { @@ -254,9 +247,6 @@ module.exports = Marionette.Layout.extend({ }, _refresh : function() { - //this.seasonCollection.add(this.model.get('seasons'), { merge : true }); - //this.episodeCollection.fetch(); - //this.episodeFileCollection.fetch(); this._setMonitoredState(); this._showInfo(); }, diff --git a/src/UI/Movies/Details/MoviesDetailsTemplate.hbs b/src/UI/Movies/Details/MoviesDetailsTemplate.hbs index b2d457ac3..90dbd6a5f 100644 --- a/src/UI/Movies/Details/MoviesDetailsTemplate.hbs +++ b/src/UI/Movies/Details/MoviesDetailsTemplate.hbs @@ -43,13 +43,14 @@ +