mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-19 21:46:43 -04:00
New: Calculate custom score using renamed filename before importing
This commit is contained in:
+100
@@ -0,0 +1,100 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.CustomFormats;
|
||||||
|
using NzbDrone.Core.Languages;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||||
|
using NzbDrone.Core.Organizer;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.Profiles;
|
||||||
|
using NzbDrone.Core.Profiles.Qualities;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class LocalEpisodeCustomFormatCalculationServiceFixture : CoreTest<LocalEpisodeCustomFormatCalculationService>
|
||||||
|
{
|
||||||
|
private const int EnglishCustomFormatScore = 10;
|
||||||
|
private const int SpanishCustomFormatScore = 2;
|
||||||
|
private LocalEpisode _localEpisode;
|
||||||
|
private Series _series;
|
||||||
|
private QualityModel _quality;
|
||||||
|
private CustomFormat _englishCustomFormat;
|
||||||
|
private CustomFormat _spanishCustomFormat;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_englishCustomFormat = new CustomFormat("HasEnglish") { Id = 1 };
|
||||||
|
_spanishCustomFormat = new CustomFormat("HasSpanish") { Id = 2 };
|
||||||
|
_series = Builder<Series>.CreateNew()
|
||||||
|
.With(e => e.Path = @"C:\Test\Series".AsOsAgnostic())
|
||||||
|
.With(e => e.QualityProfile = new QualityProfile
|
||||||
|
{
|
||||||
|
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||||
|
FormatItems = [
|
||||||
|
new ProfileFormatItem { Format = _englishCustomFormat, Score = EnglishCustomFormatScore },
|
||||||
|
new ProfileFormatItem { Format = _spanishCustomFormat, Score = SpanishCustomFormatScore }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_quality = new QualityModel(Quality.DVD);
|
||||||
|
|
||||||
|
_localEpisode = new LocalEpisode
|
||||||
|
{
|
||||||
|
Series = _series,
|
||||||
|
Quality = _quality,
|
||||||
|
Languages = new List<Language> { Language.Spanish },
|
||||||
|
Episodes = new List<Episode> { new Episode() },
|
||||||
|
Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.Spanish.XviD-OSiTV.avi"
|
||||||
|
};
|
||||||
|
|
||||||
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
|
.Setup(s => s.ParseCustomFormat(It.IsAny<LocalEpisode>(), It.Is<string>(x => x.Contains("English"))))
|
||||||
|
.Returns([_englishCustomFormat]);
|
||||||
|
|
||||||
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
|
.Setup(s => s.ParseCustomFormat(It.IsAny<LocalEpisode>(), It.Is<string>(x => x.Contains("Spanish"))))
|
||||||
|
.Returns([_spanishCustomFormat]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_build_a_filename_and_use_it_to_calculate_custom_score()
|
||||||
|
{
|
||||||
|
var renamedFileName = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.English.XviD-OSiTV.avi";
|
||||||
|
|
||||||
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
|
.Setup(s => s.BuildFileName(It.IsAny<List<Episode>>(), It.IsAny<Series>(), It.IsAny<EpisodeFile>(), "", null, null))
|
||||||
|
.Returns(renamedFileName);
|
||||||
|
|
||||||
|
Subject.ParseEpisodeCustomFormats(_localEpisode).Should().BeEquivalentTo([_englishCustomFormat]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_update_custom_formats_on_local_episode()
|
||||||
|
{
|
||||||
|
var renamedFileName = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.English.XviD-OSiTV.avi";
|
||||||
|
|
||||||
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
|
.Setup(s => s.BuildFileName(It.IsAny<List<Episode>>(), It.IsAny<Series>(), It.IsAny<EpisodeFile>(), "", null, null))
|
||||||
|
.Returns(renamedFileName);
|
||||||
|
|
||||||
|
Subject.UpdateEpisodeCustomFormats(_localEpisode);
|
||||||
|
_localEpisode.FileNameUsedForCustomFormatCalculation.Should().Be(renamedFileName);
|
||||||
|
|
||||||
|
_localEpisode.OriginalFileNameCustomFormats.Should().BeEquivalentTo([_spanishCustomFormat]);
|
||||||
|
_localEpisode.OriginalFileNameCustomFormatScore.Should().Be(SpanishCustomFormatScore);
|
||||||
|
|
||||||
|
_localEpisode.CustomFormats.Should().BeEquivalentTo([_englishCustomFormat]);
|
||||||
|
_localEpisode.CustomFormatScore.Should().Be(EnglishCustomFormatScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -9,6 +9,7 @@ using NzbDrone.Core.CustomFormats;
|
|||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Languages;
|
using NzbDrone.Core.Languages;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Profiles;
|
using NzbDrone.Core.Profiles;
|
||||||
@@ -565,5 +566,48 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
|||||||
|
|
||||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_false_and_a_specific_reason_if_not_upgrade_to_custom_format_score_after_local_file_rename_but_was_before()
|
||||||
|
{
|
||||||
|
var episodeFileCustomFormats = Builder<CustomFormat>.CreateListOfSize(1).Build().ToList();
|
||||||
|
|
||||||
|
var episodeFile = new EpisodeFile
|
||||||
|
{
|
||||||
|
Quality = new QualityModel(Quality.Bluray1080p)
|
||||||
|
};
|
||||||
|
|
||||||
|
_series.QualityProfile.Value.FormatItems = episodeFileCustomFormats.Select(c => new ProfileFormatItem
|
||||||
|
{
|
||||||
|
Format = c,
|
||||||
|
Score = 50
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Mocker.GetMock<IConfigService>()
|
||||||
|
.Setup(s => s.DownloadPropersAndRepacks)
|
||||||
|
.Returns(ProperDownloadTypes.DoNotPrefer);
|
||||||
|
|
||||||
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
|
.Setup(s => s.ParseCustomFormat(episodeFile))
|
||||||
|
.Returns(episodeFileCustomFormats);
|
||||||
|
|
||||||
|
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
||||||
|
_localEpisode.CustomFormats = Builder<CustomFormat>.CreateListOfSize(1).Build().ToList();
|
||||||
|
_localEpisode.CustomFormatScore = 20;
|
||||||
|
_localEpisode.OriginalFileNameCustomFormats = Builder<CustomFormat>.CreateListOfSize(1).Build().ToList();
|
||||||
|
_localEpisode.OriginalFileNameCustomFormatScore = 60;
|
||||||
|
|
||||||
|
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
|
||||||
|
.All()
|
||||||
|
.With(e => e.EpisodeFileId = 1)
|
||||||
|
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(episodeFile))
|
||||||
|
.Build()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var result = Subject.IsSatisfiedBy(_localEpisode, null);
|
||||||
|
result.Accepted.Should().BeFalse();
|
||||||
|
result.Reason.Should().Be(ImportRejectionReason.NotCustomFormatUpgradeAfterRename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||||||
List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile);
|
List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile);
|
||||||
List<CustomFormat> ParseCustomFormat(Blocklist blocklist, Series series);
|
List<CustomFormat> ParseCustomFormat(Blocklist blocklist, Series series);
|
||||||
List<CustomFormat> ParseCustomFormat(EpisodeHistory history, Series series);
|
List<CustomFormat> ParseCustomFormat(EpisodeHistory history, Series series);
|
||||||
List<CustomFormat> ParseCustomFormat(LocalEpisode localEpisode);
|
List<CustomFormat> ParseCustomFormat(LocalEpisode localEpisode, string fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CustomFormatCalculationService : ICustomFormatCalculationService
|
public class CustomFormatCalculationService : ICustomFormatCalculationService
|
||||||
@@ -114,12 +114,12 @@ namespace NzbDrone.Core.CustomFormats
|
|||||||
return ParseCustomFormat(input);
|
return ParseCustomFormat(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CustomFormat> ParseCustomFormat(LocalEpisode localEpisode)
|
public List<CustomFormat> ParseCustomFormat(LocalEpisode localEpisode, string fileName)
|
||||||
{
|
{
|
||||||
var episodeInfo = new ParsedEpisodeInfo
|
var episodeInfo = new ParsedEpisodeInfo
|
||||||
{
|
{
|
||||||
SeriesTitle = localEpisode.Series.Title,
|
SeriesTitle = localEpisode.Series.Title,
|
||||||
ReleaseTitle = localEpisode.SceneName.IsNotNullOrWhiteSpace() ? localEpisode.SceneName : Path.GetFileName(localEpisode.Path),
|
ReleaseTitle = localEpisode.SceneName.IsNotNullOrWhiteSpace() ? localEpisode.SceneName : fileName,
|
||||||
Quality = localEpisode.Quality,
|
Quality = localEpisode.Quality,
|
||||||
Languages = localEpisode.Languages,
|
Languages = localEpisode.Languages,
|
||||||
ReleaseGroup = localEpisode.ReleaseGroup
|
ReleaseGroup = localEpisode.ReleaseGroup
|
||||||
@@ -133,7 +133,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||||||
Languages = localEpisode.Languages,
|
Languages = localEpisode.Languages,
|
||||||
IndexerFlags = localEpisode.IndexerFlags,
|
IndexerFlags = localEpisode.IndexerFlags,
|
||||||
ReleaseType = localEpisode.ReleaseType,
|
ReleaseType = localEpisode.ReleaseType,
|
||||||
Filename = Path.GetFileName(localEpisode.Path)
|
Filename = fileName
|
||||||
};
|
};
|
||||||
|
|
||||||
return ParseCustomFormat(input);
|
return ParseCustomFormat(input);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
@@ -85,24 +84,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var episodeFile = new EpisodeFile();
|
var episodeFile = localEpisode.ToEpisodeFile();
|
||||||
episodeFile.DateAdded = DateTime.UtcNow;
|
|
||||||
episodeFile.SeriesId = localEpisode.Series.Id;
|
|
||||||
episodeFile.Path = localEpisode.Path.CleanFilePath();
|
|
||||||
episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
|
episodeFile.Size = _diskProvider.GetFileSize(localEpisode.Path);
|
||||||
episodeFile.Quality = localEpisode.Quality;
|
|
||||||
episodeFile.MediaInfo = localEpisode.MediaInfo;
|
|
||||||
episodeFile.Series = localEpisode.Series;
|
|
||||||
episodeFile.SeasonNumber = localEpisode.SeasonNumber;
|
|
||||||
episodeFile.Episodes = localEpisode.Episodes;
|
|
||||||
episodeFile.ReleaseGroup = localEpisode.ReleaseGroup;
|
|
||||||
episodeFile.ReleaseHash = localEpisode.ReleaseHash;
|
|
||||||
episodeFile.Languages = localEpisode.Languages;
|
|
||||||
|
|
||||||
// Prefer the release type from the download client, folder and finally the file so we have the most accurate information.
|
|
||||||
episodeFile.ReleaseType = localEpisode.DownloadClientEpisodeInfo?.ReleaseType ??
|
|
||||||
localEpisode.FolderEpisodeInfo?.ReleaseType ??
|
|
||||||
localEpisode.FileEpisodeInfo.ReleaseType;
|
|
||||||
|
|
||||||
if (downloadClientItem?.DownloadId.IsNotNullOrWhiteSpace() == true)
|
if (downloadClientItem?.DownloadId.IsNotNullOrWhiteSpace() == true)
|
||||||
{
|
{
|
||||||
@@ -118,23 +101,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
|||||||
// Prefer the release type from the grabbed history
|
// Prefer the release type from the grabbed history
|
||||||
if (Enum.TryParse(grabHistory?.Data.GetValueOrDefault("releaseType"), true, out ReleaseType releaseType))
|
if (Enum.TryParse(grabHistory?.Data.GetValueOrDefault("releaseType"), true, out ReleaseType releaseType))
|
||||||
{
|
{
|
||||||
episodeFile.ReleaseType = releaseType;
|
if (releaseType != ReleaseType.Unknown)
|
||||||
|
{
|
||||||
|
episodeFile.ReleaseType = releaseType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
episodeFile.IndexerFlags = localEpisode.IndexerFlags;
|
|
||||||
episodeFile.ReleaseType = localEpisode.ReleaseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to parsed information if history is unavailable or missing
|
|
||||||
if (episodeFile.ReleaseType == ReleaseType.Unknown)
|
|
||||||
{
|
|
||||||
// Prefer the release type from the download client, folder and finally the file so we have the most accurate information.
|
|
||||||
episodeFile.ReleaseType = localEpisode.DownloadClientEpisodeInfo?.ReleaseType ??
|
|
||||||
localEpisode.FolderEpisodeInfo?.ReleaseType ??
|
|
||||||
localEpisode.FileEpisodeInfo.ReleaseType;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool copyOnly;
|
bool copyOnly;
|
||||||
switch (importMode)
|
switch (importMode)
|
||||||
@@ -153,15 +125,20 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
|||||||
|
|
||||||
if (newDownload)
|
if (newDownload)
|
||||||
{
|
{
|
||||||
episodeFile.SceneName = localEpisode.SceneName;
|
if (downloadClientItem is { OutputPath.IsEmpty: false })
|
||||||
episodeFile.OriginalFilePath = GetOriginalFilePath(downloadClientItem, localEpisode);
|
{
|
||||||
|
var outputDirectory = downloadClientItem.OutputPath.Directory.ToString();
|
||||||
|
|
||||||
|
if (outputDirectory.IsParentPath(localEpisode.Path))
|
||||||
|
{
|
||||||
|
episodeFile.OriginalFilePath = outputDirectory.GetRelativePath(localEpisode.Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
oldFiles = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly).OldFiles;
|
oldFiles = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode, copyOnly).OldFiles;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
episodeFile.RelativePath = localEpisode.Series.Path.GetRelativePath(episodeFile.Path);
|
|
||||||
|
|
||||||
// Delete existing files from the DB mapped to this path
|
// Delete existing files from the DB mapped to this path
|
||||||
var previousFiles = _mediaFileService.GetFilesWithRelativePath(localEpisode.Series.Id, episodeFile.RelativePath);
|
var previousFiles = _mediaFileService.GetFilesWithRelativePath(localEpisode.Series.Id, episodeFile.RelativePath);
|
||||||
|
|
||||||
@@ -228,42 +205,5 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
|||||||
|
|
||||||
return importResults;
|
return importResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetOriginalFilePath(DownloadClientItem downloadClientItem, LocalEpisode localEpisode)
|
|
||||||
{
|
|
||||||
var path = localEpisode.Path;
|
|
||||||
|
|
||||||
if (downloadClientItem != null && !downloadClientItem.OutputPath.IsEmpty)
|
|
||||||
{
|
|
||||||
var outputDirectory = downloadClientItem.OutputPath.Directory.ToString();
|
|
||||||
|
|
||||||
if (outputDirectory.IsParentPath(path))
|
|
||||||
{
|
|
||||||
return outputDirectory.GetRelativePath(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var folderEpisodeInfo = localEpisode.FolderEpisodeInfo;
|
|
||||||
|
|
||||||
if (folderEpisodeInfo != null)
|
|
||||||
{
|
|
||||||
var folderPath = path.GetAncestorPath(folderEpisodeInfo.ReleaseTitle);
|
|
||||||
|
|
||||||
if (folderPath != null)
|
|
||||||
{
|
|
||||||
return folderPath.GetParentPath().GetRelativePath(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var parentPath = path.GetParentPath();
|
|
||||||
var grandparentPath = parentPath.GetParentPath();
|
|
||||||
|
|
||||||
if (grandparentPath != null)
|
|
||||||
{
|
|
||||||
return grandparentPath.GetRelativePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Path.GetFileName(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.CustomFormats;
|
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
using NzbDrone.Core.Download.TrackedDownloads;
|
using NzbDrone.Core.Download.TrackedDownloads;
|
||||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation;
|
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation;
|
||||||
@@ -30,7 +29,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
|||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IDetectSample _detectSample;
|
private readonly IDetectSample _detectSample;
|
||||||
private readonly ITrackedDownloadService _trackedDownloadService;
|
private readonly ITrackedDownloadService _trackedDownloadService;
|
||||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
private readonly ILocalEpisodeCustomFormatCalculationService _formatCalculator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
|
public ImportDecisionMaker(IEnumerable<IImportDecisionEngineSpecification> specifications,
|
||||||
@@ -39,7 +38,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
|||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IDetectSample detectSample,
|
IDetectSample detectSample,
|
||||||
ITrackedDownloadService trackedDownloadService,
|
ITrackedDownloadService trackedDownloadService,
|
||||||
ICustomFormatCalculationService formatCalculator,
|
ILocalEpisodeCustomFormatCalculationService formatCalculator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_specifications = specifications;
|
_specifications = specifications;
|
||||||
@@ -158,8 +157,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
localEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(localEpisode);
|
_formatCalculator.UpdateEpisodeCustomFormats(localEpisode);
|
||||||
localEpisode.CustomFormatScore = localEpisode.Series.QualityProfile?.Value.CalculateCustomFormatScore(localEpisode.CustomFormats) ?? 0;
|
|
||||||
|
|
||||||
decision = GetDecision(localEpisode, downloadClientItem);
|
decision = GetDecision(localEpisode, downloadClientItem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,5 +36,6 @@ public enum ImportRejectionReason
|
|||||||
UnverifiedSceneMapping,
|
UnverifiedSceneMapping,
|
||||||
NotQualityUpgrade,
|
NotQualityUpgrade,
|
||||||
NotRevisionUpgrade,
|
NotRevisionUpgrade,
|
||||||
NotCustomFormatUpgrade
|
NotCustomFormatUpgrade,
|
||||||
|
NotCustomFormatUpgradeAfterRename
|
||||||
}
|
}
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using NzbDrone.Core.CustomFormats;
|
||||||
|
using NzbDrone.Core.Organizer;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||||
|
|
||||||
|
public interface ILocalEpisodeCustomFormatCalculationService
|
||||||
|
{
|
||||||
|
public List<CustomFormat> ParseEpisodeCustomFormats(LocalEpisode localEpisode);
|
||||||
|
public void UpdateEpisodeCustomFormats(LocalEpisode localEpisode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocalEpisodeCustomFormatCalculationService : ILocalEpisodeCustomFormatCalculationService
|
||||||
|
{
|
||||||
|
private readonly IBuildFileNames _fileNameBuilder;
|
||||||
|
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||||
|
|
||||||
|
public LocalEpisodeCustomFormatCalculationService(IBuildFileNames fileNameBuilder, ICustomFormatCalculationService formatCalculator)
|
||||||
|
{
|
||||||
|
_fileNameBuilder = fileNameBuilder;
|
||||||
|
_formatCalculator = formatCalculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CustomFormat> ParseEpisodeCustomFormats(LocalEpisode localEpisode)
|
||||||
|
{
|
||||||
|
var fileNameUsedForCustomFormatCalculation = _fileNameBuilder.BuildFileName(localEpisode.Episodes, localEpisode.Series, localEpisode.ToEpisodeFile());
|
||||||
|
return _formatCalculator.ParseCustomFormat(localEpisode, fileNameUsedForCustomFormatCalculation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateEpisodeCustomFormats(LocalEpisode localEpisode)
|
||||||
|
{
|
||||||
|
var fileNameUsedForCustomFormatCalculation = _fileNameBuilder.BuildFileName(localEpisode.Episodes, localEpisode.Series, localEpisode.ToEpisodeFile());
|
||||||
|
localEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(localEpisode, fileNameUsedForCustomFormatCalculation);
|
||||||
|
localEpisode.FileNameUsedForCustomFormatCalculation = fileNameUsedForCustomFormatCalculation;
|
||||||
|
localEpisode.CustomFormatScore = localEpisode.Series.QualityProfile?.Value.CalculateCustomFormatScore(localEpisode.CustomFormats) ?? 0;
|
||||||
|
|
||||||
|
localEpisode.OriginalFileNameCustomFormats = _formatCalculator.ParseCustomFormat(localEpisode, Path.GetFileName(localEpisode.Path));
|
||||||
|
localEpisode.OriginalFileNameCustomFormatScore = localEpisode.Series.QualityProfile?.Value.CalculateCustomFormatScore(localEpisode.OriginalFileNameCustomFormats) ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
|||||||
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
|
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
|
||||||
private readonly IMediaFileService _mediaFileService;
|
private readonly IMediaFileService _mediaFileService;
|
||||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||||
|
private readonly ILocalEpisodeCustomFormatCalculationService _localEpisodeFormatCalculator;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
|||||||
ITrackedDownloadService trackedDownloadService,
|
ITrackedDownloadService trackedDownloadService,
|
||||||
IDownloadedEpisodesImportService downloadedEpisodesImportService,
|
IDownloadedEpisodesImportService downloadedEpisodesImportService,
|
||||||
IMediaFileService mediaFileService,
|
IMediaFileService mediaFileService,
|
||||||
|
ILocalEpisodeCustomFormatCalculationService localEpisodeFormatCalculator,
|
||||||
ICustomFormatCalculationService formatCalculator,
|
ICustomFormatCalculationService formatCalculator,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
@@ -70,6 +72,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
|||||||
_trackedDownloadService = trackedDownloadService;
|
_trackedDownloadService = trackedDownloadService;
|
||||||
_downloadedEpisodesImportService = downloadedEpisodesImportService;
|
_downloadedEpisodesImportService = downloadedEpisodesImportService;
|
||||||
_mediaFileService = mediaFileService;
|
_mediaFileService = mediaFileService;
|
||||||
|
_localEpisodeFormatCalculator = localEpisodeFormatCalculator;
|
||||||
_formatCalculator = formatCalculator;
|
_formatCalculator = formatCalculator;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -180,8 +183,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
|||||||
localEpisode.IndexerFlags = (IndexerFlags)indexerFlags;
|
localEpisode.IndexerFlags = (IndexerFlags)indexerFlags;
|
||||||
localEpisode.ReleaseType = releaseType;
|
localEpisode.ReleaseType = releaseType;
|
||||||
|
|
||||||
localEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(localEpisode);
|
_localEpisodeFormatCalculator.UpdateEpisodeCustomFormats(localEpisode);
|
||||||
localEpisode.CustomFormatScore = localEpisode.Series?.QualityProfile?.Value.CalculateCustomFormatScore(localEpisode.CustomFormats) ?? 0;
|
|
||||||
|
|
||||||
// Augment episode file so imported files have all additional information an automatic import would
|
// Augment episode file so imported files have all additional information an automatic import would
|
||||||
localEpisode = _aggregationService.Augment(localEpisode, downloadClientItem);
|
localEpisode = _aggregationService.Augment(localEpisode, downloadClientItem);
|
||||||
@@ -445,8 +447,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
|||||||
if (decision.LocalEpisode.Series != null)
|
if (decision.LocalEpisode.Series != null)
|
||||||
{
|
{
|
||||||
item.Series = decision.LocalEpisode.Series;
|
item.Series = decision.LocalEpisode.Series;
|
||||||
|
item.CustomFormats = _localEpisodeFormatCalculator.ParseEpisodeCustomFormats(decision.LocalEpisode);
|
||||||
item.CustomFormats = _formatCalculator.ParseCustomFormat(decision.LocalEpisode);
|
|
||||||
item.CustomFormatScore = item.Series.QualityProfile?.Value.CalculateCustomFormatScore(item.CustomFormats) ?? 0;
|
item.CustomFormatScore = item.Series.QualityProfile?.Value.CalculateCustomFormatScore(item.CustomFormats) ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,8 +538,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
|||||||
localEpisode.IndexerFlags = (IndexerFlags)file.IndexerFlags;
|
localEpisode.IndexerFlags = (IndexerFlags)file.IndexerFlags;
|
||||||
localEpisode.ReleaseType = file.ReleaseType;
|
localEpisode.ReleaseType = file.ReleaseType;
|
||||||
|
|
||||||
localEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(localEpisode);
|
_localEpisodeFormatCalculator.UpdateEpisodeCustomFormats(localEpisode);
|
||||||
localEpisode.CustomFormatScore = localEpisode.Series.QualityProfile?.Value.CalculateCustomFormatScore(localEpisode.CustomFormats) ?? 0;
|
|
||||||
|
|
||||||
// TODO: Cleanup non-tracked downloads
|
// TODO: Cleanup non-tracked downloads
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
|||||||
var currentFormatScore = qualityProfile.CalculateCustomFormatScore(currentFormats);
|
var currentFormatScore = qualityProfile.CalculateCustomFormatScore(currentFormats);
|
||||||
var newFormats = localEpisode.CustomFormats;
|
var newFormats = localEpisode.CustomFormats;
|
||||||
var newFormatScore = localEpisode.CustomFormatScore;
|
var newFormatScore = localEpisode.CustomFormatScore;
|
||||||
|
var newFormatsBeforeRename = localEpisode.OriginalFileNameCustomFormats;
|
||||||
|
var newFormatScoreBeforeRename = localEpisode.OriginalFileNameCustomFormatScore;
|
||||||
|
|
||||||
if (qualityCompare == 0 && newFormatScore < currentFormatScore)
|
if (qualityCompare == 0 && newFormatScore < currentFormatScore)
|
||||||
{
|
{
|
||||||
@@ -71,6 +73,18 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
|||||||
currentFormats != null ? currentFormats.ConcatToString() : "",
|
currentFormats != null ? currentFormats.ConcatToString() : "",
|
||||||
currentFormatScore);
|
currentFormatScore);
|
||||||
|
|
||||||
|
if (newFormatScoreBeforeRename > currentFormatScore)
|
||||||
|
{
|
||||||
|
return ImportSpecDecision.Reject(ImportRejectionReason.NotCustomFormatUpgradeAfterRename,
|
||||||
|
"Not a Custom Format upgrade for existing episode file(s). AfterRename: [{0}] ({1}) do not improve on Existing: [{2}] ({3}) even though BeforeRename: [{4}] ({5}) did.",
|
||||||
|
newFormats != null ? newFormats.ConcatToString() : "",
|
||||||
|
newFormatScore,
|
||||||
|
currentFormats != null ? currentFormats.ConcatToString() : "",
|
||||||
|
currentFormatScore,
|
||||||
|
newFormatsBeforeRename != null ? newFormatsBeforeRename.ConcatToString() : "",
|
||||||
|
newFormatScoreBeforeRename);
|
||||||
|
}
|
||||||
|
|
||||||
return ImportSpecDecision.Reject(ImportRejectionReason.NotCustomFormatUpgrade,
|
return ImportSpecDecision.Reject(ImportRejectionReason.NotCustomFormatUpgrade,
|
||||||
"Not a Custom Format upgrade for existing episode file(s). New: [{0}] ({1}) do not improve on Existing: [{2}] ({3})",
|
"Not a Custom Format upgrade for existing episode file(s). New: [{0}] ({1}) do not improve on Existing: [{2}] ({3})",
|
||||||
newFormats != null ? newFormats.ConcatToString() : "",
|
newFormats != null ? newFormats.ConcatToString() : "",
|
||||||
|
|||||||
@@ -85,7 +85,12 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
episodeFile.MediaInfo = updatedMediaInfo;
|
episodeFile.MediaInfo = updatedMediaInfo;
|
||||||
_mediaFileService.Update(episodeFile);
|
|
||||||
|
if (episodeFile.Id != 0)
|
||||||
|
{
|
||||||
|
_mediaFileService.Update(episodeFile);
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Debug("Updated MediaInfo for '{0}'", path);
|
_logger.Debug("Updated MediaInfo for '{0}'", path);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
@@ -13,13 +14,6 @@ namespace NzbDrone.Core.Parser.Model
|
|||||||
{
|
{
|
||||||
public class LocalEpisode
|
public class LocalEpisode
|
||||||
{
|
{
|
||||||
public LocalEpisode()
|
|
||||||
{
|
|
||||||
Episodes = new List<Episode>();
|
|
||||||
Languages = new List<Language>();
|
|
||||||
CustomFormats = new List<CustomFormat>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
public ParsedEpisodeInfo FileEpisodeInfo { get; set; }
|
public ParsedEpisodeInfo FileEpisodeInfo { get; set; }
|
||||||
@@ -27,10 +21,10 @@ namespace NzbDrone.Core.Parser.Model
|
|||||||
public DownloadClientItem DownloadItem { get; set; }
|
public DownloadClientItem DownloadItem { get; set; }
|
||||||
public ParsedEpisodeInfo FolderEpisodeInfo { get; set; }
|
public ParsedEpisodeInfo FolderEpisodeInfo { get; set; }
|
||||||
public Series Series { get; set; }
|
public Series Series { get; set; }
|
||||||
public List<Episode> Episodes { get; set; }
|
public List<Episode> Episodes { get; set; } = new();
|
||||||
public List<DeletedEpisodeFile> OldFiles { get; set; }
|
public List<DeletedEpisodeFile> OldFiles { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public List<Language> Languages { get; set; }
|
public List<Language> Languages { get; set; } = new();
|
||||||
public IndexerFlags IndexerFlags { get; set; }
|
public IndexerFlags IndexerFlags { get; set; }
|
||||||
public ReleaseType ReleaseType { get; set; }
|
public ReleaseType ReleaseType { get; set; }
|
||||||
public MediaInfoModel MediaInfo { get; set; }
|
public MediaInfoModel MediaInfo { get; set; }
|
||||||
@@ -40,11 +34,14 @@ namespace NzbDrone.Core.Parser.Model
|
|||||||
public string ReleaseHash { get; set; }
|
public string ReleaseHash { get; set; }
|
||||||
public string SceneName { get; set; }
|
public string SceneName { get; set; }
|
||||||
public bool OtherVideoFiles { get; set; }
|
public bool OtherVideoFiles { get; set; }
|
||||||
public List<CustomFormat> CustomFormats { get; set; }
|
public List<CustomFormat> CustomFormats { get; set; } = new();
|
||||||
public int CustomFormatScore { get; set; }
|
public int CustomFormatScore { get; set; }
|
||||||
|
public List<CustomFormat> OriginalFileNameCustomFormats { get; set; } = new();
|
||||||
|
public int OriginalFileNameCustomFormatScore { get; set; }
|
||||||
public GrabbedReleaseInfo Release { get; set; }
|
public GrabbedReleaseInfo Release { get; set; }
|
||||||
public bool ScriptImported { get; set; }
|
public bool ScriptImported { get; set; }
|
||||||
public string FileNameBeforeRename { get; set; }
|
public string FileNameBeforeRename { get; set; }
|
||||||
|
public string FileNameUsedForCustomFormatCalculation { get; set; }
|
||||||
public bool ShouldImportExtras { get; set; }
|
public bool ShouldImportExtras { get; set; }
|
||||||
public List<string> PossibleExtraFiles { get; set; }
|
public List<string> PossibleExtraFiles { get; set; }
|
||||||
public SubtitleTitleInfo SubtitleInfo { get; set; }
|
public SubtitleTitleInfo SubtitleInfo { get; set; }
|
||||||
@@ -75,5 +72,80 @@ namespace NzbDrone.Core.Parser.Model
|
|||||||
{
|
{
|
||||||
return Path;
|
return Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetSceneOrFileName()
|
||||||
|
{
|
||||||
|
if (SceneName.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return SceneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Path.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return System.IO.Path.GetFileNameWithoutExtension(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EpisodeFile ToEpisodeFile()
|
||||||
|
{
|
||||||
|
var episodeFile = new EpisodeFile
|
||||||
|
{
|
||||||
|
DateAdded = DateTime.UtcNow,
|
||||||
|
SeriesId = Series.Id,
|
||||||
|
Path = Path.CleanFilePath(),
|
||||||
|
Quality = Quality,
|
||||||
|
MediaInfo = MediaInfo,
|
||||||
|
Series = Series,
|
||||||
|
SeasonNumber = SeasonNumber,
|
||||||
|
Episodes = Episodes,
|
||||||
|
ReleaseGroup = ReleaseGroup,
|
||||||
|
ReleaseHash = ReleaseHash,
|
||||||
|
Languages = Languages,
|
||||||
|
IndexerFlags = IndexerFlags,
|
||||||
|
ReleaseType = ReleaseType,
|
||||||
|
SceneName = SceneName,
|
||||||
|
OriginalFilePath = GetOriginalFilePath()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Series.Path.IsParentPath(episodeFile.Path))
|
||||||
|
{
|
||||||
|
episodeFile.RelativePath = Series.Path.GetRelativePath(Path.CleanFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (episodeFile.ReleaseType == ReleaseType.Unknown)
|
||||||
|
{
|
||||||
|
episodeFile.ReleaseType = DownloadClientEpisodeInfo?.ReleaseType ??
|
||||||
|
FolderEpisodeInfo?.ReleaseType ??
|
||||||
|
FileEpisodeInfo?.ReleaseType ??
|
||||||
|
ReleaseType.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return episodeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetOriginalFilePath()
|
||||||
|
{
|
||||||
|
if (FolderEpisodeInfo != null)
|
||||||
|
{
|
||||||
|
var folderPath = Path.GetAncestorPath(FolderEpisodeInfo.ReleaseTitle);
|
||||||
|
|
||||||
|
if (folderPath != null)
|
||||||
|
{
|
||||||
|
return folderPath.GetParentPath().GetRelativePath(Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentPath = Path.GetParentPath();
|
||||||
|
var grandparentPath = parentPath.GetParentPath();
|
||||||
|
|
||||||
|
if (grandparentPath != null)
|
||||||
|
{
|
||||||
|
return grandparentPath.GetRelativePath(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return System.IO.Path.GetFileName(Path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user