mirror of
https://github.com/Radarr/Radarr.git
synced 2024-08-18 08:19:38 +02:00
New: Use MediaInfo on File Parsing
This commit is contained in:
parent
ada9b944dc
commit
bfc467dd96
@ -125,7 +125,7 @@ private Response Search()
|
||||
|
||||
var files = _diskScanService.GetVideoFiles(f.Path);
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(files.ToList(), m, true);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(files.ToList(), m);
|
||||
|
||||
var decision = decisions.Where(d => d.Approved && !d.Rejections.Any()).FirstOrDefault();
|
||||
|
||||
@ -136,10 +136,10 @@ private Response Search()
|
||||
m.MovieFile = new MovieFile
|
||||
{
|
||||
Path = local.Path,
|
||||
Edition = local.ParsedMovieInfo.Edition,
|
||||
Edition = local.Edition,
|
||||
Quality = local.Quality,
|
||||
MediaInfo = local.MediaInfo,
|
||||
ReleaseGroup = local.ParsedMovieInfo.ReleaseGroup,
|
||||
ReleaseGroup = local.ReleaseGroup,
|
||||
RelativePath = f.Path.GetRelativePath(local.Path)
|
||||
};
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ public void should_not_scan_extras_subfolder()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -113,7 +113,7 @@ public void should_not_scan_AppleDouble_subfolder()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -135,7 +135,7 @@ public void should_scan_extras_movie_and_subfolders()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 4), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 4), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -154,7 +154,7 @@ public void should_not_scan_subfolders_that_start_with_period()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -174,7 +174,7 @@ public void should_not_scan_subfolder_of_season_folder_that_starts_with_a_period
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -191,7 +191,7 @@ public void should_not_scan_Synology_eaDir()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -208,7 +208,7 @@ public void should_not_scan_thumb_folder()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -226,7 +226,7 @@ public void should_scan_dotHack_folder()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -243,7 +243,7 @@ public void should_find_files_at_root_of_series_folder()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 2), _movie), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -260,7 +260,7 @@ public void should_exclude_osx_metadata_files()
|
||||
Subject.Scan(_movie);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie, true), Times.Once());
|
||||
.Verify(v => v.GetImportDecisions(It.Is<List<string>>(l => l.Count == 1), _movie), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,11 +172,9 @@ public void should_delete_folder_if_files_were_imported_and_only_sample_files_re
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Movie>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
.Returns(DetectSampleResult.Sample);
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
@ -244,11 +242,9 @@ public void should_not_delete_if_there_is_large_rar_file()
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Movie>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
.Returns(DetectSampleResult.Sample);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
|
||||
@ -289,7 +285,7 @@ public void should_use_folder_if_folder_import()
|
||||
Subject.ProcessPath(fileName);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Movie>(), It.IsAny<DownloadClientItem>(), It.IsAny<ParsedMovieInfo>(), true, false), Times.Once());
|
||||
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Movie>(), It.IsAny<DownloadClientItem>(), It.IsAny<ParsedMovieInfo>(), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -313,7 +309,7 @@ public void should_not_use_folder_if_file_import()
|
||||
var result = Subject.ProcessPath(fileName);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Movie>(), It.IsAny<DownloadClientItem>(), null, true, false), Times.Once());
|
||||
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Movie>(), It.IsAny<DownloadClientItem>(), null, true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -355,11 +351,9 @@ public void should_not_delete_if_no_files_were_imported()
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Movie>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
.Returns(DetectSampleResult.Sample);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFileSize(It.IsAny<string>()))
|
||||
|
@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
{
|
||||
[TestFixture]
|
||||
//TODO: Update all of this for movies.
|
||||
public class ImportApprovedEpisodesFixture : CoreTest<ImportApprovedMovie>
|
||||
public class ImportApprovedMoviesFixture : CoreTest<ImportApprovedMovie>
|
||||
{
|
||||
private List<ImportDecision> _rejectedDecisions;
|
||||
private List<ImportDecision> _approvedDecisions;
|
||||
@ -51,10 +51,7 @@ public void Setup()
|
||||
Movie = movie,
|
||||
Path = Path.Combine(movie.Path, "30 Rock - S01E01 - Pilot.avi"),
|
||||
Quality = new QualityModel(),
|
||||
ParsedMovieInfo = new ParsedMovieInfo()
|
||||
{
|
||||
ReleaseGroup = "DRONE"
|
||||
}
|
||||
ReleaseGroup = "DRONE"
|
||||
}));
|
||||
|
||||
|
@ -15,29 +15,29 @@
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MovieFileMovingServiceTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MoveEpisodeFileFixture : CoreTest<MovieFileMovingService>
|
||||
public class MoveMovieFileFixture : CoreTest<MovieFileMovingService>
|
||||
{
|
||||
private Movie _series;
|
||||
private MovieFile _episodeFile;
|
||||
private LocalMovie _localEpisode;
|
||||
private Movie _movie;
|
||||
private MovieFile _movieFile;
|
||||
private LocalMovie _localMovie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Movie>.CreateNew()
|
||||
.With(s => s.Path = @"C:\Test\TV\Series".AsOsAgnostic())
|
||||
_movie = Builder<Movie>.CreateNew()
|
||||
.With(s => s.Path = @"C:\Test\Movies\Movie".AsOsAgnostic())
|
||||
.Build();
|
||||
|
||||
_episodeFile = Builder<MovieFile>.CreateNew()
|
||||
_movieFile = Builder<MovieFile>.CreateNew()
|
||||
.With(f => f.Path = null)
|
||||
.With(f => f.RelativePath = @"Season 1\File.avi")
|
||||
.With(f => f.RelativePath = @"File.avi")
|
||||
.Build();
|
||||
|
||||
_localEpisode = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.Movie = _series)
|
||||
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.Movie = _movie)
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
@ -46,9 +46,9 @@ public void Setup()
|
||||
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.BuildFilePath(It.IsAny<Movie>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns(@"C:\Test\TV\Series\File Name.avi".AsOsAgnostic());
|
||||
.Returns(@"C:\Test\Movies\Movie\File Name.avi".AsOsAgnostic());
|
||||
|
||||
var rootFolder = @"C:\Test\TV\".AsOsAgnostic();
|
||||
var rootFolder = @"C:\Test\Movies\".AsOsAgnostic();
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FolderExists(rootFolder))
|
||||
.Returns(true);
|
||||
@ -67,7 +67,7 @@ public void should_catch_UnauthorizedAccessException_during_folder_inheritance()
|
||||
.Setup(s => s.InheritFolderPermissions(It.IsAny<string>()))
|
||||
.Throws<UnauthorizedAccessException>();
|
||||
|
||||
Subject.MoveMovieFile(_episodeFile, _localEpisode);
|
||||
Subject.MoveMovieFile(_movieFile, _localMovie);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -79,13 +79,13 @@ public void should_catch_InvalidOperationException_during_folder_inheritance()
|
||||
.Setup(s => s.InheritFolderPermissions(It.IsAny<string>()))
|
||||
.Throws<InvalidOperationException>();
|
||||
|
||||
Subject.MoveMovieFile(_episodeFile, _localEpisode);
|
||||
Subject.MoveMovieFile(_movieFile, _localMovie);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_notify_on_series_folder_creation()
|
||||
public void should_notify_on_movie_folder_creation()
|
||||
{
|
||||
Subject.MoveMovieFile(_episodeFile, _localEpisode);
|
||||
Subject.MoveMovieFile(_movieFile, _localMovie);
|
||||
|
||||
Mocker.GetMock<IEventAggregator>()
|
||||
.Verify(s => s.PublishEvent<MovieFolderCreatedEvent>(It.Is<MovieFolderCreatedEvent>(p =>
|
||||
@ -93,13 +93,13 @@ public void should_notify_on_series_folder_creation()
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_notify_if_series_folder_already_exists()
|
||||
public void should_not_notify_if_movie_folder_already_exists()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FolderExists(_series.Path))
|
||||
.Setup(s => s.FolderExists(_movie.Path))
|
||||
.Returns(true);
|
||||
|
||||
Subject.MoveMovieFile(_episodeFile, _localEpisode);
|
||||
Subject.MoveMovieFile(_movieFile, _localMovie);
|
||||
|
||||
Mocker.GetMock<IEventAggregator>()
|
||||
.Verify(s => s.PublishEvent<MovieFolderCreatedEvent>(It.Is<MovieFolderCreatedEvent>(p =>
|
@ -0,0 +1,73 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AggregateLanguageFixture : CoreTest<AggregateLanguage>
|
||||
{
|
||||
private LocalMovie _localMovie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.DownloadClientMovieInfo = null)
|
||||
.With(l => l.FolderMovieInfo = null)
|
||||
.With(l => l.FileMovieInfo = null)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private ParsedMovieInfo GetParsedMovieInfo(Language language)
|
||||
{
|
||||
return new ParsedMovieInfo
|
||||
{
|
||||
Languages = new List<Language> { language }
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_file_language_when_only_file_info_is_known()
|
||||
{
|
||||
_localMovie.FileMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
|
||||
Subject.Aggregate(_localMovie, false).Languages.Should().Contain(_localMovie.FileMovieInfo.Languages);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_folder_language_when_folder_info_is_known()
|
||||
{
|
||||
_localMovie.FolderMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
_localMovie.FileMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
|
||||
Subject.Aggregate(_localMovie, false).Languages.Should().Contain(_localMovie.FolderMovieInfo.Languages);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_download_client_item_language_when_download_client_item_info_is_known()
|
||||
{
|
||||
_localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
_localMovie.FolderMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
_localMovie.FileMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
|
||||
Subject.Aggregate(_localMovie, false).Languages.Should().Contain(_localMovie.DownloadClientMovieInfo.Languages);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_file_language_when_file_language_is_higher_than_others()
|
||||
{
|
||||
_localMovie.DownloadClientMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
_localMovie.FolderMovieInfo = GetParsedMovieInfo(Language.English);
|
||||
_localMovie.FileMovieInfo = GetParsedMovieInfo(Language.French);
|
||||
|
||||
Subject.Aggregate(_localMovie, false).Languages.Should().Contain(_localMovie.FileMovieInfo.Languages);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AugmentQualityFixture : CoreTest<AggregateQuality>
|
||||
{
|
||||
private Mock<IAugmentQuality> _mediaInfoAugmenter;
|
||||
private Mock<IAugmentQuality> _fileExtensionAugmenter;
|
||||
private Mock<IAugmentQuality> _nameAugmenter;
|
||||
|
||||
private IEnumerable<IAugmentQuality> _qualityAugmenters;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_mediaInfoAugmenter = new Mock<IAugmentQuality>();
|
||||
_fileExtensionAugmenter = new Mock<IAugmentQuality>();
|
||||
_nameAugmenter = new Mock<IAugmentQuality>();
|
||||
|
||||
_mediaInfoAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(AugmentQualityResult.ResolutionOnly(Resolution.R1080P, Confidence.MediaInfo));
|
||||
|
||||
_fileExtensionAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Fallback, Resolution.R720P, Confidence.Fallback, new Revision()));
|
||||
|
||||
_nameAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Default, Resolution.R480P, Confidence.Default, new Revision()));
|
||||
}
|
||||
|
||||
private void GivenAugmenters(params Mock<IAugmentQuality>[] mocks)
|
||||
{
|
||||
Mocker.SetConstant<IEnumerable<IAugmentQuality>>(mocks.Select(c => c.Object));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_HDTV720_from_extension_when_other_augments_are_null()
|
||||
{
|
||||
var nullMock = new Mock<IAugmentQuality>();
|
||||
nullMock.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns<LocalMovie>(l => null);
|
||||
|
||||
GivenAugmenters(_fileExtensionAugmenter, nullMock);
|
||||
|
||||
var result = Subject.Aggregate(new LocalMovie(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Extension);
|
||||
result.Quality.Quality.Should().Be(Quality.HDTV720p);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_SDTV_when_HDTV720_came_from_extension()
|
||||
{
|
||||
GivenAugmenters(_fileExtensionAugmenter, _nameAugmenter);
|
||||
|
||||
var result = Subject.Aggregate(new LocalMovie(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.Name);
|
||||
result.Quality.Quality.Should().Be(Quality.SDTV);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_HDTV1080p_when_HDTV720_came_from_extension_and_mediainfo_indicates_1080()
|
||||
{
|
||||
GivenAugmenters(_fileExtensionAugmenter, _mediaInfoAugmenter);
|
||||
|
||||
var result = Subject.Aggregate(new LocalMovie(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.MediaInfo);
|
||||
result.Quality.Quality.Should().Be(Quality.HDTV1080p);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_HDTV1080p_when_SDTV_came_from_name_and_mediainfo_indicates_1080()
|
||||
{
|
||||
GivenAugmenters(_nameAugmenter, _mediaInfoAugmenter);
|
||||
|
||||
var result = Subject.Aggregate(new LocalMovie(), false);
|
||||
|
||||
result.Quality.QualityDetectionSource.Should().Be(QualityDetectionSource.MediaInfo);
|
||||
result.Quality.Quality.Should().Be(Quality.HDTV1080p);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
[TestFixture]
|
||||
public class AugmentQualityFromMediaInfoFixture : CoreTest<AugmentQualityFromMediaInfo>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_null_if_media_info_is_null()
|
||||
{
|
||||
var localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.MediaInfo = null)
|
||||
.Build();
|
||||
|
||||
Subject.AugmentQuality(localMovie).Should().Be(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_if_media_info_width_is_zero()
|
||||
{
|
||||
var mediaInfo = Builder<MediaInfoModel>.CreateNew()
|
||||
.With(m => m.Width = 0)
|
||||
.Build();
|
||||
|
||||
var localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.MediaInfo = mediaInfo)
|
||||
.Build();
|
||||
|
||||
Subject.AugmentQuality(localMovie).Should().Be(null);
|
||||
}
|
||||
|
||||
[TestCase(4096, Resolution.R2160P)] // True 4K
|
||||
[TestCase(4000, Resolution.R2160P)]
|
||||
[TestCase(3840, Resolution.R2160P)] // 4K UHD
|
||||
[TestCase(3200, Resolution.R2160P)]
|
||||
[TestCase(2000, Resolution.R1080P)]
|
||||
[TestCase(1920, Resolution.R1080P)] // Full HD
|
||||
[TestCase(1800, Resolution.R1080P)]
|
||||
[TestCase(1490, Resolution.R720P)]
|
||||
[TestCase(1280, Resolution.R720P)] // HD
|
||||
[TestCase(1200, Resolution.R720P)]
|
||||
[TestCase(800, Resolution.R480P)]
|
||||
[TestCase(720, Resolution.R480P)] // SDTV
|
||||
[TestCase(600, Resolution.R480P)]
|
||||
[TestCase(100, Resolution.R480P)]
|
||||
public void should_return_closest_resolution(int mediaInfoWidth, Resolution expectedResolution)
|
||||
{
|
||||
var mediaInfo = Builder<MediaInfoModel>.CreateNew()
|
||||
.With(m => m.Width = mediaInfoWidth)
|
||||
.Build();
|
||||
|
||||
var localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.MediaInfo = mediaInfo)
|
||||
.Build();
|
||||
|
||||
var result = Subject.AugmentQuality(localMovie);
|
||||
|
||||
result.Should().NotBe(null);
|
||||
result.Resolution.Should().Be(expectedResolution);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,11 +10,12 @@
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MovieImport
|
||||
{
|
||||
[TestFixture]
|
||||
public class SampleServiceFixture : CoreTest<DetectSample>
|
||||
public class DetectSampleFixture : CoreTest<DetectSample>
|
||||
{
|
||||
private Movie _movie;
|
||||
private LocalMovie _localMovie;
|
||||
@ -26,17 +27,13 @@ public void Setup()
|
||||
.With(s => s.Runtime = 30)
|
||||
.Build();
|
||||
|
||||
_localMovie = new LocalMovie
|
||||
{
|
||||
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
|
||||
Movie = _movie,
|
||||
Quality = new QualityModel(Quality.HDTV720p)
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenFileSize(long size)
|
||||
{
|
||||
_localMovie.Size = size;
|
||||
_localMovie = new LocalMovie
|
||||
{
|
||||
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
|
||||
Movie = _movie,
|
||||
Quality = new QualityModel(Quality.HDTV720p)
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenRuntime(int seconds)
|
||||
@ -51,7 +48,7 @@ public void should_return_false_for_flv()
|
||||
{
|
||||
_localMovie.Path = @"C:\Test\some.show.s01e01.flv";
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
@ -61,7 +58,7 @@ public void should_return_false_for_strm()
|
||||
{
|
||||
_localMovie.Path = @"C:\Test\some.show.s01e01.strm";
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
@ -70,12 +67,9 @@ public void should_return_false_for_strm()
|
||||
public void should_use_runtime()
|
||||
{
|
||||
GivenRuntime(120);
|
||||
GivenFileSize(1000.Megabytes());
|
||||
|
||||
Subject.IsSample(_localMovie.Movie,
|
||||
_localMovie.Quality,
|
||||
_localMovie.Path,
|
||||
_localMovie.Size,
|
||||
false);
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(v => v.GetRunTime(It.IsAny<string>()), Times.Once());
|
||||
@ -86,7 +80,7 @@ public void should_return_true_if_runtime_is_less_than_minimum()
|
||||
{
|
||||
GivenRuntime(60);
|
||||
|
||||
ShouldBeTrue();
|
||||
ShouldBeSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -94,7 +88,7 @@ public void should_return_false_if_runtime_greater_than_minimum()
|
||||
{
|
||||
GivenRuntime(600);
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -103,51 +97,53 @@ public void should_return_false_if_runtime_greater_than_webisode_minimum()
|
||||
_movie.Runtime = 6;
|
||||
GivenRuntime(299);
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_acceptable_size()
|
||||
public void should_return_false_if_runtime_greater_than_anime_short_minimum()
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(s => s.GetRunTime(It.IsAny<string>()))
|
||||
.Throws<DllNotFoundException>();
|
||||
_movie.Runtime = 2;
|
||||
GivenRuntime(60);
|
||||
|
||||
GivenFileSize(1000.Megabytes());
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_undersize()
|
||||
public void should_return_true_if_runtime_less_than_anime_short_minimum()
|
||||
{
|
||||
_movie.Runtime = 2;
|
||||
GivenRuntime(10);
|
||||
|
||||
ShouldBeSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_indeterminate_if_mediainfo_result_is_null()
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(s => s.GetRunTime(It.IsAny<string>()))
|
||||
.Throws<DllNotFoundException>();
|
||||
.Returns((TimeSpan?)null);
|
||||
|
||||
GivenFileSize(1.Megabytes());
|
||||
ShouldBeTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void ShouldBeTrue()
|
||||
{
|
||||
Subject.IsSample(_localMovie.Movie,
|
||||
_localMovie.Quality,
|
||||
_localMovie.Path,
|
||||
_localMovie.Size,
|
||||
false).Should().BeTrue();
|
||||
}
|
||||
|
||||
private void ShouldBeFalse()
|
||||
{
|
||||
Subject.IsSample(_localMovie.Movie,
|
||||
_localMovie.Quality,
|
||||
_localMovie.Path,
|
||||
_localMovie.Size,
|
||||
false).Should().BeFalse();
|
||||
false).Should().Be(DetectSampleResult.Indeterminate);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
private void ShouldBeSample()
|
||||
{
|
||||
Subject.IsSample(_localMovie.Movie,
|
||||
_localMovie.Path,
|
||||
false).Should().Be(DetectSampleResult.Sample);
|
||||
}
|
||||
|
||||
private void ShouldBeNotSample()
|
||||
{
|
||||
Subject.IsSample(_localMovie.Movie,
|
||||
_localMovie.Path,
|
||||
false).Should().Be(DetectSampleResult.NotSample);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
using NzbDrone.Test.Common;
|
||||
using FizzWare.NBuilder;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MovieImport
|
||||
{
|
||||
@ -75,10 +76,6 @@ public void Setup()
|
||||
Quality = _quality
|
||||
};
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalMovie(It.IsAny<string>(), It.IsAny<ParsedMovieInfo>(), It.IsAny<Movie>(), It.IsAny<List<object>>(), It.IsAny<bool>()))
|
||||
.Returns(_localMovie);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.ParseMinimalPathMovieInfo(It.IsAny<string>()))
|
||||
.Returns(_fileInfo);
|
||||
@ -100,6 +97,16 @@ private void GivenVideoFiles(IEnumerable<string> videoFiles)
|
||||
.Returns(_videoFiles);
|
||||
}
|
||||
|
||||
private void GivenAugmentationSuccess()
|
||||
{
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Setup(s => s.Augment(It.IsAny<LocalMovie>(), It.IsAny<bool>()))
|
||||
.Callback<LocalMovie, bool>((localMovie, otherFiles) =>
|
||||
{
|
||||
localMovie.Movie = _localMovie.Movie;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_call_all_specifications()
|
||||
{
|
||||
@ -108,12 +115,12 @@ public void should_call_all_specifications()
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, new Movie(), downloadClientItem, null, false);
|
||||
|
||||
_fail1.Verify(c => c.IsSatisfiedBy(_localMovie, downloadClientItem), Times.Once());
|
||||
_fail2.Verify(c => c.IsSatisfiedBy(_localMovie, downloadClientItem), Times.Once());
|
||||
_fail3.Verify(c => c.IsSatisfiedBy(_localMovie, downloadClientItem), Times.Once());
|
||||
_pass1.Verify(c => c.IsSatisfiedBy(_localMovie, downloadClientItem), Times.Once());
|
||||
_pass2.Verify(c => c.IsSatisfiedBy(_localMovie, downloadClientItem), Times.Once());
|
||||
_pass3.Verify(c => c.IsSatisfiedBy(_localMovie, downloadClientItem), Times.Once());
|
||||
_fail1.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalMovie>(), downloadClientItem), Times.Once());
|
||||
_fail2.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalMovie>(), downloadClientItem), Times.Once());
|
||||
_fail3.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalMovie>(), downloadClientItem), Times.Once());
|
||||
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalMovie>(), downloadClientItem), Times.Once());
|
||||
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalMovie>(), downloadClientItem), Times.Once());
|
||||
_pass3.Verify(c => c.IsSatisfiedBy(It.IsAny<LocalMovie>(), downloadClientItem), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -160,8 +167,8 @@ public void should_not_blowup_the_process_due_to_failed_parse()
|
||||
{
|
||||
GivenSpecifications(_pass1);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalMovie(It.IsAny<string>(), It.IsAny<ParsedMovieInfo>(), It.IsAny<Movie>(), It.IsAny<List<object>>(), It.IsAny<bool>()))
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<bool>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_videoFiles = new List<string>
|
||||
@ -175,8 +182,8 @@ public void should_not_blowup_the_process_due_to_failed_parse()
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _movie);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(c => c.GetLocalMovie(It.IsAny<string>(), It.IsAny<ParsedMovieInfo>(), It.IsAny<Movie>(), It.IsAny<List<object>>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Verify(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(3);
|
||||
}
|
||||
@ -196,88 +203,16 @@ public void should_call_parsing_service_with_filename_as_simpletitle()
|
||||
|
||||
var fileNames = _videoFiles.Select(System.IO.Path.GetFileName);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(
|
||||
c => c.GetLocalMovie(It.IsAny<string>(),
|
||||
It.Is<ParsedMovieInfo>(p => fileNames.Contains(p.SimpleReleaseTitle)), It.IsAny<Movie>(),
|
||||
It.IsAny<List<object>>(), It.IsAny<bool>()), Times.Exactly(_videoFiles.Count));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_file_quality_if_folder_quality_is_null()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _movie);
|
||||
|
||||
result.Single().LocalMovie.Quality.Should().Be(_fileInfo.Quality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_file_quality_if_file_quality_was_determined_by_name()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _movie, null, new ParsedMovieInfo{Quality = new QualityModel(Quality.SDTV)}, true);
|
||||
|
||||
result.Single().LocalMovie.Quality.Should().Be(_fileInfo.Quality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_quality_when_file_quality_was_determined_by_the_extension()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
GivenVideoFiles(new string[] { @"C:\Test\Unsorted\The.Office.S03E115.mkv".AsOsAgnostic() });
|
||||
|
||||
_localMovie.Path = _videoFiles.Single();
|
||||
_localMovie.Quality.QualitySource = QualitySource.Extension;
|
||||
_localMovie.Quality.Quality = Quality.HDTV720p;
|
||||
|
||||
var expectedQuality = new QualityModel(Quality.SDTV);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _movie, null, new ParsedMovieInfo { Quality = expectedQuality }, true);
|
||||
|
||||
result.Single().LocalMovie.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_quality_when_greater_than_file_quality()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
GivenVideoFiles(new string[] { @"C:\Test\Unsorted\The.Office.S03E115.mkv".AsOsAgnostic() });
|
||||
|
||||
_localMovie.Path = _videoFiles.Single();
|
||||
_localMovie.Quality.Quality = Quality.HDTV720p;
|
||||
|
||||
var expectedQuality = new QualityModel(Quality.Bluray720p);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _movie, null, new ParsedMovieInfo { Quality = expectedQuality }, true);
|
||||
|
||||
result.Single().LocalMovie.Quality.Should().Be(expectedQuality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_quality_when_it_is_unknown()
|
||||
{
|
||||
GivenSpecifications(_pass1, _pass2, _pass3);
|
||||
|
||||
_movie.Profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.Unknown)
|
||||
};
|
||||
|
||||
|
||||
var folderQuality = new QualityModel(Quality.Unknown);
|
||||
|
||||
var result = Subject.GetImportDecisions(_videoFiles, _movie, null, new ParsedMovieInfo { Quality = folderQuality}, true);
|
||||
|
||||
result.Single().LocalMovie.Quality.Should().Be(_quality);
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<bool>()))
|
||||
.Throws<TestException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_a_decision_when_exception_is_caught()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalMovie(It.IsAny<string>(), It.IsAny<ParsedMovieInfo>(), It.IsAny<Movie>(), It.IsAny<List<object>>(), It.IsAny<bool>()))
|
||||
Mocker.GetMock<IAggregationService>()
|
||||
.Setup(c => c.Augment(It.IsAny<LocalMovie>(), It.IsAny<bool>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_videoFiles = new List<string>
|
||||
|
@ -18,7 +18,7 @@ public void Setup()
|
||||
{
|
||||
_localMovie = Builder<LocalMovie>.CreateNew()
|
||||
.With(l => l.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic())
|
||||
.With(l => l.ParsedMovieInfo =
|
||||
.With(l => l.FileMovieInfo =
|
||||
Builder<ParsedMovieInfo>.CreateNew()
|
||||
.Build())
|
||||
.Build();
|
||||
|
@ -291,10 +291,13 @@
|
||||
<Compile Include="MediaFiles\DiskScanServiceTests\ScanFixture.cs" />
|
||||
<Compile Include="MediaFiles\DownloadedMoviesCommandServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\DownloadedMoviesImportServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatVideoDynamicRangeFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieFileMovingServiceTests\MoveMovieFileFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\AggregateLanguageFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\AggregateQualityFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromMediaInfoFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\ImportDecisionMakerFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\SampleServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\DetectSampleFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\FreeSpaceSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\GrabbedReleaseQualityFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\MatchesFolderSpecificationFixture.cs" />
|
||||
@ -302,7 +305,7 @@
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\NotUnpackingSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\SameFileSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\UpgradeSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
|
||||
<Compile Include="MediaFiles\ImportApprovedMoviesFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
|
||||
<Compile Include="MediaFiles\UpdateMovieFileQualityServiceFixture.cs" />
|
||||
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
|
||||
|
@ -43,7 +43,7 @@ public void should_correct_resolution(Resolution resolution, Source source, Reso
|
||||
|
||||
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
|
||||
movieInfo.Quality.Resolution.ShouldBeEquivalentTo(realResolution);
|
||||
movieInfo.Quality.QualitySource.ShouldBeEquivalentTo(QualitySource.MediaInfo);
|
||||
movieInfo.Quality.QualityDetectionSource.ShouldBeEquivalentTo(QualityDetectionSource.MediaInfo);
|
||||
}
|
||||
|
||||
[TestCase(Resolution.R720P, Source.BLURAY, Resolution.R1080P, Modifier.BRDISK)]
|
||||
@ -83,7 +83,7 @@ public void should_not_correct_resolution(Resolution resolution, Source source,
|
||||
|
||||
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
|
||||
movieInfo.Quality.Resolution.ShouldBeEquivalentTo(resolution);
|
||||
movieInfo.Quality.QualitySource.ShouldBeEquivalentTo(QualitySource.Name);
|
||||
movieInfo.Quality.QualityDetectionSource.ShouldBeEquivalentTo(QualityDetectionSource.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,7 +331,7 @@ public void should_parse_quality_from_other_source(string qualityString, Source
|
||||
[TestCase("White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA")]
|
||||
public void should_parse_quality_from_name(string title)
|
||||
{
|
||||
QualityParser.ParseQuality(title).QualitySource.Should().Be(QualitySource.Name);
|
||||
QualityParser.ParseQuality(title).QualityDetectionSource.Should().Be(QualityDetectionSource.Name);
|
||||
}
|
||||
|
||||
[TestCase("Revolution.S01E02.Chained.Heat.mkv")]
|
||||
@ -341,7 +341,7 @@ public void should_parse_quality_from_name(string title)
|
||||
[TestCase("[CR] Sailor Moon - 004 [48CE2D0F].avi")]
|
||||
public void should_parse_quality_from_extension(string title)
|
||||
{
|
||||
QualityParser.ParseQuality(title).QualitySource.Should().Be(QualitySource.Extension);
|
||||
QualityParser.ParseQuality(title).QualityDetectionSource.Should().Be(QualityDetectionSource.Extension);
|
||||
}
|
||||
|
||||
[TestCase("Movie.Title.2016.1080p.KORSUB.WEBRip.x264.AAC2.0-RADARR", "korsub")]
|
||||
|
@ -119,7 +119,7 @@ public void Scan(Movie movie)
|
||||
CleanMediaFiles(movie, mediaFileList);
|
||||
|
||||
var decisionsStopwatch = Stopwatch.StartNew();
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie, true);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, movie);
|
||||
decisionsStopwatch.Stop();
|
||||
_logger.Trace("Import decisions complete for: {0} [{1}]", movie, decisionsStopwatch.Elapsed);
|
||||
_importApprovedMovies.Import(decisions, false);
|
||||
|
@ -130,7 +130,7 @@ public bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Movie movie)
|
||||
|
||||
var size = _diskProvider.GetFileSize(videoFile);
|
||||
|
||||
if (!_detectSample.IsSample(movie, QualityParser.ParseQuality(Path.GetFileName(videoFile)), videoFile, size, false))
|
||||
if (_detectSample.IsSample(movie, videoFile, false) == DetectSampleResult.NotSample)
|
||||
{
|
||||
_logger.Warn("Non-sample file detected: [{0}]", videoFile);
|
||||
return false;
|
||||
@ -198,7 +198,7 @@ private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, ImportMode
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, downloadClientItem, folderInfo, true, false);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), movie, downloadClientItem, folderInfo, true);
|
||||
var importResults = _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
|
||||
|
||||
if ((downloadClientItem == null || downloadClientItem.CanBeRemoved) &&
|
||||
@ -252,7 +252,7 @@ private List<ImportResult> ProcessFile(FileInfo fileInfo, ImportMode importMode,
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, downloadClientItem, null, true, false);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, movie, downloadClientItem, null, true);
|
||||
|
||||
return _importApprovedMovie.Import(decisions, true, downloadClientItem, importMode);
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
|
||||
{
|
||||
public class AugmentingFailedException : NzbDroneException
|
||||
{
|
||||
public AugmentingFailedException(string message, params object[] args) : base(message, args)
|
||||
{
|
||||
}
|
||||
|
||||
public AugmentingFailedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public AugmentingFailedException(string message, Exception innerException, params object[] args) : base(message, innerException, args)
|
||||
{
|
||||
}
|
||||
|
||||
public AugmentingFailedException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation
|
||||
{
|
||||
public interface IAggregationService
|
||||
{
|
||||
LocalMovie Augment(LocalMovie localMovie, bool otherFiles);
|
||||
}
|
||||
|
||||
public class AggregationService : IAggregationService
|
||||
{
|
||||
private readonly IEnumerable<IAggregateLocalMovie> _augmenters;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public AggregationService(IEnumerable<IAggregateLocalMovie> augmenters,
|
||||
IDiskProvider diskProvider,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
_augmenters = augmenters;
|
||||
_diskProvider = diskProvider;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LocalMovie Augment(LocalMovie localMovie, bool otherFiles)
|
||||
{
|
||||
var isMediaFile = MediaFileExtensions.Extensions.Contains(Path.GetExtension(localMovie.Path));
|
||||
|
||||
if (localMovie.DownloadClientMovieInfo == null &&
|
||||
localMovie.FolderMovieInfo == null &&
|
||||
localMovie.FileMovieInfo == null)
|
||||
{
|
||||
if (isMediaFile)
|
||||
{
|
||||
throw new AugmentingFailedException("Unable to parse movie info from path: {0}", localMovie.Path);
|
||||
}
|
||||
}
|
||||
|
||||
localMovie.Size = _diskProvider.GetFileSize(localMovie.Path);
|
||||
|
||||
if (isMediaFile && (!localMovie.ExistingFile || _configService.EnableMediaInfo))
|
||||
{
|
||||
localMovie.MediaInfo = _videoFileInfoReader.GetMediaInfo(localMovie.Path);
|
||||
}
|
||||
|
||||
foreach (var augmenter in _augmenters)
|
||||
{
|
||||
try
|
||||
{
|
||||
augmenter.Aggregate(localMovie, otherFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return localMovie;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
public class AggregateLanguage : IAggregateLocalMovie
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public AggregateLanguage(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles)
|
||||
{
|
||||
// Get languages in preferred order, download client item, folder and finally file.
|
||||
// Non-English languages will be preferred later, in the event there is a conflict
|
||||
// between parsed languages the more preferred item will be used.
|
||||
|
||||
var languages = new List<Language>();
|
||||
|
||||
languages.AddRange(GetLanguage(localMovie.DownloadClientMovieInfo));
|
||||
languages.AddRange(GetLanguage(localMovie.FolderMovieInfo));
|
||||
languages.AddRange(GetLanguage(localMovie.FileMovieInfo));
|
||||
|
||||
var language = new List<Language> { languages.FirstOrDefault(l => l != Language.English) ?? Language.English };
|
||||
|
||||
_logger.Debug("Using language: {0}", language.First());
|
||||
|
||||
localMovie.Languages = language;
|
||||
|
||||
return localMovie;
|
||||
}
|
||||
|
||||
private List<Language> GetLanguage(ParsedMovieInfo parsedMovieInfo)
|
||||
{
|
||||
if (parsedMovieInfo == null)
|
||||
{
|
||||
// English is the default language when otherwise unknown
|
||||
|
||||
return new List<Language> { Language.English };
|
||||
}
|
||||
|
||||
return parsedMovieInfo.Languages;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
public class AggregateQuality : IAggregateLocalMovie
|
||||
{
|
||||
private readonly IEnumerable<IAugmentQuality> _augmentQualities;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public AggregateQuality(IEnumerable<IAugmentQuality> augmentQualities,
|
||||
Logger logger)
|
||||
{
|
||||
_augmentQualities = augmentQualities;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles)
|
||||
{
|
||||
var augmentedQualities = _augmentQualities.Select(a => a.AugmentQuality(localMovie))
|
||||
.Where(a => a != null)
|
||||
.OrderBy(a => a.SourceConfidence);
|
||||
|
||||
var source = Source.UNKNOWN;
|
||||
var sourceConfidence = Confidence.Default;
|
||||
var resolution = Resolution.Unknown;
|
||||
var resolutionConfidence = Confidence.Default;
|
||||
var revison = new Revision();
|
||||
|
||||
foreach (var augmentedQuality in augmentedQualities)
|
||||
{
|
||||
if (augmentedQuality.Source > source ||
|
||||
augmentedQuality.SourceConfidence > sourceConfidence && augmentedQuality.Source != Source.UNKNOWN)
|
||||
{
|
||||
source = augmentedQuality.Source;
|
||||
sourceConfidence = augmentedQuality.SourceConfidence;
|
||||
}
|
||||
|
||||
if (augmentedQuality.Resolution > resolution ||
|
||||
augmentedQuality.ResolutionConfidence > resolutionConfidence && augmentedQuality.Resolution > 0)
|
||||
{
|
||||
resolution = augmentedQuality.Resolution;
|
||||
resolutionConfidence = augmentedQuality.ResolutionConfidence;
|
||||
}
|
||||
|
||||
if (augmentedQuality.Revision != null && augmentedQuality.Revision > revison)
|
||||
{
|
||||
revison = augmentedQuality.Revision;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Trace("Finding quality. Source: {0}. Resolution: {1}", source, resolution);
|
||||
|
||||
var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution), revison);
|
||||
|
||||
if (resolutionConfidence == Confidence.MediaInfo)
|
||||
{
|
||||
quality.QualityDetectionSource = QualityDetectionSource.MediaInfo;
|
||||
}
|
||||
else if (sourceConfidence == Confidence.Fallback || resolutionConfidence == Confidence.Fallback)
|
||||
{
|
||||
quality.QualityDetectionSource = QualityDetectionSource.Extension;
|
||||
}
|
||||
else
|
||||
{
|
||||
quality.QualityDetectionSource = QualityDetectionSource.Name;
|
||||
}
|
||||
|
||||
_logger.Debug("Using quality: {0}", quality);
|
||||
|
||||
localMovie.Quality = quality;
|
||||
|
||||
return localMovie;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
public class AggregateReleaseGroup : IAggregateLocalMovie
|
||||
{
|
||||
public LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles)
|
||||
{
|
||||
var releaseGroup = localMovie.DownloadClientMovieInfo?.ReleaseGroup;
|
||||
|
||||
if (releaseGroup.IsNullOrWhiteSpace())
|
||||
{
|
||||
releaseGroup = localMovie.FolderMovieInfo?.ReleaseGroup;
|
||||
}
|
||||
|
||||
if (releaseGroup.IsNullOrWhiteSpace())
|
||||
{
|
||||
releaseGroup = localMovie.FileMovieInfo?.ReleaseGroup;
|
||||
}
|
||||
|
||||
localMovie.ReleaseGroup = releaseGroup;
|
||||
|
||||
return localMovie;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
public class AugmentQualityFromDownloadClientItem : IAugmentQuality
|
||||
{
|
||||
public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
{
|
||||
var quality = localMovie.DownloadClientMovieInfo?.Quality;
|
||||
|
||||
if (quality == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AugmentQualityResult(quality.Quality.Source,
|
||||
Confidence.Tag,
|
||||
quality.Quality.Resolution,
|
||||
Confidence.Tag,
|
||||
quality.Revision);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
public class AugmentQualityFromFileName : IAugmentQuality
|
||||
{
|
||||
public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
{
|
||||
var quality = localMovie.FileMovieInfo?.Quality;
|
||||
|
||||
if (quality == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var confidence = quality.QualityDetectionSource == QualityDetectionSource.Extension
|
||||
? Confidence.Fallback
|
||||
: Confidence.Tag;
|
||||
|
||||
return new AugmentQualityResult(quality.Quality.Source,
|
||||
confidence,
|
||||
quality.Quality.Resolution,
|
||||
confidence,
|
||||
quality.Revision);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
public class AugmentQualityFromFolder : IAugmentQuality
|
||||
{
|
||||
public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
{
|
||||
var quality = localMovie.FolderMovieInfo?.Quality;
|
||||
|
||||
if (quality == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AugmentQualityResult(quality.Quality.Source,
|
||||
Confidence.Tag,
|
||||
quality.Quality.Resolution,
|
||||
Confidence.Tag,
|
||||
quality.Revision);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
public class AugmentQualityFromMediaInfo : IAugmentQuality
|
||||
{
|
||||
public AugmentQualityResult AugmentQuality(LocalMovie localMovie)
|
||||
{
|
||||
if (localMovie.MediaInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var width = localMovie.MediaInfo.Width;
|
||||
|
||||
if (width >= 3200)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R2160P, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
if (width >= 1800)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R1080P, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
if (width >= 1200)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R720P, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
if (width > 0)
|
||||
{
|
||||
return AugmentQualityResult.ResolutionOnly(Resolution.R480P, Confidence.MediaInfo);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
public class AugmentQualityResult
|
||||
{
|
||||
public Source Source { get; set; }
|
||||
public Confidence SourceConfidence { get; set; }
|
||||
public Resolution Resolution { get; set; }
|
||||
public Confidence ResolutionConfidence { get; set; }
|
||||
public Revision Revision { get; set; }
|
||||
|
||||
public AugmentQualityResult(Source source,
|
||||
Confidence sourceConfidence,
|
||||
Resolution resolution,
|
||||
Confidence resolutionConfidence,
|
||||
Revision revision)
|
||||
{
|
||||
Source = source;
|
||||
SourceConfidence = sourceConfidence;
|
||||
Resolution = resolution;
|
||||
ResolutionConfidence = resolutionConfidence;
|
||||
Revision = revision;
|
||||
}
|
||||
|
||||
public static AugmentQualityResult SourceOnly(Source source, Confidence sourceConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(source, sourceConfidence, 0, Confidence.Default, null);
|
||||
}
|
||||
|
||||
public static AugmentQualityResult ResolutionOnly(Resolution resolution, Confidence resolutionConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, resolution, resolutionConfidence, null);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
public enum Confidence
|
||||
{
|
||||
Fallback,
|
||||
Default,
|
||||
Tag,
|
||||
MediaInfo
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
{
|
||||
public interface IAugmentQuality
|
||||
{
|
||||
AugmentQualityResult AugmentQuality(LocalMovie localMovie);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
||||
{
|
||||
public interface IAggregateLocalMovie
|
||||
{
|
||||
LocalMovie Aggregate(LocalMovie localMovie, bool otherFiles);
|
||||
}
|
||||
}
|
@ -1,17 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public interface IDetectSample
|
||||
{
|
||||
bool IsSample(Movie movie, QualityModel quality, string path, long size, bool isSpecial);
|
||||
DetectSampleResult IsSample(Movie movie, string path, bool isSpecial);
|
||||
}
|
||||
|
||||
public class DetectSample : IDetectSample
|
||||
@ -19,23 +16,18 @@ public class DetectSample : IDetectSample
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly Logger _logger;
|
||||
|
||||
//private static List<Quality> _largeSampleSizeQualities = new List<Quality> { Quality.HDTV1080p, Quality.WEBDL1080p, Quality.Bluray1080p };
|
||||
private static List<Resolution> _largeSampleSizeResolutions = new List<Resolution>{Resolution.R1080P, Resolution.R2160P};
|
||||
|
||||
public DetectSample(IVideoFileInfoReader videoFileInfoReader, Logger logger)
|
||||
{
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public static long SampleSizeLimit => 70.Megabytes();
|
||||
|
||||
public bool IsSample(Movie movie, QualityModel quality, string path, long size, bool isSpecial)
|
||||
public DetectSampleResult IsSample(Movie movie, string path, bool isSpecial)
|
||||
{
|
||||
if (isSpecial)
|
||||
{
|
||||
_logger.Debug("Special, skipping sample check");
|
||||
return false;
|
||||
return DetectSampleResult.NotSample;
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(path);
|
||||
@ -43,72 +35,64 @@ public bool IsSample(Movie movie, QualityModel quality, string path, long size,
|
||||
if (extension != null && extension.Equals(".flv", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Debug("Skipping sample check for .flv file");
|
||||
return false;
|
||||
return DetectSampleResult.NotSample;
|
||||
}
|
||||
|
||||
if (extension != null && extension.Equals(".strm", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Debug("Skipping sample check for .strm file");
|
||||
return false;
|
||||
return DetectSampleResult.NotSample;
|
||||
}
|
||||
|
||||
try
|
||||
// TODO: Use MediaInfo from the import process, no need to re-process the file again here
|
||||
var runTime = _videoFileInfoReader.GetRunTime(path);
|
||||
|
||||
if (!runTime.HasValue)
|
||||
{
|
||||
var runTime = _videoFileInfoReader.GetRunTime(path);
|
||||
var minimumRuntime = GetMinimumAllowedRuntime(movie);
|
||||
|
||||
if (runTime.Value.TotalMinutes.Equals(0))
|
||||
{
|
||||
_logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (runTime.Value.TotalSeconds < minimumRuntime)
|
||||
{
|
||||
_logger.Debug("[{0}] appears to be a sample. Runtime: {1} seconds. Expected at least: {2} seconds", path, runTime, minimumRuntime);
|
||||
return true;
|
||||
}
|
||||
_logger.Error("Failed to get runtime from the file, make sure mediainfo is available");
|
||||
return DetectSampleResult.Indeterminate;
|
||||
}
|
||||
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
_logger.Debug("Falling back to file size detection");
|
||||
var minimumRuntime = GetMinimumAllowedRuntime(movie);
|
||||
|
||||
return CheckSize(size, quality);
|
||||
if (runTime.Value.TotalMinutes.Equals(0))
|
||||
{
|
||||
_logger.Error("[{0}] has a runtime of 0, is it a valid video file?", path);
|
||||
return DetectSampleResult.Sample;
|
||||
}
|
||||
|
||||
if (runTime.Value.TotalSeconds < minimumRuntime)
|
||||
{
|
||||
_logger.Debug("[{0}] appears to be a sample. Runtime: {1} seconds. Expected at least: {2} seconds", path, runTime, minimumRuntime);
|
||||
return DetectSampleResult.Sample;
|
||||
}
|
||||
|
||||
_logger.Debug("Runtime is over 90 seconds");
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckSize(long size, QualityModel quality)
|
||||
{
|
||||
if (_largeSampleSizeResolutions.Contains(quality.Resolution))
|
||||
{
|
||||
if (size < SampleSizeLimit * 2)
|
||||
{
|
||||
_logger.Debug("1080p file is less than sample limit");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (size < SampleSizeLimit)
|
||||
{
|
||||
_logger.Debug("File is less than sample limit");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return DetectSampleResult.NotSample;
|
||||
}
|
||||
|
||||
private int GetMinimumAllowedRuntime(Movie movie)
|
||||
{
|
||||
if (movie.Runtime < 1)
|
||||
//Anime short - 15 seconds
|
||||
if (movie.Runtime <= 3)
|
||||
{
|
||||
return 5 * 60;
|
||||
return 15;
|
||||
}
|
||||
|
||||
return movie.Runtime / 5 * 60;
|
||||
//Webisodes - 90 seconds
|
||||
if (movie.Runtime <= 10)
|
||||
{
|
||||
return 90;
|
||||
}
|
||||
|
||||
//30 minute episodes - 5 minutes
|
||||
if (movie.Runtime <= 30)
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
//60 minute episodes - 10 minutes
|
||||
return 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
{
|
||||
public enum DetectSampleResult
|
||||
{
|
||||
Indeterminate,
|
||||
Sample,
|
||||
NotSample
|
||||
}
|
||||
}
|
@ -85,8 +85,8 @@ public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownloa
|
||||
movieFile.Languages = localMovie.Languages;
|
||||
movieFile.MediaInfo = localMovie.MediaInfo;
|
||||
movieFile.Movie = localMovie.Movie;
|
||||
movieFile.ReleaseGroup = localMovie.ParsedMovieInfo?.ReleaseGroup;
|
||||
movieFile.Edition = localMovie.ParsedMovieInfo?.Edition;
|
||||
movieFile.ReleaseGroup = localMovie.ReleaseGroup;
|
||||
movieFile.Edition = localMovie.Edition;
|
||||
|
||||
bool copyOnly;
|
||||
switch (importMode)
|
||||
|
@ -15,6 +15,7 @@
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation;
|
||||
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
@ -22,18 +23,15 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
||||
public interface IMakeImportDecision
|
||||
{
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool shouldCheckQuality);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality);
|
||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource);
|
||||
}
|
||||
|
||||
public class ImportDecisionMaker : IMakeImportDecision
|
||||
{
|
||||
private readonly IEnumerable<IImportDecisionEngineSpecification> _specifications;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IAggregationService _aggregationService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IDetectSample _detectSample;
|
||||
private readonly IQualityDefinitionService _qualitiesService;
|
||||
private readonly IConfigService _config;
|
||||
@ -42,10 +40,9 @@ public class ImportDecisionMaker : IMakeImportDecision
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
|
||||
IParsingService parsingService,
|
||||
IMediaFileService mediaFileService,
|
||||
IAggregationService aggregationService,
|
||||
IDiskProvider diskProvider,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
IDetectSample detectSample,
|
||||
IQualityDefinitionService qualitiesService,
|
||||
IConfigService config,
|
||||
@ -54,10 +51,9 @@ public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> speci
|
||||
Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
_parsingService = parsingService;
|
||||
_mediaFileService = mediaFileService;
|
||||
_aggregationService = aggregationService;
|
||||
_diskProvider = diskProvider;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_detectSample = detectSample;
|
||||
_qualitiesService = qualitiesService;
|
||||
_config = config;
|
||||
@ -68,12 +64,7 @@ public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> speci
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie)
|
||||
{
|
||||
return GetImportDecisions(videoFiles, movie, null, null, true, false);
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, bool shouldCheckQuality = false)
|
||||
{
|
||||
return GetImportDecisions(videoFiles, movie, null, null, true, shouldCheckQuality);
|
||||
return GetImportDecisions(videoFiles, movie, null, null, true);
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource)
|
||||
@ -82,102 +73,79 @@ public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie mo
|
||||
|
||||
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());
|
||||
|
||||
var shouldUseFolderName = ShouldUseFolderName(videoFiles, movie, folderInfo);
|
||||
ParsedMovieInfo downloadClientItemInfo = null;
|
||||
|
||||
if (downloadClientItem != null)
|
||||
{
|
||||
downloadClientItemInfo = Parser.Parser.ParseMovieTitle(downloadClientItem.Title, false);
|
||||
}
|
||||
|
||||
var nonSampleVideoFileCount = GetNonSampleVideoFileCount(newFiles, movie, downloadClientItemInfo, folderInfo);
|
||||
|
||||
var decisions = new List<ImportDecision>();
|
||||
|
||||
foreach (var file in newFiles)
|
||||
{
|
||||
decisions.AddIfNotNull(GetDecision(file, movie, downloadClientItem, folderInfo, sceneSource, shouldUseFolderName));
|
||||
var localMovie = new LocalMovie
|
||||
{
|
||||
Movie = movie,
|
||||
DownloadClientMovieInfo = downloadClientItemInfo,
|
||||
FolderMovieInfo = folderInfo,
|
||||
Path = file,
|
||||
SceneSource = sceneSource
|
||||
};
|
||||
|
||||
decisions.AddIfNotNull(GetDecision(localMovie, downloadClientItem, nonSampleVideoFileCount > 1));
|
||||
}
|
||||
|
||||
return decisions;
|
||||
}
|
||||
|
||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldCheckQuality)
|
||||
{
|
||||
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), movie);
|
||||
|
||||
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());
|
||||
|
||||
var shouldUseFolderName = ShouldUseFolderName(videoFiles, movie, folderInfo);
|
||||
var decisions = new List<ImportDecision>();
|
||||
|
||||
foreach (var file in newFiles)
|
||||
{
|
||||
decisions.AddIfNotNull(GetDecision(file, movie, downloadClientItem, folderInfo, sceneSource, shouldUseFolderName, shouldCheckQuality));
|
||||
}
|
||||
|
||||
return decisions;
|
||||
}
|
||||
|
||||
private ImportDecision GetDecision(string file, Movie movie, DownloadClientItem downloadClientItem, ParsedMovieInfo folderInfo, bool sceneSource, bool shouldUseFolderName, bool shouldCheckQuality = false)
|
||||
private ImportDecision GetDecision(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
||||
{
|
||||
ImportDecision decision = null;
|
||||
|
||||
var fileMovieInfo = Parser.Parser.ParseMoviePath(localMovie.Path, false);
|
||||
|
||||
localMovie.FileMovieInfo = fileMovieInfo;
|
||||
localMovie.Size = _diskProvider.GetFileSize(localMovie.Path);
|
||||
|
||||
try
|
||||
{
|
||||
ParsedMovieInfo modifiedFolderInfo = null;
|
||||
if (folderInfo != null)
|
||||
_aggregationService.Augment(localMovie, otherFiles);
|
||||
|
||||
if (localMovie.Movie == null)
|
||||
{
|
||||
modifiedFolderInfo = folderInfo.JsonClone();
|
||||
// We want the filename to be used for parsing quality, etc. even if we didn't get any movie info from there.
|
||||
modifiedFolderInfo.SimpleReleaseTitle = Path.GetFileName(file);
|
||||
}
|
||||
|
||||
var minimalInfo = _parsingService.ParseMinimalPathMovieInfo(file) ?? modifiedFolderInfo;
|
||||
|
||||
LocalMovie localMovie = null;
|
||||
|
||||
if (minimalInfo != null)
|
||||
{
|
||||
//TODO: make it so media info doesn't ruin the import process of a new movie
|
||||
var mediaInfo = (_config.EnableMediaInfo || !movie.Path?.IsParentPath(file) == true) ? _videoFileInfoReader.GetMediaInfo(file) : null;
|
||||
var size = _diskProvider.GetFileSize(file);
|
||||
var historyItems = _historyService.FindByDownloadId(downloadClientItem?.DownloadId ?? "");
|
||||
var firstHistoryItem = historyItems?.OrderByDescending(h => h.Date)?.FirstOrDefault();
|
||||
var sizeMovie = new LocalMovie();
|
||||
sizeMovie.Size = size;
|
||||
localMovie = _parsingService.GetLocalMovie(file, minimalInfo, movie, new List<object>{mediaInfo, firstHistoryItem, sizeMovie, folderInfo}, sceneSource);
|
||||
localMovie.Quality = GetQuality(folderInfo, localMovie.Quality, movie);
|
||||
localMovie.Size = size;
|
||||
|
||||
_logger.Debug("Size: {0}", localMovie.Size);
|
||||
|
||||
decision = GetDecision(localMovie, downloadClientItem);
|
||||
decision = new ImportDecision(localMovie, new Rejection("Invalid movie"));
|
||||
}
|
||||
else
|
||||
{
|
||||
localMovie = new LocalMovie();
|
||||
localMovie.Path = file;
|
||||
|
||||
if (MediaFileExtensions.Extensions.Contains(Path.GetExtension(file)))
|
||||
{
|
||||
if (_warnedFiles.Find(file) == null)
|
||||
{
|
||||
_warnedFiles.Set(file, "warned");
|
||||
_logger.Warn("Unable to parse movie info from path {0}", file);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Trace("Already warned user that we are unable to parse movie info from path: {0}", file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
decision = new ImportDecision(localMovie, new Rejection("Unable to parse file"));
|
||||
decision = GetDecision(localMovie, downloadClientItem);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (AugmentingFailedException)
|
||||
{
|
||||
_logger.Error(e, "Couldn't import file. " + file);
|
||||
decision = new ImportDecision(localMovie, new Rejection("Unable to parse file"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Couldn't import file. {0}", localMovie.Path);
|
||||
|
||||
var localMovie = new LocalMovie { Path = file };
|
||||
decision = new ImportDecision(localMovie, new Rejection("Unexpected error processing file"));
|
||||
}
|
||||
|
||||
//LocalMovie nullMovie = null;
|
||||
|
||||
//decision = new ImportDecision(nullMovie, new Rejection("IMPLEMENTATION MISSING!!!"));
|
||||
if (decision == null)
|
||||
{
|
||||
_logger.Error("Unable to make a decision on {0}", localMovie.Path);
|
||||
}
|
||||
else if (decision.Rejections.Any())
|
||||
{
|
||||
_logger.Debug("File rejected for the following reasons: {0}", string.Join(", ", decision.Rejections));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("File accepted");
|
||||
}
|
||||
|
||||
return decision;
|
||||
}
|
||||
@ -217,66 +185,19 @@ private Rejection EvaluateSpec(IImportDecisionEngineSpecification spec, LocalMov
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO: Remove this method, since it is no longer needed.
|
||||
private bool ShouldUseFolderName(List<string> videoFiles, Movie movie, ParsedMovieInfo folderInfo)
|
||||
private int GetNonSampleVideoFileCount(List<string> videoFiles, Movie movie, ParsedMovieInfo downloadClientItemInfo, ParsedMovieInfo folderInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private QualityModel GetQuality(ParsedMovieInfo folderInfo, QualityModel fileQuality, Movie movie)
|
||||
{
|
||||
if (UseFolderQuality(folderInfo, fileQuality, movie))
|
||||
return videoFiles.Count(file =>
|
||||
{
|
||||
_logger.Debug("Using quality from folder: {0}", folderInfo.Quality);
|
||||
return folderInfo.Quality;
|
||||
}
|
||||
var sample = _detectSample.IsSample(movie, file, false);
|
||||
|
||||
return fileQuality;
|
||||
}
|
||||
if (sample == DetectSampleResult.Sample)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool UseFolderQuality(ParsedMovieInfo folderInfo, QualityModel fileQuality, Movie movie)
|
||||
{
|
||||
if (folderInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (folderInfo.Quality.Quality == Quality.Unknown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileQuality.QualitySource == QualitySource.Extension)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fileQuality.QualitySource == QualitySource.MediaInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new QualityModelComparer(movie.Profile).Compare(folderInfo.Quality, fileQuality) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private bool ShouldCheckQualityForParsedQuality(Quality quality)
|
||||
{
|
||||
List<Quality> shouldNotCheck = new List<Quality> { Quality.WORKPRINT, Quality.TELECINE, Quality.TELESYNC,
|
||||
Quality.DVDSCR, Quality.DVD, Quality.CAM, Quality.DVDR, Quality.Remux1080p, Quality.Remux2160p, Quality.REGIONAL
|
||||
};
|
||||
|
||||
if (shouldNotCheck.Contains(quality))
|
||||
{
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
{
|
||||
@ -32,8 +33,8 @@ public class ManualImportService : IExecute<ManualImportCommand>, IManualImportS
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||
private readonly IImportApprovedMovie _importApprovedMovie;
|
||||
private readonly IAggregationService _aggregationService;
|
||||
private readonly ITrackedDownloadService _trackedDownloadService;
|
||||
private readonly IDownloadedMovieImportService _downloadedMovieImportService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
@ -46,7 +47,7 @@ public ManualImportService(IDiskProvider diskProvider,
|
||||
IDiskScanService diskScanService,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IMovieService movieService,
|
||||
IVideoFileInfoReader videoFileInfoReader,
|
||||
IAggregationService aggregationService,
|
||||
IImportApprovedMovie importApprovedMovie,
|
||||
ITrackedDownloadService trackedDownloadService,
|
||||
IDownloadedMovieImportService downloadedMovieImportService,
|
||||
@ -60,7 +61,7 @@ public ManualImportService(IDiskProvider diskProvider,
|
||||
_diskScanService = diskScanService;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_movieService = movieService;
|
||||
_videoFileInfoReader = videoFileInfoReader;
|
||||
_aggregationService = aggregationService;
|
||||
_importApprovedMovie = importApprovedMovie;
|
||||
_trackedDownloadService = trackedDownloadService;
|
||||
_downloadedMovieImportService = downloadedMovieImportService;
|
||||
@ -91,16 +92,17 @@ public List<ManualImportItem> GetMediaFiles(string path, string downloadId, bool
|
||||
return new List<ManualImportItem>();
|
||||
}
|
||||
|
||||
return new List<ManualImportItem> { ProcessFile(path, downloadId) };
|
||||
var rootFolder = Path.GetDirectoryName(path);
|
||||
return new List<ManualImportItem> { ProcessFile(rootFolder, rootFolder, path, downloadId) };
|
||||
}
|
||||
|
||||
return ProcessFolder(path, downloadId, filterExistingFiles);
|
||||
return ProcessFolder(path, path, downloadId, filterExistingFiles);
|
||||
}
|
||||
|
||||
private List<ManualImportItem> ProcessFolder(string folder, string downloadId, bool filterExistingFiles)
|
||||
private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, bool filterExistingFiles)
|
||||
{
|
||||
DownloadClientItem downloadClientItem = null;
|
||||
var directoryInfo = new DirectoryInfo(folder);
|
||||
var directoryInfo = new DirectoryInfo(baseFolder);
|
||||
var movie = _parsingService.GetMovie(directoryInfo.Name);
|
||||
|
||||
if (downloadId.IsNotNullOrWhiteSpace())
|
||||
@ -114,33 +116,40 @@ private List<ManualImportItem> ProcessFolder(string folder, string downloadId, b
|
||||
}
|
||||
}
|
||||
|
||||
// Try a lookup by the path if the movie is still unknown, this will handle
|
||||
// the case where the movie folder doesn't match the movie title.
|
||||
if (movie == null)
|
||||
{
|
||||
var files = _diskScanService.FilterFiles(folder, _diskScanService.GetVideoFiles(folder));
|
||||
|
||||
return files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList();
|
||||
movie = _movieService.FindByPath(rootFolder);
|
||||
}
|
||||
|
||||
var historyItems = _historyService.FindByDownloadId(downloadId);
|
||||
var firstHistoryItem = historyItems.OrderByDescending(h => h.Date).FirstOrDefault();
|
||||
var folderInfo = _parsingService.ParseMovieInfo(directoryInfo.Name, new List<object>{firstHistoryItem});
|
||||
var movieFiles = _diskScanService.GetVideoFiles(folder).ToList();
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(movieFiles, movie, downloadClientItem, folderInfo, SceneSource(movie, folder), false);
|
||||
if (movie == null)
|
||||
{
|
||||
var files = _diskScanService.FilterFiles(baseFolder, _diskScanService.GetVideoFiles(baseFolder, false));
|
||||
var subfolders = _diskScanService.FilterFiles(baseFolder, _diskProvider.GetDirectories(baseFolder));
|
||||
|
||||
return decisions.Select(decision => MapItem(decision, folder, downloadId, directoryInfo.Name)).ToList();
|
||||
var processedFiles = files.Select(file => ProcessFile(rootFolder, baseFolder, file, downloadId));
|
||||
var processedFolders = subfolders.SelectMany(subfolder => ProcessFolder(rootFolder, subfolder, downloadId, filterExistingFiles));
|
||||
|
||||
return processedFiles.Concat(processedFolders).Where(i => i != null).ToList();
|
||||
}
|
||||
|
||||
var folderInfo = Parser.Parser.ParseMovieTitle(directoryInfo.Name, false);
|
||||
var movieFiles = _diskScanService.GetVideoFiles(baseFolder).ToList();
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(movieFiles, movie, downloadClientItem, folderInfo, SceneSource(movie, baseFolder));
|
||||
|
||||
return decisions.Select(decision => MapItem(decision, rootFolder, downloadId, directoryInfo.Name)).ToList();
|
||||
}
|
||||
|
||||
private ManualImportItem ProcessFile(string file, string downloadId, string folder = null)
|
||||
private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId, Movie movie = null)
|
||||
{
|
||||
if (folder.IsNullOrWhiteSpace())
|
||||
{
|
||||
folder = new FileInfo(file).Directory.FullName;
|
||||
}
|
||||
|
||||
DownloadClientItem downloadClientItem = null;
|
||||
var relativeFile = folder.GetRelativePath(file);
|
||||
var relativeFile = baseFolder.GetRelativePath(file);
|
||||
|
||||
var movie = _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]);
|
||||
if (movie == null)
|
||||
{
|
||||
_parsingService.GetMovie(relativeFile.Split('\\', '/')[0]);
|
||||
}
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
@ -160,34 +169,39 @@ private ManualImportItem ProcessFile(string file, string downloadId, string fold
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
var localMovie = new LocalMovie()
|
||||
{
|
||||
Path = file,
|
||||
Quality = QualityParser.ParseQuality(file),
|
||||
Size = _diskProvider.GetFileSize(file)
|
||||
};
|
||||
var relativeParseInfo = Parser.Parser.ParseMoviePath(relativeFile, false);
|
||||
|
||||
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), folder, downloadId, null);
|
||||
if (relativeParseInfo != null)
|
||||
{
|
||||
movie = _movieService.FindByTitle(relativeParseInfo.MovieTitle);
|
||||
}
|
||||
}
|
||||
|
||||
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
|
||||
movie, downloadClientItem, null, SceneSource(movie, folder), true);
|
||||
if (movie == null)
|
||||
{
|
||||
var localMovie = new LocalMovie();
|
||||
localMovie.Path = file;
|
||||
localMovie.Quality = QualityParser.ParseQuality(file);
|
||||
localMovie.Languages = LanguageParser.ParseLanguages(file);
|
||||
localMovie.Size = _diskProvider.GetFileSize(file);
|
||||
|
||||
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null);
|
||||
}
|
||||
|
||||
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> {file}, movie, downloadClientItem, null, SceneSource(movie, baseFolder));
|
||||
|
||||
if (importDecisions.Any())
|
||||
{
|
||||
return MapItem(importDecisions.First(), folder, downloadId, null);
|
||||
return MapItem(importDecisions.First(), rootFolder, downloadId, null);
|
||||
}
|
||||
|
||||
return new ManualImportItem
|
||||
{
|
||||
DownloadId = downloadId,
|
||||
Path = file,
|
||||
RelativePath = folder.GetRelativePath(file),
|
||||
RelativePath = rootFolder.GetRelativePath(file),
|
||||
Name = Path.GetFileNameWithoutExtension(file),
|
||||
Rejections = new List<Rejection>
|
||||
{
|
||||
new Rejection("Unable to process file")
|
||||
}
|
||||
Rejections = new List<Rejection>()
|
||||
};
|
||||
}
|
||||
|
||||
@ -232,15 +246,14 @@ public void Execute(ManualImportCommand message)
|
||||
|
||||
var file = message.Files[i];
|
||||
var movie = _movieService.GetMovie(file.MovieId);
|
||||
var parsedMovieInfo = _parsingService.ParseMoviePathInfo(file.Path, new List<object>()) ?? new ParsedMovieInfo();
|
||||
var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
|
||||
var fileMovieInfo = Parser.Parser.ParseMoviePath(file.Path, false) ?? new ParsedMovieInfo();
|
||||
var existingFile = movie.Path.IsParentPath(file.Path);
|
||||
TrackedDownload trackedDownload = null;
|
||||
|
||||
var localMovie = new LocalMovie
|
||||
{
|
||||
ExistingFile = false,
|
||||
MediaInfo = mediaInfo,
|
||||
ParsedMovieInfo = parsedMovieInfo,
|
||||
FileMovieInfo = fileMovieInfo,
|
||||
Path = file.Path,
|
||||
Quality = file.Quality,
|
||||
Languages = file.Languages,
|
||||
@ -248,26 +261,42 @@ public void Execute(ManualImportCommand message)
|
||||
Size = 0
|
||||
};
|
||||
|
||||
if (file.DownloadId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
trackedDownload = _trackedDownloadService.Find(file.DownloadId);
|
||||
localMovie.DownloadClientMovieInfo = trackedDownload?.RemoteMovie?.ParsedMovieInfo;
|
||||
}
|
||||
|
||||
if (file.FolderName.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
localMovie.FolderMovieInfo = Parser.Parser.ParseMovieTitle(file.FolderName, false);
|
||||
}
|
||||
|
||||
localMovie = _aggregationService.Augment(localMovie, false);
|
||||
|
||||
// Apply the user-chosen values.
|
||||
localMovie.Movie = movie;
|
||||
localMovie.Quality = file.Quality;
|
||||
|
||||
//TODO: Cleanup non-tracked downloads
|
||||
|
||||
var importDecision = new ImportDecision(localMovie);
|
||||
|
||||
if (file.DownloadId.IsNullOrWhiteSpace())
|
||||
if (trackedDownload == null)
|
||||
{
|
||||
imported.AddRange(_importApprovedMovie.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
var trackedDownload = _trackedDownloadService.Find(file.DownloadId);
|
||||
var importResult = _importApprovedMovie.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();
|
||||
|
||||
imported.Add(importResult);
|
||||
|
||||
importedTrackedDownload.Add(new ManuallyImportedFile
|
||||
{
|
||||
TrackedDownload = trackedDownload,
|
||||
ImportResult = importResult
|
||||
});
|
||||
{
|
||||
TrackedDownload = trackedDownload,
|
||||
ImportResult = importResult
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,15 +17,13 @@ public NotSampleSpecification(IDetectSample detectSample,
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(LocalMovie localEpisode, DownloadClientItem downloadClientItem)
|
||||
public Decision IsSatisfiedBy(LocalMovie localMovie, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
var sample = _detectSample.IsSample(localEpisode.Movie,
|
||||
localEpisode.Quality,
|
||||
localEpisode.Path,
|
||||
localEpisode.Size,
|
||||
var sample = _detectSample.IsSample(localMovie.Movie,
|
||||
localMovie.Path,
|
||||
false);
|
||||
|
||||
if (sample)
|
||||
if (sample == DetectSampleResult.Sample)
|
||||
{
|
||||
return Decision.Reject("Sample");
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ public interface IMovieRepository : IBasicRepository<Movie>
|
||||
List<Movie> GetMoviesByFileId(int fileId);
|
||||
void SetFileId(int fileId, int movieId);
|
||||
PagingSpec<Movie> MoviesWhereCutoffUnmet(PagingSpec<Movie> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff);
|
||||
Movie FindByPath(string path);
|
||||
}
|
||||
|
||||
public class MovieRepository : BasicRepository<Movie>, IMovieRepository
|
||||
@ -103,61 +104,6 @@ public PagingSpec<Movie> MoviesWithoutFiles(PagingSpec<Movie> pagingSpec)
|
||||
return pagingSpec;
|
||||
}
|
||||
|
||||
/*public override PagingSpec<Movie> GetPaged(PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
if (pagingSpec.SortKey == "downloadedQuality")
|
||||
{
|
||||
var mapper = _database.GetDataMapper();
|
||||
var offset = pagingSpec.PagingOffset();
|
||||
var limit = pagingSpec.PageSize;
|
||||
var direction = "ASC";
|
||||
if (pagingSpec.SortDirection == NzbDrone.Core.Datastore.SortDirection.Descending)
|
||||
{
|
||||
direction = "DESC";
|
||||
}
|
||||
var q = mapper.Query<Movie>($"SELECT * from \"Movies\" , \"MovieFiles\", \"QualityDefinitions\" WHERE Movies.MovieFileId=MovieFiles.Id AND instr(MovieFiles.Quality, ('quality\": ' || QualityDefinitions.Quality || \",\")) > 0 ORDER BY QualityDefinitions.Title {direction} LIMIT {offset},{limit};");
|
||||
var q2 = mapper.Query<Movie>("SELECT * from \"Movies\" , \"MovieFiles\", \"QualityDefinitions\" WHERE Movies.MovieFileId=MovieFiles.Id AND instr(MovieFiles.Quality, ('quality\": ' || QualityDefinitions.Quality || \",\")) > 0 ORDER BY QualityDefinitions.Title ASC;");
|
||||
|
||||
//var ok = q.BuildQuery();
|
||||
var q3 = Query.OrderBy("json_extract([t2].[quality], '$.quality') DESC");
|
||||
|
||||
pagingSpec.Records = q3.ToList();
|
||||
pagingSpec.TotalRecords = q3.GetRowCount();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
pagingSpec = base.GetPaged(pagingSpec);
|
||||
//pagingSpec.Records = GetPagedQuery(Query, pagingSpec).ToList();
|
||||
//pagingSpec.TotalRecords = GetPagedQuery(Query, pagingSpec).GetRowCount();
|
||||
}
|
||||
|
||||
if (pagingSpec.Records.Count == 0 && pagingSpec.Page != 1)
|
||||
{
|
||||
var lastPossiblePage = pagingSpec.TotalRecords / pagingSpec.PageSize + 1;
|
||||
pagingSpec.Page = lastPossiblePage;
|
||||
return GetPaged(pagingSpec);
|
||||
}
|
||||
|
||||
return pagingSpec;
|
||||
}*/
|
||||
|
||||
/*protected override SortBuilder<Movie> GetPagedQuery(QueryBuilder<Movie> query, PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
return DataMapper.Query<Movie>().Join<Movie, AlternativeTitle>(JoinType.Left, m => m.AlternativeTitles,
|
||||
(m, t) => m.Id == t.MovieId).Where(pagingSpec.FilterExpression)
|
||||
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||
.Skip(pagingSpec.PagingOffset())
|
||||
.Take(pagingSpec.PageSize);
|
||||
}*/
|
||||
|
||||
/*protected override SortBuilder<Movie> GetPagedQuery(QueryBuilder<Movie> query, PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
var newQuery = base.GetPagedQuery(query.Join<Movie, AlternativeTitle>(JoinType.Left, m => m.JoinAlternativeTitles, (movie, title) => title.MovieId == movie.Id), pagingSpec);
|
||||
System.Console.WriteLine(newQuery.ToString());
|
||||
return newQuery;
|
||||
}*/
|
||||
|
||||
public SortBuilder<Movie> GetMoviesWithoutFilesQuery(PagingSpec<Movie> pagingSpec)
|
||||
{
|
||||
return Query.Where(pagingSpec.FilterExpressions.FirstOrDefault())
|
||||
@ -242,15 +188,6 @@ private Movie FindByTitle(string cleanTitle, int? year)
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
/*IEnumerable<Movie> movies = All();
|
||||
Func<string, string> titleCleaner = title => CoreParser.CleanSeriesTitle(title.ToLower());
|
||||
Func<IEnumerable<AlternativeTitle>, string, bool> altTitleComparer =
|
||||
(alternativeTitles, atitle) =>
|
||||
alternativeTitles.Any(altTitle => altTitle.CleanTitle == atitle);*/
|
||||
|
||||
/*result = movies.Where(m => altTitleComparer(m.AlternativeTitles, cleanTitle) ||
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithRomanNumbers) ||
|
||||
altTitleComparer(m.AlternativeTitles, cleanTitleWithArabicNumbers)).FirstWithYear(year);*/
|
||||
|
||||
result = Query.Join<Movie, AlternativeTitle>(JoinType.Inner, m => m.AlternativeTitles, (m, t) => m.Id == t.MovieId)
|
||||
.Where(t => t.CleanTitle == cleanTitle || t.CleanTitle == cleanTitleWithArabicNumbers || t.CleanTitle == cleanTitleWithRomanNumbers)
|
||||
@ -260,12 +197,6 @@ private Movie FindByTitle(string cleanTitle, int? year)
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
/*return year.HasValue
|
||||
? results?.FirstOrDefault(movie => movie.Year == year.Value)
|
||||
|
||||
|
||||
: results?.FirstOrDefault();*/
|
||||
}
|
||||
|
||||
public Movie FindByTmdbId(int tmdbid)
|
||||
@ -273,6 +204,12 @@ public Movie FindByTmdbId(int tmdbid)
|
||||
return Query.Where(m => m.TmdbId == tmdbid).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Movie FindByPath(string path)
|
||||
{
|
||||
return Query.Where(s => s.Path == path)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
protected override QueryBuilder<TActual> AddJoinQueries<TActual>(QueryBuilder<TActual> baseQuery)
|
||||
{
|
||||
baseQuery = base.AddJoinQueries(baseQuery);
|
||||
|
@ -30,6 +30,7 @@ public interface IMovieService
|
||||
Movie FindByTitle(string title, int year);
|
||||
Movie FindByTitleInexact(string title, int? year);
|
||||
Movie FindByTitleSlug(string slug);
|
||||
Movie FindByPath(string path);
|
||||
bool MovieExists(Movie movie);
|
||||
Movie GetMovieByFileId(int fileId);
|
||||
List<Movie> GetMoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
||||
@ -287,6 +288,11 @@ public Movie FindByTitle(string title, int year)
|
||||
return _movieRepository.FindByTitle(title.CleanSeriesTitle(), year);
|
||||
}
|
||||
|
||||
public Movie FindByPath(string path)
|
||||
{
|
||||
return _movieRepository.FindByPath(path);
|
||||
}
|
||||
|
||||
public void DeleteMovie(int movieId, bool deleteFiles, bool addExclusion = false)
|
||||
{
|
||||
var movie = _movieRepository.Get(movieId);
|
||||
|
@ -205,6 +205,20 @@
|
||||
<Compile Include="MediaCover\EnsureMediaCoversCommand.cs" />
|
||||
<Compile Include="MediaFiles\Commands\UpdateMovieFileQualityCommand.cs" />
|
||||
<Compile Include="MediaFiles\Events\MovieFileRenamedEvent.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\AggregationFailedException.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\AggregationService.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\AggregateLanguage.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\AggregateQuality.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\AggregateReleaseGroup.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromDownloadClientItem.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromFileName.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromFolder.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromMediaInfo.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityResult.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\Confidence.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\Augmenters\Quality\IAugmentQuality.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Aggregation\Aggregators\IAggregateLocalMovie.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\DetectSampleResult.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\GrabbedReleaseQualitySpecification.cs" />
|
||||
<Compile Include="MediaFiles\MovieImport\Specifications\SameFileSpecification.cs" />
|
||||
<Compile Include="MediaFiles\Events\MovieFileUpdatedEvent.cs" />
|
||||
@ -1078,6 +1092,8 @@
|
||||
<Compile Include="Profiles\ProfileFormatItem.cs" />
|
||||
<Compile Include="Profiles\ProfileRepository.cs" />
|
||||
<Compile Include="ProgressMessaging\ProgressMessageContext.cs" />
|
||||
<Compile Include="Qualities\QualityDetectionSource.cs" />
|
||||
<Compile Include="Qualities\QualityFinder.cs" />
|
||||
<Compile Include="Qualities\QualitySource.cs" />
|
||||
<Compile Include="Qualities\Revision.cs" />
|
||||
<Compile Include="Queue\EstimatedCompletionTimeComparer.cs" />
|
||||
|
@ -6,42 +6,6 @@ namespace NzbDrone.Core.Organizer
|
||||
{
|
||||
public static class FileNameValidation
|
||||
{
|
||||
private static readonly Regex SeasonFolderRegex = new Regex(@"(\{season(\:\d+)?\})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
internal static readonly Regex OriginalTokenRegex = new Regex(@"(\{original[- ._](?:title|filename)\})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new ValidStandardEpisodeFormatValidator());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidDailyEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new ValidDailyEpisodeFormatValidator());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidAnimeEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new ValidAnimeEpisodeFormatValidator());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidSeriesFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.SeriesTitleRegex)).WithMessage("Must contain movie title");
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidSeasonFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(SeasonFolderRegex)).WithMessage("Must contain season number");
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidMovieFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
@ -54,78 +18,4 @@ public static IRuleBuilderOptions<T, string> ValidMovieFormat<T>(this IRuleBuild
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.MovieTitleRegex)).WithMessage("Must contain movie title");
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidStandardEpisodeFormatValidator : PropertyValidator
|
||||
{
|
||||
public ValidStandardEpisodeFormatValidator()
|
||||
: base("Must contain season and episode numbers OR Original Title")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var value = context.PropertyValue as string;
|
||||
|
||||
return true;
|
||||
|
||||
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
||||
!FileNameValidation.OriginalTokenRegex.IsMatch(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidDailyEpisodeFormatValidator : PropertyValidator
|
||||
{
|
||||
public ValidDailyEpisodeFormatValidator()
|
||||
: base("Must contain Air Date OR Season and Episode OR Original Title")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var value = context.PropertyValue as string;
|
||||
|
||||
return true;
|
||||
|
||||
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
||||
!FileNameBuilder.AirDateRegex.IsMatch(value) &&
|
||||
!FileNameValidation.OriginalTokenRegex.IsMatch(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidAnimeEpisodeFormatValidator : PropertyValidator
|
||||
{
|
||||
public ValidAnimeEpisodeFormatValidator()
|
||||
: base("Must contain Absolute Episode number OR Season and Episode OR Original Title")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var value = context.PropertyValue as string;
|
||||
|
||||
return true;
|
||||
|
||||
if (!FileNameBuilder.SeasonEpisodePatternRegex.IsMatch(value) &&
|
||||
!FileNameBuilder.AbsoluteEpisodePatternRegex.IsMatch(value) &&
|
||||
!FileNameValidation.OriginalTokenRegex.IsMatch(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper
|
||||
if (existing != quality.Resolution)
|
||||
{
|
||||
//_logger.Debug("Overwriting resolution info {0} with info from media info {1}", existing, quality.Resolution);
|
||||
quality.QualitySource = QualitySource.MediaInfo;
|
||||
quality.QualityDetectionSource = QualityDetectionSource.MediaInfo;
|
||||
movieInfo.Quality = quality;
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,18 @@ public LocalMovie()
|
||||
|
||||
public string Path { get; set; }
|
||||
public long Size { get; set; }
|
||||
public ParsedMovieInfo ParsedMovieInfo { get; set; }
|
||||
public ParsedMovieInfo FileMovieInfo { get; set; }
|
||||
public ParsedMovieInfo DownloadClientMovieInfo { get; set; }
|
||||
public ParsedMovieInfo FolderMovieInfo { get; set; }
|
||||
public Movie Movie { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public MediaInfoModel MediaInfo { get; set; }
|
||||
public bool ExistingFile { get; set; }
|
||||
|
||||
public bool SceneSource { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public string Edition { get; set; }
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@ -28,10 +28,7 @@ public class ParsedMovieInfo
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public IDictionary<string, object> ExtraInfo = new Dictionary<string, object>();
|
||||
//public int SeasonNumber { get; set; }
|
||||
public List<Language> Languages = new List<Language>();
|
||||
//public bool FullSeason { get; set; }
|
||||
//public bool Special { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public string ReleaseHash { get; set; }
|
||||
public string Edition { get; set;}
|
||||
|
@ -149,7 +149,7 @@ public static class Parser
|
||||
{"ü", "ue"},
|
||||
};
|
||||
|
||||
private static ParsedMovieInfo ParseMoviePath(string path, bool isLenient)
|
||||
public static ParsedMovieInfo ParseMoviePath(string path, bool isLenient)
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
|
@ -22,11 +22,9 @@ namespace NzbDrone.Core.Parser
|
||||
{
|
||||
public interface IParsingService
|
||||
{
|
||||
LocalMovie GetLocalMovie(string filename, ParsedMovieInfo minimalInfo, Movie movie, List<object> helpers, bool sceneSource = false);
|
||||
Movie GetMovie(string title);
|
||||
MappingResult Map(ParsedMovieInfo parsedMovieInfo, string imdbId, SearchCriteriaBase searchCriteria = null);
|
||||
ParsedMovieInfo ParseMovieInfo(string title, List<object> helpers);
|
||||
ParsedMovieInfo ParseMoviePathInfo(string path, List<object> helpers);
|
||||
ParsedMovieInfo ParseMinimalMovieInfo(string path, bool isDir = false);
|
||||
ParsedMovieInfo ParseMinimalPathMovieInfo(string path);
|
||||
List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo);
|
||||
@ -122,27 +120,6 @@ private ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo minimalInfo, List<objec
|
||||
return minimalInfo;
|
||||
}
|
||||
|
||||
public ParsedMovieInfo ParseMoviePathInfo(string path, List<object> helpers)
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
|
||||
var result = ParseMovieInfo(fileInfo.Name, helpers);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
_logger.Debug("Attempting to parse movie info using directory and file names. {0}", fileInfo.Directory.Name);
|
||||
result = ParseMovieInfo(fileInfo.Directory.Name + " " + fileInfo.Name, helpers);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
_logger.Debug("Attempting to parse movie info using directory name. {0}", fileInfo.Directory.Name);
|
||||
result = ParseMovieInfo(fileInfo.Directory.Name + fileInfo.Extension, helpers);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
var matches = MatchFormatTags(movieInfo);
|
||||
@ -178,22 +155,6 @@ public List<FormatTagMatchResult> MatchFormatTags(ParsedMovieInfo movieInfo)
|
||||
return matches;
|
||||
}
|
||||
|
||||
public LocalMovie GetLocalMovie(string filename, ParsedMovieInfo minimalInfo, Movie movie, List<object> helpers, bool sceneSource = false)
|
||||
{
|
||||
var enhanced = EnhanceMinimalInfo(minimalInfo, helpers);
|
||||
|
||||
return new LocalMovie
|
||||
{
|
||||
Movie = movie,
|
||||
Quality = enhanced.Quality,
|
||||
Languages = enhanced.Languages,
|
||||
Path = filename,
|
||||
ParsedMovieInfo = enhanced,
|
||||
ExistingFile = movie.Path.IsParentPath(filename),
|
||||
MediaInfo = helpers.FirstOrDefault(h => h?.GetType() == typeof(MediaInfoModel)) as MediaInfoModel
|
||||
};
|
||||
}
|
||||
|
||||
public ParsedMovieInfo ParseMinimalMovieInfo(string file, bool isDir = false)
|
||||
{
|
||||
return Parser.ParseMovieTitle(file, _config.ParsingLeniency > 0, isDir);
|
||||
|
@ -349,7 +349,7 @@ public static QualityModel ParseQuality(string name)
|
||||
result.Source = MediaFileExtensions.GetSourceForExtension(Path.GetExtension(name));
|
||||
result.Resolution = MediaFileExtensions.GetResolutionForExtension(Path.GetExtension(name));
|
||||
|
||||
result.QualitySource = QualitySource.Extension;
|
||||
result.QualityDetectionSource = QualityDetectionSource.Extension;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
|
9
src/NzbDrone.Core/Qualities/QualityDetectionSource.cs
Normal file
9
src/NzbDrone.Core/Qualities/QualityDetectionSource.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.Qualities
|
||||
{
|
||||
public enum QualityDetectionSource
|
||||
{
|
||||
Name,
|
||||
Extension,
|
||||
MediaInfo
|
||||
}
|
||||
}
|
41
src/NzbDrone.Core/Qualities/QualityFinder.cs
Normal file
41
src/NzbDrone.Core/Qualities/QualityFinder.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
|
||||
namespace NzbDrone.Core.Qualities
|
||||
{
|
||||
public static class QualityFinder
|
||||
{
|
||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(QualityFinder));
|
||||
|
||||
public static Quality FindBySourceAndResolution(Source source, Resolution resolution)
|
||||
{
|
||||
var matchingQuality = Quality.All.SingleOrDefault(q => q.Source == source && q.Resolution == resolution);
|
||||
|
||||
if (matchingQuality != null)
|
||||
{
|
||||
return matchingQuality;
|
||||
}
|
||||
|
||||
var matchingResolution = Quality.All.Where(q => q.Resolution == resolution)
|
||||
.OrderBy(q => q.Source)
|
||||
.ToList();
|
||||
|
||||
var nearestQuality = Quality.Unknown;
|
||||
|
||||
foreach (var quality in matchingResolution)
|
||||
{
|
||||
if (quality.Source >= source)
|
||||
{
|
||||
nearestQuality = quality;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Warn("Unable to find exact quality for {0} and {1}. Using {2} as fallback", source, resolution, nearestQuality);
|
||||
|
||||
return nearestQuality;
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ public class QualityModel : IEmbeddedDocument, IEquatable<QualityModel>
|
||||
public string HardcodedSubs { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public QualitySource QualitySource { get; set; }
|
||||
public QualityDetectionSource QualityDetectionSource { get; set; }
|
||||
|
||||
public QualityModel()
|
||||
: this(Quality.Unknown, new Revision())
|
||||
|
@ -2,8 +2,12 @@
|
||||
{
|
||||
public enum QualitySource
|
||||
{
|
||||
Name,
|
||||
Extension,
|
||||
MediaInfo
|
||||
Unknown,
|
||||
Television,
|
||||
TelevisionRaw,
|
||||
Web,
|
||||
WebRip,
|
||||
DVD,
|
||||
Bluray
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ namespace NzbDrone.Core.Update.Commands
|
||||
public class ApplicationUpdateCommand : Command
|
||||
{
|
||||
public override bool SendUpdatesToClient => true;
|
||||
public override bool RequiresDiskAccess => true;
|
||||
public override bool IsExclusive => true;
|
||||
|
||||
public override string CompletionMessage => "Restarting Radarr to apply updates";
|
||||
public override string CompletionMessage => null;
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ protected override sealed void RegisterTypes(TinyIoCContainer container, IEnumer
|
||||
break;
|
||||
case Lifetime.PerRequest:
|
||||
throw new InvalidOperationException("Unable to directly register a per request lifetime.");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
@ -118,7 +117,6 @@ protected override sealed void RegisterCollectionTypes(TinyIoCContainer containe
|
||||
break;
|
||||
case Lifetime.PerRequest:
|
||||
throw new InvalidOperationException("Unable to directly register a per request lifetime.");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user