mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 12:02:35 +02:00
Added: Importing extra files from downloaded movies and generate metadata such as .nfo (#2506)
Fixes #121, Fixes #167, Fixes #2262 and Fixes #1104
This commit is contained in:
parent
b40423f3a3
commit
e7e9e2b154
@ -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
|
||||
};
|
||||
|
45
src/NzbDrone.Api/ExtraFiles/ExtraFileModule.cs
Normal file
45
src/NzbDrone.Api/ExtraFiles/ExtraFileModule.cs
Normal file
@ -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<ExtraFileResource>
|
||||
{
|
||||
private readonly IExtraFileService<SubtitleFile> _subtitleFileService;
|
||||
private readonly IExtraFileService<MetadataFile> _metadataFileService;
|
||||
private readonly IExtraFileService<OtherExtraFile> _otherFileService;
|
||||
|
||||
public ExtraFileModule(IExtraFileService<SubtitleFile> subtitleFileService, IExtraFileService<MetadataFile> metadataFileService, IExtraFileService<OtherExtraFile> otherExtraFileService)
|
||||
: base("/extrafile")
|
||||
{
|
||||
_subtitleFileService = subtitleFileService;
|
||||
_metadataFileService = metadataFileService;
|
||||
_otherFileService = otherExtraFileService;
|
||||
GetResourceAll = GetFiles;
|
||||
}
|
||||
|
||||
private List<ExtraFileResource> GetFiles()
|
||||
{
|
||||
if (!Request.Query.MovieId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("MovieId is missing");
|
||||
}
|
||||
|
||||
var extraFiles = new List<ExtraFileResource>();
|
||||
|
||||
List<SubtitleFile> subtitleFiles = _subtitleFileService.GetFilesByMovie(Request.Query.MovieId);
|
||||
List<MetadataFile> metadataFiles = _metadataFileService.GetFilesByMovie(Request.Query.MovieId);
|
||||
List<OtherExtraFile> otherExtraFiles = _otherFileService.GetFilesByMovie(Request.Query.MovieId);
|
||||
|
||||
extraFiles.AddRange(subtitleFiles.ToResource());
|
||||
extraFiles.AddRange(metadataFiles.ToResource());
|
||||
extraFiles.AddRange(otherExtraFiles.ToResource());
|
||||
|
||||
return extraFiles;
|
||||
}
|
||||
}
|
||||
}
|
84
src/NzbDrone.Api/ExtraFiles/ExtraFileResource.cs
Normal file
84
src/NzbDrone.Api/ExtraFiles/ExtraFileResource.cs
Normal file
@ -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<ExtraFileResource> ToResource(this IEnumerable<SubtitleFile> movies)
|
||||
{
|
||||
return movies.Select(ToResource).ToList();
|
||||
}
|
||||
|
||||
public static List<ExtraFileResource> ToResource(this IEnumerable<MetadataFile> movies)
|
||||
{
|
||||
return movies.Select(ToResource).ToList();
|
||||
}
|
||||
|
||||
public static List<ExtraFileResource> ToResource(this IEnumerable<OtherExtraFile> movies)
|
||||
{
|
||||
return movies.Select(ToResource).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -115,10 +115,12 @@
|
||||
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
|
||||
<Compile Include="ExtraFiles\ExtraFileResource.cs" />
|
||||
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
|
||||
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
||||
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
||||
<Compile Include="Indexers\ReleasePushModule.cs" />
|
||||
<Compile Include="ExtraFiles\ExtraFileModule.cs" />
|
||||
<Compile Include="Movies\AlternativeTitleModule.cs" />
|
||||
<Compile Include="Movies\AlternativeYearResource.cs" />
|
||||
<Compile Include="Movies\AlternativeYearModule.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<DeleteBadMediaCovers>
|
||||
{
|
||||
private List<MetadataFile> _metadata;
|
||||
private List<Series> _series;
|
||||
private List<Movie> _movies;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateListOfSize(1)
|
||||
_movies = Builder<Movie>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(c => c.Path = "C:\\TV\\".AsOsAgnostic())
|
||||
.With(c => c.Path = "C:\\Movie\\".AsOsAgnostic())
|
||||
.Build().ToList();
|
||||
|
||||
|
||||
_metadata = Builder<MetadataFile>.CreateListOfSize(1)
|
||||
.Build().ToList();
|
||||
|
||||
Mocker.GetMock<ISeriesService>()
|
||||
.Setup(c => c.GetAllSeries())
|
||||
.Returns(_series);
|
||||
Mocker.GetMock<IMovieService>()
|
||||
.Setup(c => c.GetAllMovies())
|
||||
.Returns(_movies);
|
||||
|
||||
|
||||
Mocker.GetMock<IMetadataFileService>()
|
||||
.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<IDiskProvider>()
|
||||
.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<IDiskProvider>()
|
||||
.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<IDiskProvider>()
|
||||
.Setup(c => c.OpenReadStream(imagePath))
|
||||
|
@ -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<CleanupDuplicateMetadataFiles, MetadataFile>
|
||||
{
|
||||
[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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.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<MetadataFile>.CreateNew()
|
||||
.BuildNew();
|
||||
|
@ -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<CleanupOrphanedMetadataFiles, MetadataFile>
|
||||
{
|
||||
[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<MetadataFile>.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<Series>.CreateNew()
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(series);
|
||||
Db.Insert(movie);
|
||||
|
||||
var metadataFile = Builder<MetadataFile>.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<Series>.CreateNew()
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(series);
|
||||
Db.Insert(movie);
|
||||
|
||||
var metadataFile = Builder<MetadataFile>.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<Series>.CreateNew()
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
var episodeFile = Builder<EpisodeFile>.CreateNew()
|
||||
var movieFile = Builder<MovieFile>.CreateNew()
|
||||
.With(h => h.Quality = new QualityModel())
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(series);
|
||||
Db.Insert(episodeFile);
|
||||
Db.Insert(movie);
|
||||
Db.Insert(movieFile);
|
||||
|
||||
var metadataFile = Builder<MetadataFile>.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<Series>.CreateNew()
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(series);
|
||||
Db.Insert(movie);
|
||||
|
||||
var metadataFile = Builder<MetadataFile>.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<Series>.CreateNew()
|
||||
var movie = Builder<Movie>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(series);
|
||||
Db.Insert(movie);
|
||||
|
||||
var metadataFile = Builder<MetadataFile>.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);
|
||||
|
@ -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<RoksboxMetadata>
|
||||
{
|
||||
private Series _series;
|
||||
private Movie _movie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic())
|
||||
_movie = Builder<Movie>.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<WdtvMetadata>
|
||||
{
|
||||
private Series _series;
|
||||
private Movie _movie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic())
|
||||
_movie = Builder<Movie>.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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); }
|
||||
}
|
||||
|
@ -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; }
|
||||
|
44
src/NzbDrone.Core/Datastore/Migration/142_movie_extras.cs
Normal file
44
src/NzbDrone.Core/Datastore/Migration/142_movie_extras.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<SeriesScannedEvent>
|
||||
public class ExistingExtraFileService : IHandle<MovieScannedEvent>
|
||||
{
|
||||
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<ExtraFile>();
|
||||
|
||||
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<string>();
|
||||
|
||||
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);
|
||||
|
@ -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<MediaCoversUpdatedEvent>,
|
||||
IHandle<EpisodeFolderCreatedEvent>,
|
||||
IHandle<SeriesRenamedEvent>
|
||||
IHandle<MovieRenamedEvent>
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly List<IManageExtraFiles> _extraFileManagers;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ExtraService(IMediaFileService mediaFileService,
|
||||
IEpisodeService episodeService,
|
||||
IMovieService movieService,
|
||||
IDiskProvider diskProvider,
|
||||
IConfigService configService,
|
||||
List<IManageExtraFiles> 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<EpisodeFile> GetEpisodeFiles(int seriesId)
|
||||
private List<MovieFile> 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<Episode>(episodes.Where(e => e.EpisodeFileId == localEpisodeFile.Id));
|
||||
movieFile.Movie = new LazyLoaded<Movie>(_movieService.GetMovie(movieId));
|
||||
}
|
||||
|
||||
return episodeFiles;
|
||||
return movieFiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<ExtraFile> CreateAfterSeriesScan(Series series, List<EpisodeFile> episodeFiles);
|
||||
IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile);
|
||||
IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder);
|
||||
IEnumerable<ExtraFile> MoveFilesAfterRename(Series series, List<EpisodeFile> episodeFiles);
|
||||
ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly);
|
||||
IEnumerable<ExtraFile> CreateAfterMovieScan(Movie movie, List<MovieFile> movieFiles);
|
||||
IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
||||
IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
|
||||
ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
|
||||
}
|
||||
|
||||
public abstract class ExtraFileManager<TExtraFile> : IManageExtraFiles
|
||||
@ -23,29 +25,40 @@ public abstract class ExtraFileManager<TExtraFile> : IManageExtraFiles
|
||||
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IDiskTransferService _diskTransferService;
|
||||
private readonly IExtraFileService<TExtraFile> _extraFileService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ExtraFileManager(IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
IDiskTransferService diskTransferService,
|
||||
IExtraFileService<TExtraFile> extraFileService)
|
||||
Logger logger)
|
||||
{
|
||||
_configService = configService;
|
||||
_diskProvider = diskProvider;
|
||||
_diskTransferService = diskTransferService;
|
||||
_extraFileService = extraFileService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public abstract int Order { get; }
|
||||
public abstract IEnumerable<ExtraFile> CreateAfterSeriesScan(Series series, List<EpisodeFile> episodeFiles);
|
||||
public abstract IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile);
|
||||
public abstract IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder);
|
||||
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Series series, List<EpisodeFile> episodeFiles);
|
||||
public abstract ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly);
|
||||
public abstract IEnumerable<ExtraFile> CreateAfterMovieScan(Movie movie, List<MovieFile> movieFiles);
|
||||
public abstract IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
|
||||
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<TExtraFile> : IBasicRepository<TExtraFile> where TExtraFile : ExtraFile, new()
|
||||
{
|
||||
void DeleteForSeries(int seriesId);
|
||||
void DeleteForSeason(int seriesId, int seasonNumber);
|
||||
void DeleteForEpisodeFile(int episodeFileId);
|
||||
List<TExtraFile> GetFilesBySeries(int seriesId);
|
||||
List<TExtraFile> GetFilesBySeason(int seriesId, int seasonNumber);
|
||||
List<TExtraFile> GetFilesByEpisodeFile(int episodeFileId);
|
||||
void DeleteForMovie(int movieId);
|
||||
void DeleteForMovieFile(int movieFileId);
|
||||
List<TExtraFile> GetFilesByMovie(int movieId);
|
||||
List<TExtraFile> 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<TExtraFile> GetFilesByMovie(int movieId)
|
||||
{
|
||||
Delete(c => c.EpisodeFileId == episodeFileId);
|
||||
return Query.Where(c => c.MovieId == movieId);
|
||||
}
|
||||
|
||||
public List<TExtraFile> GetFilesBySeries(int seriesId)
|
||||
public List<TExtraFile> GetFilesByMovieFile(int movieFileId)
|
||||
{
|
||||
return Query.Where(c => c.SeriesId == seriesId);
|
||||
}
|
||||
|
||||
public List<TExtraFile> GetFilesBySeason(int seriesId, int seasonNumber)
|
||||
{
|
||||
return Query.Where(c => c.SeriesId == seriesId && c.SeasonNumber == seasonNumber);
|
||||
}
|
||||
|
||||
public List<TExtraFile> GetFilesByEpisodeFile(int episodeFileId)
|
||||
{
|
||||
return Query.Where(c => c.EpisodeFileId == episodeFileId);
|
||||
return Query.Where(c => c.MovieFileId == movieFileId);
|
||||
}
|
||||
|
||||
public TExtraFile FindByPath(string path)
|
||||
|
@ -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<TExtraFile>
|
||||
where TExtraFile : ExtraFile, new()
|
||||
{
|
||||
List<TExtraFile> GetFilesBySeries(int seriesId);
|
||||
List<TExtraFile> GetFilesByEpisodeFile(int episodeFileId);
|
||||
List<TExtraFile> GetFilesByMovie(int movieId);
|
||||
List<TExtraFile> GetFilesByMovieFile(int movieFileId);
|
||||
TExtraFile FindByPath(string path);
|
||||
void Upsert(TExtraFile extraFile);
|
||||
void Upsert(List<TExtraFile> extraFiles);
|
||||
@ -25,24 +25,24 @@ public interface IExtraFileService<TExtraFile>
|
||||
}
|
||||
|
||||
public abstract class ExtraFileService<TExtraFile> : IExtraFileService<TExtraFile>,
|
||||
IHandleAsync<SeriesDeletedEvent>,
|
||||
IHandleAsync<EpisodeFileDeletedEvent>
|
||||
IHandleAsync<MovieDeletedEvent>,
|
||||
IHandleAsync<MovieFileDeletedEvent>
|
||||
where TExtraFile : ExtraFile, new()
|
||||
{
|
||||
private readonly IExtraFileRepository<TExtraFile> _repository;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ExtraFileService(IExtraFileRepository<TExtraFile> 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<TExtraFile> repository,
|
||||
|
||||
public virtual bool PermanentlyDelete => false;
|
||||
|
||||
public List<TExtraFile> GetFilesBySeries(int seriesId)
|
||||
public List<TExtraFile> GetFilesByMovie(int movieId)
|
||||
{
|
||||
return _repository.GetFilesBySeries(seriesId);
|
||||
return _repository.GetFilesByMovie(movieId);
|
||||
}
|
||||
|
||||
public List<TExtraFile> GetFilesByEpisodeFile(int episodeFileId)
|
||||
public List<TExtraFile> GetFilesByMovieFile(int movieFileId)
|
||||
{
|
||||
return _repository.GetFilesByEpisodeFile(episodeFileId);
|
||||
return _repository.GetFilesByMovieFile(movieFileId);
|
||||
}
|
||||
|
||||
public TExtraFile FindByPath(string path)
|
||||
@ -96,28 +96,28 @@ public void DeleteMany(IEnumerable<int> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<ExtraFile> ProcessFiles(Series series, List<string> filesOnDisk, List<string> importedFiles);
|
||||
IEnumerable<ExtraFile> ProcessFiles(Movie movie, List<string> filesOnDisk, List<string> importedFiles);
|
||||
}
|
||||
}
|
||||
|
@ -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<TExtraFile> extraFileServi
|
||||
}
|
||||
|
||||
public abstract int Order { get; }
|
||||
public abstract IEnumerable<ExtraFile> ProcessFiles(Series series, List<string> filesOnDisk, List<string> importedFiles);
|
||||
public abstract IEnumerable<ExtraFile> ProcessFiles(Movie movie, List<string> filesOnDisk, List<string> importedFiles);
|
||||
|
||||
public virtual ImportExistingExtraFileFilterResult<TExtraFile> FilterAndClean(Series series, List<string> filesOnDisk, List<string> importedFiles)
|
||||
public virtual ImportExistingExtraFileFilterResult<TExtraFile> FilterAndClean(Movie movie, List<string> filesOnDisk, List<string> 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<TExtraFile> Filter(Series series, List<string> filesOnDisk, List<string> importedFiles, List<TExtraFile> seriesFiles)
|
||||
private ImportExistingExtraFileFilterResult<TExtraFile> Filter(Movie movie, List<string> filesOnDisk, List<string> importedFiles, List<TExtraFile> 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<TExtraFile> Filter(Series series, Li
|
||||
return new ImportExistingExtraFileFilterResult<TExtraFile>(previouslyImported, filteredFiles);
|
||||
}
|
||||
|
||||
private void Clean(Series series, List<string> filesOnDisk, List<string> importedFiles, List<TExtraFile> seriesFiles)
|
||||
private void Clean(Movie movie, List<string> filesOnDisk, List<string> importedFiles, List<TExtraFile> 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);
|
||||
|
@ -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<ImageFileResult> SeriesImages(Series series)
|
||||
|
||||
public override List<ImageFileResult> MovieImages(Movie movie, MovieFile movieFile)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> SeasonImages(Series series, Season season)
|
||||
private IEnumerable<ImageFileResult> ProcessMovieImages(Movie movie)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
private IEnumerable<ImageFileResult> ProcessSeriesImages(Series series)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
private IEnumerable<ImageFileResult> ProcessSeasonImages(Series series, Season season)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
private string GetEpisodeNfoFilename(string episodeFilePath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetEpisodeImageFilename(string episodeFilePath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -31,30 +31,30 @@ public RoksboxMetadata(IMapCoversToLocal mediaCoverService,
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private static List<string> ValidCertification = new List<string> { "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 (?<season>\d+))|(?<specials>specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
//Re-enable when/if we store and use mpaa certification
|
||||
//private static List<string> ValidCertification = new List<string> { "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<ImageFileResult> SeriesImages(Series series)
|
||||
public override List<ImageFileResult> MovieImages(Movie movie, MovieFile movieFile)
|
||||
{
|
||||
var image = series.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? series.Images.FirstOrDefault();
|
||||
if (!Settings.MovieImages)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
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<ImageFileResult>{ new ImageFileResult(destination, source) };
|
||||
return new List<ImageFileResult> { new ImageFileResult(destination, source) };
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> 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<ImageFileResult>();
|
||||
}
|
||||
|
||||
//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<ImageFileResult>();
|
||||
}
|
||||
|
||||
var filename = Path.GetFileName(seasonFolder) + ".jpg";
|
||||
var path = series.Path.GetRelativePath(Path.Combine(series.Path, seasonFolder, filename));
|
||||
|
||||
return new List<ImageFileResult> { new ImageFileResult(path, image.Url) };
|
||||
return Path.ChangeExtension(movieFilePath, "xml");
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> 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<ImageFileResult>();
|
||||
}
|
||||
|
||||
return new List<ImageFileResult> {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<int, string> GetSeasonFolders(Series series)
|
||||
{
|
||||
var seasonFolderMap = new Dictionary<int, string>();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -31,30 +31,28 @@ public WdtvMetadata(IMapCoversToLocal mediaCoverService,
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?<season>\d+))|(?<specials>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<ImageFileResult> SeriesImages(Series series)
|
||||
public override List<ImageFileResult> MovieImages(Movie movie, MovieFile moviefile)
|
||||
{
|
||||
if (!Settings.SeriesImages)
|
||||
if (!Settings.MovieImages)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
//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<ImageFileResult>();
|
||||
}
|
||||
|
||||
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<ImageFileResult>
|
||||
@ -194,102 +153,14 @@ public override List<ImageFileResult> SeriesImages(Series series)
|
||||
};
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> SeasonImages(Series series, Season season)
|
||||
private string GetMovieFileMetadataFilename(string movieFilePath)
|
||||
{
|
||||
if (!Settings.SeasonImages)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
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<ImageFileResult>();
|
||||
}
|
||||
|
||||
//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<ImageFileResult>();
|
||||
}
|
||||
|
||||
var path = Path.Combine(seasonFolder, "folder.jpg");
|
||||
|
||||
return new List<ImageFileResult>{ new ImageFileResult(path, image.Url) };
|
||||
return Path.ChangeExtension(movieFilePath, "xml");
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile)
|
||||
private string GetMovieFileImageFilename(string movieFilePath)
|
||||
{
|
||||
if (!Settings.EpisodeImages)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
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<ImageFileResult>();
|
||||
}
|
||||
|
||||
return new List<ImageFileResult>{ 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<int, string> GetSeasonFolders(Series series)
|
||||
{
|
||||
var seasonFolderMap = new Dictionary<int, string>();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -27,357 +27,227 @@ public XbmcMetadata(IMapCoversToLocal mediaCoverService,
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private static readonly Regex SeriesImagesRegex = new Regex(@"^(?<type>poster|banner|fanart)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex SeasonImagesRegex = new Regex(@"^season(?<season>\d{2,}|-all|-specials)-(?<type>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(@"^(?<type>poster|banner|fanart|clearart|disc|landscape|logo)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex MovieFileImageRegex = new Regex(@"(?<type>-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<ImageFileResult> 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<ImageFileResult> SeriesImages(Series series)
|
||||
{
|
||||
if (!Settings.SeriesImages)
|
||||
if (!Settings.MovieImages)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
return ProcessSeriesImages(series).ToList();
|
||||
return ProcessMovieImages(movie).ToList();
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> SeasonImages(Series series, Season season)
|
||||
private IEnumerable<ImageFileResult> ProcessMovieImages(Movie movie)
|
||||
{
|
||||
if (!Settings.SeasonImages)
|
||||
foreach (var image in movie.Images)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
return ProcessSeasonImages(series, season).ToList();
|
||||
}
|
||||
|
||||
public override List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile)
|
||||
{
|
||||
if (!Settings.EpisodeImages)
|
||||
{
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
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<ImageFileResult>();
|
||||
}
|
||||
|
||||
return new List<ImageFileResult>
|
||||
{
|
||||
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<ImageFileResult>();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<ImageFileResult> 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<ImageFileResult> 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)
|
||||
|
@ -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 <movie-filename>.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()
|
||||
|
@ -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<MetadataFile> metadataFileServ
|
||||
|
||||
public override int Order => 0;
|
||||
|
||||
public override IEnumerable<ExtraFile> ProcessFiles(Series series, List<string> filesOnDisk, List<string> importedFiles)
|
||||
public override IEnumerable<ExtraFile> ProcessFiles(Movie movie, List<string> filesOnDisk, List<string> 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<MetadataFile>();
|
||||
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<ExtraFile> ProcessFiles(Series series, List<string>
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -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<MetadataFile>
|
||||
|
||||
public class MetadataFileService : ExtraFileService<MetadataFile>, IMetadataFileService
|
||||
{
|
||||
public MetadataFileService(IExtraFileRepository<MetadataFile> repository, ISeriesService seriesService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger)
|
||||
: base(repository, seriesService, diskProvider, recycleBinProvider, logger)
|
||||
public MetadataFileService(IExtraFileRepository<MetadataFile> repository, IMovieService movieService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger)
|
||||
: base(repository, movieService, diskProvider, recycleBinProvider, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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<ImageFileResult> SeriesImages(Series series);
|
||||
List<ImageFileResult> SeasonImages(Series series, Season season);
|
||||
List<ImageFileResult> 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<ImageFileResult> MovieImages(Movie movie, MovieFile movieFile);
|
||||
}
|
||||
}
|
||||
|
@ -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<ImageFileResult> SeriesImages(Series series);
|
||||
public abstract List<ImageFileResult> SeasonImages(Series series, Season season);
|
||||
public abstract List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile);
|
||||
public abstract MetadataFile FindMetadataFile(Movie movie, string path);
|
||||
|
||||
public abstract MetadataFileResult MovieMetadata(Movie movie, MovieFile movieFile);
|
||||
public abstract List<ImageFileResult> MovieImages(Movie movie, MovieFile movieFile);
|
||||
|
||||
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }
|
||||
|
||||
|
@ -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<MetadataFile>
|
||||
{
|
||||
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<ExtraFile> CreateAfterSeriesScan(Series series, List<EpisodeFile> episodeFiles)
|
||||
public override IEnumerable<ExtraFile> CreateAfterMovieScan(Movie movie, List<MovieFile> 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<MetadataFile>();
|
||||
}
|
||||
|
||||
@ -66,14 +66,10 @@ public override IEnumerable<ExtraFile> 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<ExtraFile> CreateAfterSeriesScan(Series series, List
|
||||
return files;
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile)
|
||||
public override IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile)
|
||||
{
|
||||
var files = new List<MetadataFile>();
|
||||
|
||||
foreach (var consumer in _metadataFactory.Enabled())
|
||||
{
|
||||
|
||||
files.AddIfNotNull(ProcessEpisodeMetadata(consumer, series, episodeFile, new List<MetadataFile>()));
|
||||
files.AddRange(ProcessEpisodeImages(consumer, series, episodeFile, new List<MetadataFile>()));
|
||||
files.AddIfNotNull(ProcessMovieMetadata(consumer, movie, movieFile, new List<MetadataFile>()));
|
||||
files.AddRange(ProcessMovieImages(consumer, movie, movieFile, new List<MetadataFile>()));
|
||||
}
|
||||
|
||||
_metadataFileService.Upsert(files);
|
||||
@ -98,41 +94,9 @@ public override IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, E
|
||||
return files;
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder)
|
||||
public override IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles)
|
||||
{
|
||||
var metadataFiles = _metadataFileService.GetFilesBySeries(series.Id);
|
||||
|
||||
if (seriesFolder.IsNullOrWhiteSpace() && seasonFolder.IsNullOrWhiteSpace())
|
||||
{
|
||||
return new List<MetadataFile>();
|
||||
}
|
||||
|
||||
var files = new List<MetadataFile>();
|
||||
|
||||
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<ExtraFile> MoveFilesAfterRename(Series series, List<EpisodeFile> episodeFiles)
|
||||
{
|
||||
var metadataFiles = _metadataFileService.GetFilesBySeries(series.Id);
|
||||
var metadataFiles = _metadataFileService.GetFilesByMovie(movie.Id);
|
||||
var movedFiles = new List<MetadataFile>();
|
||||
|
||||
// TODO: Move EpisodeImage and EpisodeMetadata metadata files, instead of relying on consumers to do it
|
||||
@ -140,26 +104,26 @@ public override IEnumerable<ExtraFile> 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<ExtraFile> 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<MetadataFile> GetMetadataFilesForConsumer(IMetadata consumer, List<MetadataFile> seriesMetadata)
|
||||
private List<MetadataFile> GetMetadataFilesForConsumer(IMetadata consumer, List<MetadataFile> 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<MetadataFile> existingMetadataFiles)
|
||||
private MetadataFile ProcessMovieMetadata(IMetadata consumer, Movie movie, MovieFile movieFile, List<MetadataFile> 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<MetadataFile> 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<MetadataFile> ProcessSeriesImages(IMetadata consumer, Series series, List<MetadataFile> existingMetadataFiles)
|
||||
|
||||
private List<MetadataFile> ProcessMovieImages(IMetadata consumer, Movie movie, MovieFile movieFile, List<MetadataFile> existingMetadataFiles)
|
||||
{
|
||||
var result = new List<MetadataFile>();
|
||||
|
||||
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<MetadataFile> ProcessSeasonImages(IMetadata consumer, Series series, List<MetadataFile> existingMetadataFiles)
|
||||
{
|
||||
var result = new List<MetadataFile>();
|
||||
|
||||
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<MetadataFile> ProcessEpisodeImages(IMetadata consumer, Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
||||
{
|
||||
var result = new List<MetadataFile>();
|
||||
|
||||
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<MetadataFile> 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<MetadataFile> 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<MetadataFile> existingMetadataFiles, Func<MetadataFile, bool> predicate)
|
||||
private MetadataFile GetMetadataFile(Movie movie, List<MetadataFile> existingMetadataFiles, Func<MetadataFile, bool> predicate)
|
||||
{
|
||||
var matchingMetadataFiles = existingMetadataFiles.Where(predicate).ToList();
|
||||
|
||||
@ -439,7 +287,7 @@ private MetadataFile GetMetadataFile(Series series, List<MetadataFile> 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);
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<OtherExtraFile> otherExtraFi
|
||||
|
||||
public override int Order => 2;
|
||||
|
||||
public override IEnumerable<ExtraFile> ProcessFiles(Series series, List<string> filesOnDisk, List<string> importedFiles)
|
||||
public override IEnumerable<ExtraFile> ProcessFiles(Movie movie, List<string> filesOnDisk, List<string> 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<OtherExtraFile>();
|
||||
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);
|
||||
|
@ -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<OtherExtraFile>
|
||||
|
||||
public class OtherExtraFileService : ExtraFileService<OtherExtraFile>, IOtherExtraFileService
|
||||
{
|
||||
public OtherExtraFileService(IExtraFileRepository<OtherExtraFile> repository, ISeriesService seriesService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger)
|
||||
: base(repository, seriesService, diskProvider, recycleBinProvider, logger)
|
||||
public OtherExtraFileService(IExtraFileRepository<OtherExtraFile> repository, IMovieService movieService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger)
|
||||
: base(repository, movieService, diskProvider, recycleBinProvider, logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -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<OtherExtraFile>
|
||||
{
|
||||
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<ExtraFile> CreateAfterSeriesScan(Series series, List<EpisodeFile> episodeFiles)
|
||||
public override IEnumerable<ExtraFile> CreateAfterMovieScan(Movie movie, List<MovieFile> movieFiles)
|
||||
{
|
||||
return Enumerable.Empty<ExtraFile>();
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile)
|
||||
public override IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile)
|
||||
{
|
||||
return Enumerable.Empty<ExtraFile>();
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder)
|
||||
{
|
||||
return Enumerable.Empty<ExtraFile>();
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> MoveFilesAfterRename(Series series, List<EpisodeFile> episodeFiles)
|
||||
{
|
||||
// TODO: Remove
|
||||
// We don't want to move files after rename yet.
|
||||
|
||||
return Enumerable.Empty<ExtraFile>();
|
||||
|
||||
var extraFiles = _otherExtraFileService.GetFilesBySeries(series.Id);
|
||||
public override IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles)
|
||||
{
|
||||
var extraFiles = _otherExtraFileService.GetFilesByMovie(movie.Id);
|
||||
var movedFiles = new List<OtherExtraFile>();
|
||||
|
||||
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<ExtraFile> 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);
|
||||
|
||||
|
@ -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<SubtitleFile> subtitleFileServ
|
||||
|
||||
public override int Order => 1;
|
||||
|
||||
public override IEnumerable<ExtraFile> ProcessFiles(Series series, List<string> filesOnDisk, List<string> importedFiles)
|
||||
public override IEnumerable<ExtraFile> ProcessFiles(Movie movie, List<string> filesOnDisk, List<string> 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<SubtitleFile>();
|
||||
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<ExtraFile> ProcessFiles(Series series, List<string>
|
||||
|
||||
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
|
||||
};
|
||||
|
@ -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<string>
|
||||
_fileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
".aqt",
|
||||
".ass",
|
||||
|
@ -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<SubtitleFile>
|
||||
|
||||
public class SubtitleFileService : ExtraFileService<SubtitleFile>, ISubtitleFileService
|
||||
{
|
||||
public SubtitleFileService(IExtraFileRepository<SubtitleFile> repository, ISeriesService seriesService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger)
|
||||
: base(repository, seriesService, diskProvider, recycleBinProvider, logger)
|
||||
public SubtitleFileService(IExtraFileRepository<SubtitleFile> repository, IMovieService movieService, IDiskProvider diskProvider, IRecycleBinProvider recycleBinProvider, Logger logger)
|
||||
: base(repository, movieService, diskProvider, recycleBinProvider, logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -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<SubtitleFile>
|
||||
{
|
||||
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<ExtraFile> CreateAfterSeriesScan(Series series, List<EpisodeFile> episodeFiles)
|
||||
public override IEnumerable<ExtraFile> CreateAfterMovieScan(Movie movie, List<MovieFile> movieFiles)
|
||||
{
|
||||
return Enumerable.Empty<SubtitleFile>();
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, EpisodeFile episodeFile)
|
||||
public override IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile)
|
||||
{
|
||||
return Enumerable.Empty<SubtitleFile>();
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> CreateAfterEpisodeImport(Series series, string seriesFolder, string seasonFolder)
|
||||
public override IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles)
|
||||
{
|
||||
return Enumerable.Empty<SubtitleFile>();
|
||||
}
|
||||
|
||||
public override IEnumerable<ExtraFile> MoveFilesAfterRename(Series series, List<EpisodeFile> episodeFiles)
|
||||
{
|
||||
// TODO: Remove
|
||||
// We don't want to move files after rename yet.
|
||||
|
||||
return Enumerable.Empty<ExtraFile>();
|
||||
|
||||
var subtitleFiles = _subtitleFileService.GetFilesBySeries(series.Id);
|
||||
var subtitleFiles = _subtitleFileService.GetFilesByMovie(movie.Id);
|
||||
|
||||
var movedFiles = new List<SubtitleFile>();
|
||||
|
||||
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<ExtraFile> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
@ -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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<string> FilterFiles(Series series, IEnumerable<string> files);
|
||||
List<string> FilterFiles(Movie series, IEnumerable<string> 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)
|
||||
{
|
||||
|
@ -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<ImportResult> 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)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -117,7 +117,7 @@ public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownloa
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
_extraService.ImportExtraFiles(localEpisode, episodeFile, copyOnly);
|
||||
// _extraService.ImportExtraFiles(localEpisode, episodeFile, copyOnly);
|
||||
}
|
||||
|
||||
if (downloadClientItem != null)
|
||||
|
@ -120,7 +120,7 @@ public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownloa
|
||||
|
||||
if (newDownload)
|
||||
{
|
||||
//_extraService.ImportExtraFiles(localMovie, episodeFile, copyOnly); TODO update for movie
|
||||
_extraService.ImportExtraFiles(localMovie, movieFile, copyOnly);
|
||||
}
|
||||
|
||||
if (downloadClientItem != null)
|
||||
|
@ -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<string, Quality>
|
||||
_fileExtensions = new Dictionary<string, Quality>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
//Unknown
|
||||
{ ".webm", Quality.Unknown },
|
||||
@ -70,7 +71,7 @@ static MediaFileExtensions()
|
||||
};
|
||||
}
|
||||
|
||||
public static HashSet<string> Extensions => new HashSet<string>(_fileExtensions.Keys);
|
||||
public static HashSet<string> Extensions => new HashSet<string>(_fileExtensions.Keys, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static Quality GetQualityForExtension(string extension)
|
||||
{
|
||||
|
@ -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<MovieFile> movieFiles, Movie movie, string oldMovi
|
||||
{
|
||||
_logger.Error(ex, "Failed to rename file: " + oldMovieFilePath);
|
||||
}
|
||||
|
||||
if (renamed.Any())
|
||||
{
|
||||
_eventAggregator.PublishEvent(new MovieRenamedEvent(movie));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,7 @@
|
||||
<Compile Include="Authentication\UserRepository.cs" />
|
||||
<Compile Include="Authentication\UserService.cs" />
|
||||
<Compile Include="Datastore\Migration\123_create_netimport_table.cs" />
|
||||
<Compile Include="Datastore\Migration\142_movie_extras.cs" />
|
||||
<Compile Include="Datastore\Migration\140_add_alternative_titles_table.cs" />
|
||||
<Compile Include="Datastore\Migration\141_fix_duplicate_alt_titles.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\RequiredIndexerFlagsSpecification.cs" />
|
||||
@ -1378,11 +1379,7 @@
|
||||
<Compile Include="Notifications\Telegram\TelegramError.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Folder Include="NetImport\ImportExclusions\" />
|
||||
<Folder Include="NetImport\Radarr\" />
|
||||
<Folder Include="MetadataSource\RadarrAPI\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
@ -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<RefreshMovieCommand>
|
||||
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<string>.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<int>{movie.Id}));
|
||||
_commandQueueManager.Push(new RenameMovieFolderCommand(new List<int> { movie.Id }));
|
||||
_diskScanService.Scan(movie);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
14
src/UI/Cells/ExtraExtensionCell.js
Normal file
14
src/UI/Cells/ExtraExtensionCell.js
Normal file
@ -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;
|
||||
}
|
||||
});
|
19
src/UI/Cells/ExtraTypeCell.js
Normal file
19
src/UI/Cells/ExtraTypeCell.js
Normal file
@ -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();});
|
||||
}
|
||||
});
|
@ -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();
|
||||
},
|
||||
|
@ -43,13 +43,14 @@
|
||||
<ul class="nav nav-tabs" id="myTab">
|
||||
<li><a href="#movie-history" class="x-movie-history">History</a></li>
|
||||
<li><a href="#movie-search" class="x-movie-search">Search</a></li>
|
||||
<li><a href="#movie-files" class="x-movie-files">Files</a></li>
|
||||
<li><a href="#movie-files-tabs" class="x-movie-files-tabs">Files</a></li>
|
||||
<li><a href="#movie-titles" class="x-movie-titles">Titles</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="movie-history"/>
|
||||
<div class="tab-pane" id="movie-search"/>
|
||||
<div class="tab-pane" id="movie-files"/>
|
||||
<div class="tab-pane" id="movie-files-tabs"/>
|
||||
<div class="tab-pane" id="movie-titles"/>
|
||||
</div>
|
||||
</div>
|
||||
|
30
src/UI/Movies/Files/AllFilesLayout.js
Normal file
30
src/UI/Movies/Files/AllFilesLayout.js
Normal file
@ -0,0 +1,30 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var FilesLayout = require('./Media/FilesLayout');
|
||||
var ExtraFilesLayout = require('./Extras/ExtraFilesLayout');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Movies/Files/AllFilesLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
files : "#movie-files",
|
||||
mediaFiles : "#movie-media-files",
|
||||
extras : "#movie-extra-files"
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.filesLayout = new FilesLayout({ model : this.model });
|
||||
this.extraFilesLayout = new ExtraFilesLayout({ model : this.model });
|
||||
|
||||
this._showFiles();
|
||||
},
|
||||
|
||||
_showFiles : function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.mediaFiles.show(this.filesLayout);
|
||||
this.extras.show(this.extraFilesLayout);
|
||||
}
|
||||
});
|
5
src/UI/Movies/Files/AllFilesLayoutTemplate.hbs
Normal file
5
src/UI/Movies/Files/AllFilesLayoutTemplate.hbs
Normal file
@ -0,0 +1,5 @@
|
||||
<div class="x-movie-files" id="movie-files">
|
||||
<div id="movie-media-files" />
|
||||
<legend>Extras</legend>
|
||||
<div id="movie-extra-files" />
|
||||
</div>
|
37
src/UI/Movies/Files/Extras/ExtraFilesCollection.js
Normal file
37
src/UI/Movies/Files/Extras/ExtraFilesCollection.js
Normal file
@ -0,0 +1,37 @@
|
||||
var PagableCollection = require('backbone.pageable');
|
||||
var ExtraFileModel = require('./ExtraFileModel');
|
||||
var AsSortedCollection = require('../../../Mixins/AsSortedCollection');
|
||||
|
||||
var Collection = PagableCollection.extend({
|
||||
url : window.NzbDrone.ApiRoot + "/extrafile",
|
||||
model : ExtraFileModel,
|
||||
|
||||
state : {
|
||||
pageSize : 2000,
|
||||
sortKey : 'relativePath',
|
||||
order : -1
|
||||
},
|
||||
|
||||
mode : 'client',
|
||||
|
||||
sortMappings : {
|
||||
'relativePath' : {
|
||||
sortKey : "relativePath"
|
||||
},
|
||||
"type" : {
|
||||
sortKey : "type"
|
||||
},
|
||||
"extension" : {
|
||||
sortKey : "extension"
|
||||
}
|
||||
},
|
||||
|
||||
fetchMovieExtras : function(movieId) {
|
||||
return this.fetch({ data : { movieId : movieId}});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Collection = AsSortedCollection.call(Collection);
|
||||
|
||||
module.exports = Collection;
|
62
src/UI/Movies/Files/Extras/ExtraFilesLayout.js
Normal file
62
src/UI/Movies/Files/Extras/ExtraFilesLayout.js
Normal file
@ -0,0 +1,62 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
var ExtraFilesCollection = require('./ExtraFilesCollection');
|
||||
var LoadingView = require('../../../Shared/LoadingView');
|
||||
var ExtraFileModel = require("./ExtraFileModel");
|
||||
var FileTitleCell = require('../../../Cells/FileTitleCell');
|
||||
var ExtraExtensionCell = require('../../../Cells/ExtraExtensionCell');
|
||||
var ExtraTypeCell = require('../../../Cells/ExtraTypeCell');
|
||||
var NoResultsView = require('../NoFilesView');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Movies/Files/Extras/ExtraFilesLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
extraFilesTable : '.extra-files-table'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'relativePath',
|
||||
label : 'File',
|
||||
cell : FileTitleCell
|
||||
},
|
||||
{
|
||||
name : 'extension',
|
||||
label : 'Extension',
|
||||
cell : ExtraExtensionCell
|
||||
},
|
||||
{
|
||||
name : 'type',
|
||||
label : 'Type',
|
||||
cell : ExtraTypeCell
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
initialize : function() {
|
||||
this.collection = new ExtraFilesCollection();
|
||||
|
||||
this.listenTo(this.collection, 'sync', this._showTable);
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.extraFilesTable.show(new LoadingView());
|
||||
|
||||
this.collection.fetchMovieExtras(this.model.id);
|
||||
},
|
||||
|
||||
_showTable : function() {
|
||||
if (this.collection.any()) {
|
||||
this.extraFilesTable.show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.collection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
} else {
|
||||
this.extraFilesTable.show(new NoResultsView());
|
||||
}
|
||||
}
|
||||
});
|
1
src/UI/Movies/Files/Extras/ExtraFilesLayoutTemplate.hbs
Normal file
1
src/UI/Movies/Files/Extras/ExtraFilesLayoutTemplate.hbs
Normal file
@ -0,0 +1 @@
|
||||
<div class="extra-files-table table-responsive"></div>
|
@ -1,148 +0,0 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
//var ButtonsView = require('./ButtonsView');
|
||||
//var ManualSearchLayout = require('./ManualLayout');
|
||||
var FilesCollection = require('./FilesCollection');
|
||||
var CommandController = require('../../Commands/CommandController');
|
||||
var LoadingView = require('../../Shared/LoadingView');
|
||||
var NoResultsView = require('./NoFilesView');
|
||||
var FileModel = require("./FileModel");
|
||||
var FileTitleCell = require('../../Cells/FileTitleCell');
|
||||
var FileSizeCell = require('../../Cells/FileSizeCell');
|
||||
var QualityCell = require('../../Cells/QualityCell');
|
||||
var MediaInfoCell = require('../../Cells/MediaInfoCell');
|
||||
var ApprovalStatusCell = require('../../Cells/ApprovalStatusCell');
|
||||
var DownloadReportCell = require('../../Release/DownloadReportCell');
|
||||
var AgeCell = require('../../Release/AgeCell');
|
||||
var ProtocolCell = require('../../Release/ProtocolCell');
|
||||
var PeersCell = require('../../Release/PeersCell');
|
||||
var EditionCell = require('../../Cells/EditionCell');
|
||||
var DeleteFileCell = require("./DeleteFileCell");
|
||||
var EditFileCell = require("./EditFileCell");
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Movies/Files/FilesLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
main : '#movie-files-region',
|
||||
grid : "#movie-files-grid"
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-search-auto' : '_searchAuto',
|
||||
'click .x-search-manual' : '_searchManual',
|
||||
'click .x-search-back' : '_showButtons'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'title',
|
||||
label : 'Title',
|
||||
cell : FileTitleCell
|
||||
},
|
||||
{
|
||||
name : "mediaInfo",
|
||||
label : "Media Info",
|
||||
cell : MediaInfoCell
|
||||
},
|
||||
{
|
||||
name : 'edition',
|
||||
label : 'Edition',
|
||||
cell : EditionCell,
|
||||
title : "Edition",
|
||||
},
|
||||
{
|
||||
name : 'size',
|
||||
label : 'Size',
|
||||
cell : FileSizeCell
|
||||
},
|
||||
{
|
||||
name : 'quality',
|
||||
label : 'Quality',
|
||||
cell : QualityCell,
|
||||
},
|
||||
{
|
||||
name : "delete",
|
||||
label : "",
|
||||
cell : DeleteFileCell,
|
||||
},
|
||||
{
|
||||
name : "edit",
|
||||
label : "",
|
||||
cell : EditFileCell,
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
initialize : function(movie) {
|
||||
this.filesCollection = new FilesCollection();
|
||||
var file = movie.model.get("movieFile");
|
||||
this.movie = movie;
|
||||
this.filesCollection.add(file);
|
||||
//this.listenTo(this.releaseCollection, 'sync', this._showSearchResults);
|
||||
this.listenTo(this.model, 'change', function(model, options) {
|
||||
if (options && options.changeSource === 'signalr') {
|
||||
this._refresh(model);
|
||||
}
|
||||
});
|
||||
|
||||
vent.on(vent.Commands.MovieFileEdited, this._showGrid, this);
|
||||
},
|
||||
|
||||
_refresh : function(model) {
|
||||
this.filesCollection = new FilesCollection();
|
||||
|
||||
if(model.get('hasFile')) {
|
||||
var file = model.get("movieFile");
|
||||
this.filesCollection.add(file);
|
||||
}
|
||||
|
||||
this.onShow();
|
||||
},
|
||||
|
||||
_refreshClose : function(options) {
|
||||
this.filesCollection = new FilesCollection();
|
||||
var file = this.movie.model.get("movieFile");
|
||||
this.filesCollection.add(file);
|
||||
this._showGrid();
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.grid.show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.filesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
},
|
||||
|
||||
_showGrid : function() {
|
||||
this.regionManager.get('grid').show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.filesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
},
|
||||
|
||||
_showMainView : function() {
|
||||
this.main.show(this.mainView);
|
||||
},
|
||||
|
||||
_showButtons : function() {
|
||||
this._showMainView();
|
||||
},
|
||||
|
||||
_showSearchResults : function() {
|
||||
if (this.releaseCollection.length === 0) {
|
||||
this.mainView = new NoResultsView();
|
||||
}
|
||||
|
||||
else {
|
||||
//this.mainView = new ManualSearchLayout({ collection : this.releaseCollection });
|
||||
}
|
||||
|
||||
this._showMainView();
|
||||
}
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
<div id="movie-files-region">
|
||||
<div id="movie-files-grid" class="table-responsive"></div>
|
||||
</div>
|
@ -1,14 +1,14 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var Qualities = require('../../../Quality/QualityDefinitionCollection');
|
||||
var AsModelBoundView = require('../../../Mixins/AsModelBoundView');
|
||||
var AsValidatedView = require('../../../Mixins/AsValidatedView');
|
||||
var AsEditModalView = require('../../../Mixins/AsEditModalView');
|
||||
require('../../../Mixins/TagInput');
|
||||
require('../../../Mixins/FileBrowser');
|
||||
var Qualities = require('../../../../Quality/QualityDefinitionCollection');
|
||||
var AsModelBoundView = require('../../../../Mixins/AsModelBoundView');
|
||||
var AsValidatedView = require('../../../../Mixins/AsValidatedView');
|
||||
var AsEditModalView = require('../../../../Mixins/AsEditModalView');
|
||||
require('../../../../Mixins/TagInput');
|
||||
require('../../../../Mixins/FileBrowser');
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Movies/Files/Edit/EditFileTemplate',
|
||||
template : 'Movies/Files/Media/Edit/EditFileTemplate',
|
||||
|
||||
ui : {
|
||||
quality : '.x-quality',
|
3
src/UI/Movies/Files/Media/FileModel.js
Normal file
3
src/UI/Movies/Files/Media/FileModel.js
Normal file
@ -0,0 +1,3 @@
|
||||
var Backbone = require('backbone');
|
||||
|
||||
module.exports = Backbone.Model.extend({});
|
@ -1,6 +1,6 @@
|
||||
var PagableCollection = require('backbone.pageable');
|
||||
var FileModel = require('./FileModel');
|
||||
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
|
||||
var AsSortedCollection = require('../../../Mixins/AsSortedCollection');
|
||||
|
||||
var Collection = PagableCollection.extend({
|
||||
url : window.NzbDrone.ApiRoot + "/moviefile",
|
120
src/UI/Movies/Files/Media/FilesLayout.js
Normal file
120
src/UI/Movies/Files/Media/FilesLayout.js
Normal file
@ -0,0 +1,120 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
var FilesCollection = require('./FilesCollection');
|
||||
var CommandController = require('../../../Commands/CommandController');
|
||||
var LoadingView = require('../../../Shared/LoadingView');
|
||||
var NoResultsView = require('../NoFilesView');
|
||||
var FileModel = require("./FileModel");
|
||||
var FileTitleCell = require('../../../Cells/FileTitleCell');
|
||||
var FileSizeCell = require('../../../Cells/FileSizeCell');
|
||||
var QualityCell = require('../../../Cells/QualityCell');
|
||||
var MediaInfoCell = require('../../../Cells/MediaInfoCell');
|
||||
var EditionCell = require('../../../Cells/EditionCell');
|
||||
var DeleteFileCell = require("./DeleteFileCell");
|
||||
var EditFileCell = require("./EditFileCell");
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Movies/Files/Media/FilesLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
grid : "#movie-files-grid"
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-search-auto' : '_searchAuto',
|
||||
'click .x-search-manual' : '_searchManual',
|
||||
'click .x-search-back' : '_showButtons'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'title',
|
||||
label : 'Title',
|
||||
cell : FileTitleCell
|
||||
},
|
||||
{
|
||||
name : "mediaInfo",
|
||||
label : "Media Info",
|
||||
cell : MediaInfoCell
|
||||
},
|
||||
{
|
||||
name : 'edition',
|
||||
label : 'Edition',
|
||||
cell : EditionCell,
|
||||
title : "Edition",
|
||||
},
|
||||
{
|
||||
name : 'size',
|
||||
label : 'Size',
|
||||
cell : FileSizeCell
|
||||
},
|
||||
{
|
||||
name : 'quality',
|
||||
label : 'Quality',
|
||||
cell : QualityCell,
|
||||
},
|
||||
{
|
||||
name : "delete",
|
||||
label : "",
|
||||
cell : DeleteFileCell,
|
||||
},
|
||||
{
|
||||
name : "edit",
|
||||
label : "",
|
||||
cell : EditFileCell,
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
initialize : function(movie) {
|
||||
this.filesCollection = new FilesCollection();
|
||||
var file = movie.model.get("movieFile");
|
||||
this.movie = movie;
|
||||
this.filesCollection.add(file);
|
||||
|
||||
this.listenTo(this.model, 'change', function(model, options) {
|
||||
if (options && options.changeSource === 'signalr') {
|
||||
this._refresh(model);
|
||||
}
|
||||
});
|
||||
|
||||
vent.on(vent.Commands.MovieFileEdited, this._showGrid, this);
|
||||
},
|
||||
|
||||
_refresh : function(model) {
|
||||
this.filesCollection = new FilesCollection();
|
||||
|
||||
if(model.get('hasFile')) {
|
||||
var file = model.get("movieFile");
|
||||
this.filesCollection.add(file);
|
||||
}
|
||||
|
||||
this.onShow();
|
||||
},
|
||||
|
||||
_refreshClose : function(options) {
|
||||
this.filesCollection = new FilesCollection();
|
||||
var file = this.movie.model.get("movieFile");
|
||||
this.filesCollection.add(file);
|
||||
this._showGrid();
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this._showGrid();
|
||||
},
|
||||
|
||||
_showGrid : function() {
|
||||
if (this.filesCollection.length === 0) {
|
||||
this.grid.show(new NoResultsView());
|
||||
}
|
||||
else {
|
||||
this.regionManager.get('grid').show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.filesCollection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
2
src/UI/Movies/Files/Media/FilesLayoutTemplate.hbs
Normal file
2
src/UI/Movies/Files/Media/FilesLayoutTemplate.hbs
Normal file
@ -0,0 +1,2 @@
|
||||
<div id="movie-files-grid" class="table-responsive"/>
|
||||
|
@ -3,7 +3,34 @@ var AsModelBoundView = require('../../../Mixins/AsModelBoundView');
|
||||
var AsValidatedView = require('../../../Mixins/AsValidatedView');
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Settings/MediaManagement/Sorting/SortingViewTemplate'
|
||||
template : 'Settings/MediaManagement/Sorting/SortingViewTemplate',
|
||||
|
||||
events : {
|
||||
'change .x-import-extra-files' : '_setExtraFileExtensionVisibility'
|
||||
},
|
||||
|
||||
ui : {
|
||||
importExtraFiles : '.x-import-extra-files',
|
||||
extraFileExtensions : '.x-extra-file-extensions'
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
if (!this.ui.importExtraFiles.prop('checked')) {
|
||||
this.ui.extraFileExtensions.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_setExtraFileExtensionVisibility : function() {
|
||||
var showExtraFileExtensions = this.ui.importExtraFiles.prop('checked');
|
||||
|
||||
if (showExtraFileExtensions) {
|
||||
this.ui.extraFileExtensions.slideDown();
|
||||
}
|
||||
|
||||
else {
|
||||
this.ui.extraFileExtensions.slideUp();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AsModelBoundView.call(view);
|
||||
|
@ -71,11 +71,11 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="advanced-setting">
|
||||
<fieldset>
|
||||
<legend>Importing</legend>
|
||||
|
||||
{{#if_mono}}
|
||||
<div class="form-group">
|
||||
<div class="form-group advanced-setting">
|
||||
<label class="col-sm-3 control-label">Skip Free Space Check</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
@ -99,7 +99,7 @@
|
||||
</div>
|
||||
{{/if_mono}}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group advanced-setting">
|
||||
<label class="col-sm-3 control-label">Use Hardlinks instead of Copy</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
@ -122,4 +122,39 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Import Extra Files</label>
|
||||
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="importExtraFiles" class="x-import-extra-files"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-info" title="Import matching extra files (subtitles, nfo, etc) after importing an episode file"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group x-extra-file-extensions">
|
||||
<label class="col-sm-3 control-label">Extra File Extensions</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-5 help-inline">
|
||||
<i class="icon-sonarr-form-info" title="Comma separated list of extra files to import, ie sub,nfo (.nfo will be imported as .nfo-orig)"/>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5 col-sm-pull-1">
|
||||
<input type="text" name="extraFileExtensions" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
@ -15,7 +15,7 @@
|
||||
{{#if value}}
|
||||
<span class="label label-success">{{label}}</span>
|
||||
{{else}}
|
||||
<span class="label">{{label}}</span>
|
||||
<span class="label label-default">{{label}}</span>
|
||||
{{/if}}
|
||||
{{/if_eq}}
|
||||
{{/each}}
|
||||
|
@ -11,7 +11,7 @@ var RenamePreviewLayout = require('../../Rename/RenamePreviewLayout');
|
||||
var ManualImportLayout = require('../../ManualImport/ManualImportLayout');
|
||||
var FileBrowserLayout = require('../FileBrowser/FileBrowserLayout');
|
||||
var MoviesDetailsLayout = require('../../Movies/Details/MoviesDetailsLayout');
|
||||
var EditFileView = require("../../Movies/Files/Edit/EditFileView");
|
||||
var EditFileView = require("../../Movies/Files/Media/Edit/EditFileView");
|
||||
|
||||
module.exports = Marionette.AppRouter.extend({
|
||||
initialize : function() {
|
||||
|
Loading…
Reference in New Issue
Block a user