Fixed: Parsing of releases with episode titles that contain languages
Closes #861
This commit is contained in:
parent
16e2d130e6
commit
6216a71f8c
|
@ -1,15 +1,13 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.CustomFormats
|
||||
{
|
||||
[TestFixture]
|
||||
public class CustomFormatsFixture : CoreTest
|
||||
public class CustomFormatsTestHelpers : CoreTest
|
||||
{
|
||||
private static List<CustomFormat> _customFormats { get; set; }
|
||||
|
|
@ -46,14 +46,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
|
||||
};
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(_format1, _format2);
|
||||
CustomFormatsTestHelpers.GivenCustomFormats(_format1, _format2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_allow_if_format_score_greater_than_min()
|
||||
{
|
||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format1 };
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name);
|
||||
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
|
@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_deny_if_format_score_not_greater_than_min()
|
||||
{
|
||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format2 };
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name);
|
||||
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||
|
||||
Console.WriteLine(_remoteEpisode.CustomFormatScore);
|
||||
|
@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_deny_if_format_score_not_greater_than_min_2()
|
||||
{
|
||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name);
|
||||
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
|
@ -86,7 +86,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_allow_if_all_format_is_defined_in_profile()
|
||||
{
|
||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
|
@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_deny_if_no_format_was_parsed_and_min_score_positive()
|
||||
{
|
||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { };
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
|
@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_allow_if_no_format_was_parsed_min_score_is_zero()
|
||||
{
|
||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { };
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
_remoteEpisode.Series.QualityProfile.Value.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
_remoteEpisode.Series.QualityProfile.Value.MinFormatScore = 0;
|
||||
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||
|
||||
|
|
|
@ -45,8 +45,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
|
||||
private void GivenProfile(QualityProfile profile)
|
||||
{
|
||||
CustomFormatsFixture.GivenCustomFormats();
|
||||
profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems();
|
||||
CustomFormatsTestHelpers.GivenCustomFormats();
|
||||
profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems();
|
||||
profile.MinFormatScore = 0;
|
||||
_remoteMovie.Series.QualityProfile = profile;
|
||||
|
||||
|
@ -79,7 +79,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
{
|
||||
_customFormat = new CustomFormat("My Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 1 };
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(_customFormat);
|
||||
CustomFormatsTestHelpers.GivenCustomFormats(_customFormat);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -221,7 +221,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
Cutoff = Quality.HDTV720p.Id,
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
MinFormatScore = 0,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("My Format"),
|
||||
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("My Format"),
|
||||
UpgradeAllowed = true
|
||||
});
|
||||
|
||||
|
|
|
@ -36,14 +36,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
{
|
||||
Mocker.Resolve<UpgradableSpecification>();
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats();
|
||||
CustomFormatsTestHelpers.GivenCustomFormats();
|
||||
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(e => e.QualityProfile = new QualityProfile
|
||||
{
|
||||
UpgradeAllowed = true,
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems(),
|
||||
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(),
|
||||
MinFormatScore = 0
|
||||
})
|
||||
.Build();
|
||||
|
@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
.Build();
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
private void GivenQueueFormats(List<CustomFormat> formats)
|
||||
{
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||
.Returns(formats);
|
||||
}
|
||||
|
||||
|
@ -215,9 +215,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
|
||||
var lowFormat = new List<CustomFormat> { new CustomFormat("Bad Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 2 } };
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(_remoteEpisode.CustomFormats.First(), lowFormat.First());
|
||||
CustomFormatsTestHelpers.GivenCustomFormats(_remoteEpisode.CustomFormats.First(), lowFormat.First());
|
||||
|
||||
_series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems("My Format");
|
||||
_series.QualityProfile.Value.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("My Format");
|
||||
|
||||
GivenQueueFormats(lowFormat);
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
|||
Mocker.Resolve<UpgradableSpecification>();
|
||||
_upgradeHistory = Mocker.Resolve<HistorySpecification>();
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats();
|
||||
CustomFormatsTestHelpers.GivenCustomFormats();
|
||||
|
||||
var singleEpisodeList = new List<Episode> { new Episode { Id = FIRST_EPISODE_ID, SeasonNumber = 12, EpisodeNumber = 3 } };
|
||||
var doubleEpisodeList = new List<Episode>
|
||||
|
@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
|||
{
|
||||
UpgradeAllowed = true,
|
||||
Cutoff = Quality.Bluray1080p.Id,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("None"),
|
||||
MinFormatScore = 0,
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities()
|
||||
})
|
||||
|
@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
|||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<EpisodeHistory>()))
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<EpisodeHistory>(), It.IsAny<Series>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
Mocker.Resolve<UpgradableSpecification>();
|
||||
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats();
|
||||
CustomFormatsTestHelpers.GivenCustomFormats();
|
||||
|
||||
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now, Languages = new List<Language> { Language.English } };
|
||||
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now, Languages = new List<Language> { Language.English } };
|
||||
|
@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
UpgradeAllowed = true,
|
||||
Cutoff = Quality.Bluray1080p.Id,
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("None"),
|
||||
MinFormatScore = 0,
|
||||
})
|
||||
.Build();
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download.Aggregation.Aggregators;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Aggregation.Aggregators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AggregateLanguagesFixture : CoreTest<AggregateLanguages>
|
||||
{
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
private Series _series;
|
||||
private string _simpleReleaseTitle = "Series.Title.S01E01.xyz-RlsGroup";
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.BuildList();
|
||||
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(m => m.OriginalLanguage = Language.English)
|
||||
.Build();
|
||||
|
||||
_remoteEpisode = Builder<RemoteEpisode>.CreateNew()
|
||||
.With(l => l.ParsedEpisodeInfo = null)
|
||||
.With(l => l.Episodes = episodes)
|
||||
.With(l => l.Series = _series)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private ParsedEpisodeInfo GetParsedEpisodeInfo(List<Language> languages, string releaseTitle, string releaseTokens = "")
|
||||
{
|
||||
return new ParsedEpisodeInfo
|
||||
{
|
||||
Languages = languages,
|
||||
ReleaseTitle = releaseTitle,
|
||||
ReleaseTokens = releaseTokens
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_existing_language_if_episode_title_does_not_have_language()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.Original }, _simpleReleaseTitle);
|
||||
|
||||
Subject.Aggregate(_remoteEpisode).Languages.Should().Contain(_series.OriginalLanguage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_parsed_language()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.French }, _simpleReleaseTitle);
|
||||
|
||||
Subject.Aggregate(_remoteEpisode).Languages.Should().Equal(_remoteEpisode.ParsedEpisodeInfo.Languages);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_exclude_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title()
|
||||
{
|
||||
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.xyz-RlsGroup";
|
||||
var releaseTokens = ".Jimmy.The.Greek.xyz-RlsGroup";
|
||||
|
||||
_remoteEpisode.Episodes.First().Title = "Jimmy The Greek";
|
||||
_remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
|
||||
|
||||
Subject.Aggregate(_remoteEpisode).Languages.Should().Equal(_series.OriginalLanguage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_parsed_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title()
|
||||
{
|
||||
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.French.xyz-RlsGroup";
|
||||
var releaseTokens = ".Jimmy.The.Greek.French.xyz-RlsGroup";
|
||||
|
||||
_remoteEpisode.Episodes.First().Title = "Jimmy The Greek";
|
||||
_remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.Greek, Language.French }, releaseTitle, releaseTokens);
|
||||
|
||||
Subject.Aggregate(_remoteEpisode).Languages.Should().Equal(Language.French);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_exclude_language_that_is_part_of_episode_title_when_release_tokens_does_not_contain_episode_title()
|
||||
{
|
||||
var releaseTitle = "Series.Title.S01E01.xyz-RlsGroup";
|
||||
var releaseTokens = ".xyz-RlsGroup";
|
||||
|
||||
_remoteEpisode.Episodes.First().Title = "Jimmy The Greek";
|
||||
_remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
|
||||
|
||||
Subject.Aggregate(_remoteEpisode).Languages.Should().Equal(Language.Greek);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_reparse_language_after_determining_languages_that_are_in_episode_titles()
|
||||
{
|
||||
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.Greek.xyz-RlsGroup";
|
||||
var releaseTokens = ".Jimmy.The.Greek.Greek.xyz-RlsGroup";
|
||||
|
||||
_remoteEpisode.Episodes.First().Title = "Jimmy The Greek";
|
||||
_remoteEpisode.ParsedEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
|
||||
|
||||
Subject.Aggregate(_remoteEpisode).Languages.Should().Equal(Language.Greek);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,12 +10,9 @@ using NzbDrone.Core.Datastore;
|
|||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
using NzbDrone.Core.Profiles.Releases;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
|
@ -272,7 +269,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
|||
.Returns(new List<CustomFormat>());
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
|
||||
_localEpisode.Quality = new QualityModel(Quality.Bluray2160p);
|
||||
|
@ -306,7 +303,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
|||
.Returns(new List<CustomFormat>());
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
|
||||
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
||||
|
@ -384,7 +381,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
|||
.Returns(new List<CustomFormat>());
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
|
||||
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
||||
|
@ -417,7 +414,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
|||
.Returns(new List<CustomFormat>());
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
||||
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
|
||||
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Blocklisting;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
|
@ -14,28 +13,101 @@ namespace NzbDrone.Core.CustomFormats
|
|||
{
|
||||
public interface ICustomFormatCalculationService
|
||||
{
|
||||
List<CustomFormat> ParseCustomFormat(ParsedEpisodeInfo episodeInfo, Series series);
|
||||
List<CustomFormat> ParseCustomFormat(RemoteEpisode remoteEpisode);
|
||||
List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile, Series series);
|
||||
List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile);
|
||||
List<CustomFormat> ParseCustomFormat(Blocklist blocklist);
|
||||
List<CustomFormat> ParseCustomFormat(EpisodeHistory history);
|
||||
List<CustomFormat> ParseCustomFormat(Blocklist blocklist, Series series);
|
||||
List<CustomFormat> ParseCustomFormat(EpisodeHistory history, Series series);
|
||||
}
|
||||
|
||||
public class CustomFormatCalculationService : ICustomFormatCalculationService
|
||||
{
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly ISeriesService _seriesService;
|
||||
|
||||
public CustomFormatCalculationService(ICustomFormatService formatService,
|
||||
IParsingService parsingService,
|
||||
ISeriesService seriesService)
|
||||
public CustomFormatCalculationService(ICustomFormatService formatService)
|
||||
{
|
||||
_formatService = formatService;
|
||||
_parsingService = parsingService;
|
||||
_seriesService = seriesService;
|
||||
}
|
||||
|
||||
public static List<CustomFormat> ParseCustomFormat(ParsedEpisodeInfo episodeInfo, List<CustomFormat> allCustomFormats)
|
||||
public List<CustomFormat> ParseCustomFormat(RemoteEpisode remoteEpisode)
|
||||
{
|
||||
var input = new CustomFormatInput
|
||||
{
|
||||
EpisodeInfo = remoteEpisode.ParsedEpisodeInfo,
|
||||
Series = remoteEpisode.Series,
|
||||
Size = remoteEpisode.Release.Size,
|
||||
Languages = remoteEpisode.Languages
|
||||
};
|
||||
|
||||
return ParseCustomFormat(input);
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile, Series series)
|
||||
{
|
||||
return ParseCustomFormat(episodeFile, series, _formatService.All());
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile)
|
||||
{
|
||||
return ParseCustomFormat(episodeFile, episodeFile.Series.Value, _formatService.All());
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(Blocklist blocklist, Series series)
|
||||
{
|
||||
var parsed = Parser.Parser.ParseTitle(blocklist.SourceTitle);
|
||||
|
||||
var episodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
SeriesTitle = series.Title,
|
||||
ReleaseTitle = parsed?.ReleaseTitle ?? blocklist.SourceTitle,
|
||||
Quality = blocklist.Quality,
|
||||
Languages = blocklist.Languages,
|
||||
ReleaseGroup = parsed?.ReleaseGroup
|
||||
};
|
||||
|
||||
var input = new CustomFormatInput
|
||||
{
|
||||
EpisodeInfo = episodeInfo,
|
||||
Series = series,
|
||||
Size = blocklist.Size ?? 0,
|
||||
Languages = blocklist.Languages
|
||||
};
|
||||
|
||||
return ParseCustomFormat(input);
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(EpisodeHistory history, Series series)
|
||||
{
|
||||
var parsed = Parser.Parser.ParseTitle(history.SourceTitle);
|
||||
|
||||
long.TryParse(history.Data.GetValueOrDefault("size"), out var size);
|
||||
|
||||
var episodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
SeriesTitle = series.Title,
|
||||
ReleaseTitle = parsed?.ReleaseTitle ?? history.SourceTitle,
|
||||
Quality = history.Quality,
|
||||
Languages = history.Languages,
|
||||
ReleaseGroup = parsed?.ReleaseGroup,
|
||||
};
|
||||
|
||||
var input = new CustomFormatInput
|
||||
{
|
||||
EpisodeInfo = episodeInfo,
|
||||
Series = series,
|
||||
Size = size,
|
||||
Languages = history.Languages
|
||||
};
|
||||
|
||||
return ParseCustomFormat(input);
|
||||
}
|
||||
|
||||
private List<CustomFormat> ParseCustomFormat(CustomFormatInput input)
|
||||
{
|
||||
return ParseCustomFormat(input, _formatService.All());
|
||||
}
|
||||
|
||||
private static List<CustomFormat> ParseCustomFormat(CustomFormatInput input, List<CustomFormat> allCustomFormats)
|
||||
{
|
||||
var matches = new List<CustomFormat>();
|
||||
|
||||
|
@ -45,7 +117,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||
.GroupBy(t => t.GetType())
|
||||
.Select(g => new SpecificationMatchesGroup
|
||||
{
|
||||
Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(episodeInfo))
|
||||
Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(input))
|
||||
})
|
||||
.ToList();
|
||||
|
||||
|
@ -58,7 +130,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||
return matches;
|
||||
}
|
||||
|
||||
public static List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile, List<CustomFormat> allCustomFormats)
|
||||
private static List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile, Series series, List<CustomFormat> allCustomFormats)
|
||||
{
|
||||
var sceneName = string.Empty;
|
||||
if (episodeFile.SceneName.IsNotNullOrWhiteSpace())
|
||||
|
@ -74,81 +146,25 @@ namespace NzbDrone.Core.CustomFormats
|
|||
sceneName = Path.GetFileName(episodeFile.RelativePath);
|
||||
}
|
||||
|
||||
var info = new ParsedEpisodeInfo
|
||||
var episodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
SeriesTitle = episodeFile.Series.Value.Title,
|
||||
ReleaseTitle = sceneName,
|
||||
ReleaseTitle = sceneName,
|
||||
Quality = episodeFile.Quality,
|
||||
Languages = episodeFile.Languages,
|
||||
ReleaseGroup = episodeFile.ReleaseGroup,
|
||||
ExtraInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "Size", episodeFile.Size },
|
||||
{ "Filename", Path.GetFileName(episodeFile.RelativePath) },
|
||||
{ "OriginalLanguage", episodeFile.Series.Value.OriginalLanguage }
|
||||
}
|
||||
ReleaseGroup = episodeFile.ReleaseGroup
|
||||
};
|
||||
|
||||
return ParseCustomFormat(info, allCustomFormats);
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(ParsedEpisodeInfo episodeInfo, Series series)
|
||||
{
|
||||
if (series?.OriginalLanguage != null)
|
||||
var input = new CustomFormatInput
|
||||
{
|
||||
episodeInfo.ExtraInfo["OriginalLanguage"] = series.OriginalLanguage;
|
||||
}
|
||||
|
||||
return ParseCustomFormat(episodeInfo, _formatService.All());
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile)
|
||||
{
|
||||
return ParseCustomFormat(episodeFile, _formatService.All());
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(Blocklist blocklist)
|
||||
{
|
||||
var series = _seriesService.GetSeries(blocklist.SeriesId);
|
||||
var parsed = Parser.Parser.ParseTitle(blocklist.SourceTitle);
|
||||
|
||||
var info = new ParsedEpisodeInfo
|
||||
{
|
||||
SeriesTitle = series.Title,
|
||||
ReleaseTitle = parsed?.ReleaseTitle ?? blocklist.SourceTitle,
|
||||
Quality = blocklist.Quality,
|
||||
Languages = blocklist.Languages,
|
||||
ReleaseGroup = parsed?.ReleaseGroup,
|
||||
ExtraInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "Size", blocklist.Size }
|
||||
}
|
||||
EpisodeInfo = episodeInfo,
|
||||
Series = series,
|
||||
Size = episodeFile.Size,
|
||||
Languages = episodeFile.Languages,
|
||||
Filename = Path.GetFileName(episodeFile.RelativePath)
|
||||
};
|
||||
|
||||
return ParseCustomFormat(info, series);
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(EpisodeHistory history)
|
||||
{
|
||||
var series = _seriesService.GetSeries(history.SeriesId);
|
||||
var parsed = Parser.Parser.ParseTitle(history.SourceTitle);
|
||||
|
||||
long.TryParse(history.Data.GetValueOrDefault("size"), out var size);
|
||||
|
||||
var info = new ParsedEpisodeInfo
|
||||
{
|
||||
SeriesTitle = series.Title,
|
||||
ReleaseTitle = parsed?.ReleaseTitle ?? history.SourceTitle,
|
||||
Quality = history.Quality,
|
||||
Languages = history.Languages,
|
||||
ReleaseGroup = parsed?.ReleaseGroup,
|
||||
ExtraInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "Size", size }
|
||||
}
|
||||
};
|
||||
|
||||
return ParseCustomFormat(info, series);
|
||||
return ParseCustomFormat(input, allCustomFormats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class CustomFormatInput
|
||||
{
|
||||
public ParsedEpisodeInfo EpisodeInfo { get; set; }
|
||||
public Series Series { get; set; }
|
||||
public long Size { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public string Filename { get; set; }
|
||||
|
||||
public CustomFormatInput()
|
||||
{
|
||||
Languages = new List<Language>();
|
||||
}
|
||||
|
||||
// public CustomFormatInput(ParsedEpisodeInfo episodeInfo, Series series)
|
||||
// {
|
||||
// EpisodeInfo = episodeInfo;
|
||||
// Series = series;
|
||||
// }
|
||||
//
|
||||
// public CustomFormatInput(ParsedEpisodeInfo episodeInfo, Series series, long size, List<Language> languages)
|
||||
// {
|
||||
// EpisodeInfo = episodeInfo;
|
||||
// Series = series;
|
||||
// Size = size;
|
||||
// Languages = languages;
|
||||
// }
|
||||
//
|
||||
// public CustomFormatInput(ParsedEpisodeInfo episodeInfo, Series series, long size, List<Language> languages, string filename)
|
||||
// {
|
||||
// EpisodeInfo = episodeInfo;
|
||||
// Series = series;
|
||||
// Size = size;
|
||||
// Languages = languages;
|
||||
// Filename = filename;
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
|
@ -21,9 +20,9 @@ namespace NzbDrone.Core.CustomFormats
|
|||
|
||||
public abstract NzbDroneValidationResult Validate();
|
||||
|
||||
public bool IsSatisfiedBy(ParsedEpisodeInfo episodeInfo)
|
||||
public bool IsSatisfiedBy(CustomFormatInput input)
|
||||
{
|
||||
var match = IsSatisfiedByWithoutNegate(episodeInfo);
|
||||
var match = IsSatisfiedByWithoutNegate(input);
|
||||
if (Negate)
|
||||
{
|
||||
match = !match;
|
||||
|
@ -32,6 +31,6 @@ namespace NzbDrone.Core.CustomFormats
|
|||
return match;
|
||||
}
|
||||
|
||||
protected abstract bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo);
|
||||
protected abstract bool IsSatisfiedByWithoutNegate(CustomFormatInput input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@ namespace NzbDrone.Core.CustomFormats
|
|||
NzbDroneValidationResult Validate();
|
||||
|
||||
ICustomFormatSpecification Clone();
|
||||
bool IsSatisfiedBy(ParsedEpisodeInfo episodeInfo);
|
||||
bool IsSatisfiedBy(CustomFormatInput input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ using System.Linq;
|
|||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
|
@ -32,13 +31,13 @@ namespace NzbDrone.Core.CustomFormats
|
|||
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo)
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
{
|
||||
var comparedLanguage = episodeInfo != null && Value == Language.Original.Id && episodeInfo.ExtraInfo.ContainsKey("OriginalLanguage")
|
||||
? (Language)episodeInfo.ExtraInfo["OriginalLanguage"]
|
||||
var comparedLanguage = input.EpisodeInfo != null && Value == Language.Original.Id && input.Series.OriginalLanguage != Language.Unknown
|
||||
? input.Series.OriginalLanguage
|
||||
: (Language)Value;
|
||||
|
||||
return episodeInfo?.Languages?.Contains(comparedLanguage) ?? false;
|
||||
return input.Languages?.Contains(comparedLanguage) ?? false;
|
||||
}
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class ReleaseGroupSpecification : RegexSpecificationBase
|
||||
|
@ -8,9 +6,9 @@ namespace NzbDrone.Core.CustomFormats
|
|||
public override string ImplementationName => "Release Group";
|
||||
public override string InfoLink => "https://wiki.servarr.com/sonarr/settings#custom-formats-2";
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo)
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
{
|
||||
return MatchString(episodeInfo?.ReleaseGroup);
|
||||
return MatchString(input.EpisodeInfo?.ReleaseGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class ReleaseTitleSpecification : RegexSpecificationBase
|
||||
|
@ -10,11 +6,9 @@ namespace NzbDrone.Core.CustomFormats
|
|||
public override string ImplementationName => "Release Title";
|
||||
public override string InfoLink => "https://wiki.servarr.com/sonarr/settings#custom-formats-2";
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo)
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
{
|
||||
var filename = (string)episodeInfo?.ExtraInfo?.GetValueOrDefault("Filename");
|
||||
|
||||
return MatchString(episodeInfo?.ReleaseTitle) || MatchString(filename);
|
||||
return MatchString(input.EpisodeInfo?.ReleaseTitle) || MatchString(input.Filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ namespace NzbDrone.Core.CustomFormats
|
|||
[FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo)
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
{
|
||||
return (episodeInfo?.Quality?.Quality?.Resolution ?? (int)Resolution.Unknown) == Value;
|
||||
return (input.EpisodeInfo?.Quality?.Quality?.Resolution ?? (int)Resolution.Unknown) == Value;
|
||||
}
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
|
@ -29,9 +26,9 @@ namespace NzbDrone.Core.CustomFormats
|
|||
[FieldDefinition(1, Label = "Maximum Size", HelpText = "Release must be less than or equal to this size", Unit = "GB", Type = FieldType.Number)]
|
||||
public double Max { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo)
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
{
|
||||
var size = (episodeInfo?.ExtraInfo?.GetValueOrDefault("Size", 0.0) as long?) ?? 0;
|
||||
var size = input.Size;
|
||||
|
||||
return size > Min.Gigabytes() && size <= Max.Gigabytes();
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ namespace NzbDrone.Core.CustomFormats
|
|||
[FieldDefinition(1, Label = "Source", Type = FieldType.Select, SelectOptions = typeof(QualitySource))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo)
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
{
|
||||
return (episodeInfo?.Quality?.Quality?.Source ?? (int)QualitySource.Unknown) == (QualitySource)Value;
|
||||
return (input.EpisodeInfo?.Quality?.Quality?.Source ?? (int)QualitySource.Unknown) == (QualitySource)Value;
|
||||
}
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -88,19 +88,11 @@ namespace NzbDrone.Core.DecisionEngine
|
|||
}
|
||||
}
|
||||
|
||||
if (parsedEpisodeInfo != null && report.Size > 0)
|
||||
{
|
||||
parsedEpisodeInfo.ExtraInfo.Add("Size", report.Size);
|
||||
}
|
||||
|
||||
if (parsedEpisodeInfo != null && !parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace())
|
||||
{
|
||||
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvdbId, report.TvRageId, searchCriteria);
|
||||
remoteEpisode.Release = report;
|
||||
|
||||
remoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(parsedEpisodeInfo, remoteEpisode.Series);
|
||||
remoteEpisode.CustomFormatScore = remoteEpisode?.Series?.QualityProfile?.Value.CalculateCustomFormatScore(remoteEpisode.CustomFormats) ?? 0;
|
||||
|
||||
if (remoteEpisode.Series == null)
|
||||
{
|
||||
var reason = "Unknown Series";
|
||||
|
@ -120,6 +112,10 @@ namespace NzbDrone.Core.DecisionEngine
|
|||
else
|
||||
{
|
||||
_aggregationService.Augment(remoteEpisode);
|
||||
|
||||
remoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(remoteEpisode);
|
||||
remoteEpisode.CustomFormatScore = remoteEpisode?.Series?.QualityProfile?.Value.CalculateCustomFormatScore(remoteEpisode.CustomFormats) ?? 0;
|
||||
|
||||
remoteEpisode.DownloadAllowed = remoteEpisode.Episodes.Any();
|
||||
decision = GetDecisionForReport(remoteEpisode, searchCriteria);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
continue;
|
||||
}
|
||||
|
||||
var queuedItemCustomFormats = _formatService.ParseCustomFormat(remoteEpisode.ParsedEpisodeInfo, subject.Series);
|
||||
var queuedItemCustomFormats = _formatService.ParseCustomFormat(remoteEpisode);
|
||||
|
||||
_logger.Debug("Checking if existing release in queue meets cutoff. Queued: {0}", remoteEpisode.ParsedEpisodeInfo.Quality);
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
|||
continue;
|
||||
}
|
||||
|
||||
var customFormats = _formatService.ParseCustomFormat(mostRecent);
|
||||
var customFormats = _formatService.ParseCustomFormat(mostRecent, subject.Series);
|
||||
|
||||
// The series will be the same as the one in history since it's the same episode.
|
||||
// Instead of fetching the series from the DB reuse the known series.
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download.Aggregation.Aggregators
|
||||
{
|
||||
public class AggregateLanguages : IAggregateRemoteEpisode
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public AggregateLanguages(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public RemoteEpisode Aggregate(RemoteEpisode remoteEpisode)
|
||||
{
|
||||
var parsedEpisodeInfo = remoteEpisode.ParsedEpisodeInfo;
|
||||
var languages = parsedEpisodeInfo.Languages;
|
||||
var series = remoteEpisode.Series;
|
||||
var releaseTokens = parsedEpisodeInfo.ReleaseTokens ?? parsedEpisodeInfo.ReleaseTitle;
|
||||
var normalizedReleaseTokens = Parser.Parser.NormalizeEpisodeTitle(releaseTokens);
|
||||
var languagesToRemove = new List<Language>();
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unable to aggregate languages, using parsed values: {0}", string.Join(", ", languages.ToList()));
|
||||
|
||||
remoteEpisode.Languages = languages;
|
||||
|
||||
return remoteEpisode;
|
||||
}
|
||||
|
||||
// Exclude any languages that are part of the episode title, if the episode title is in the release tokens (falls back to release title)
|
||||
foreach (var episode in remoteEpisode.Episodes)
|
||||
{
|
||||
var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title);
|
||||
|
||||
if (!episodeTitleLanguage.Contains(Language.Unknown))
|
||||
{
|
||||
var normalizedEpisodeTitle = Parser.Parser.NormalizeEpisodeTitle(episode.Title);
|
||||
var episodeTitleIndex = normalizedReleaseTokens.IndexOf(normalizedEpisodeTitle, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
if (episodeTitleIndex >= 0)
|
||||
{
|
||||
releaseTokens = releaseTokens.Remove(episodeTitleIndex, normalizedEpisodeTitle.Length);
|
||||
languagesToRemove.AddRange(episodeTitleLanguage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any languages still in the title that would normally be removed
|
||||
languagesToRemove = languagesToRemove.Except(LanguageParser.ParseLanguages(releaseTokens)).ToList();
|
||||
|
||||
// Remove all languages that aren't part of the updated releaseTokens
|
||||
languages = languages.Except(languagesToRemove).ToList();
|
||||
|
||||
// Use series language as fallback if we couldn't parse a language
|
||||
if (languages.Count == 0 || (languages.Count == 1 && languages.First() == Language.Unknown))
|
||||
{
|
||||
languages = new List<Language> { series.OriginalLanguage };
|
||||
_logger.Debug("Language couldn't be parsed from release, fallback to series original language: {0}", series.OriginalLanguage.Name);
|
||||
}
|
||||
|
||||
if (languages.Contains(Language.Original))
|
||||
{
|
||||
languages.Remove(Language.Original);
|
||||
|
||||
if (!languages.Contains(series.OriginalLanguage))
|
||||
{
|
||||
languages.Add(series.OriginalLanguage);
|
||||
}
|
||||
else
|
||||
{
|
||||
languages.Add(Language.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Debug("Selected languages: {0}", string.Join(", ", languages.ToList()));
|
||||
|
||||
remoteEpisode.Languages = languages;
|
||||
|
||||
return remoteEpisode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
|||
result.Add(remoteEpisode.ParsedEpisodeInfo.Quality.Quality.ToString());
|
||||
break;
|
||||
case (int)AdditionalTags.Languages:
|
||||
result.UnionWith(remoteEpisode.ParsedEpisodeInfo.Languages.ConvertAll(language => language.ToString()));
|
||||
result.UnionWith(remoteEpisode.Languages.ConvertAll(language => language.ToString()));
|
||||
break;
|
||||
case (int)AdditionalTags.ReleaseGroup:
|
||||
result.Add(remoteEpisode.ParsedEpisodeInfo.ReleaseGroup);
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Download
|
|||
{
|
||||
SeriesId = series.Id,
|
||||
EpisodeIds = episodes.Select(e => e.Id).ToList(),
|
||||
Languages = trackedDownload.RemoteEpisode.ParsedEpisodeInfo.Languages,
|
||||
Languages = trackedDownload.RemoteEpisode.Languages,
|
||||
Quality = trackedDownload.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||
SourceTitle = trackedDownload.DownloadItem.Title,
|
||||
DownloadClientInfo = trackedDownload.DownloadItem.DownloadClientInfo,
|
||||
|
|
|
@ -7,6 +7,7 @@ using NzbDrone.Common.Extensions;
|
|||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Aggregation;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Jobs;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
@ -44,7 +45,8 @@ namespace NzbDrone.Core.Download.Pending
|
|||
private readonly IDelayProfileService _delayProfileService;
|
||||
private readonly ITaskManager _taskManager;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ICustomFormatCalculationService _customFormatCalculationService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly IRemoteEpisodeAggregationService _aggregationService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
@ -55,7 +57,8 @@ namespace NzbDrone.Core.Download.Pending
|
|||
IDelayProfileService delayProfileService,
|
||||
ITaskManager taskManager,
|
||||
IConfigService configService,
|
||||
ICustomFormatCalculationService customFormatCalculationService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IRemoteEpisodeAggregationService aggregationService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
|
@ -66,7 +69,8 @@ namespace NzbDrone.Core.Download.Pending
|
|||
_delayProfileService = delayProfileService;
|
||||
_taskManager = taskManager;
|
||||
_configService = configService;
|
||||
_customFormatCalculationService = customFormatCalculationService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_aggregationService = aggregationService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -158,6 +162,7 @@ namespace NzbDrone.Core.Download.Pending
|
|||
var nextRssSync = new Lazy<DateTime>(() => _taskManager.GetNextExecution(typeof(RssSyncCommand)));
|
||||
|
||||
var pendingReleases = IncludeRemoteEpisodes(_repository.WithoutFallback());
|
||||
|
||||
foreach (var pendingRelease in pendingReleases)
|
||||
{
|
||||
foreach (var episode in pendingRelease.RemoteEpisode.Episodes)
|
||||
|
@ -185,7 +190,7 @@ namespace NzbDrone.Core.Download.Pending
|
|||
Id = GetQueueId(pendingRelease, episode),
|
||||
Series = pendingRelease.RemoteEpisode.Series,
|
||||
Episode = episode,
|
||||
Languages = pendingRelease.RemoteEpisode.ParsedEpisodeInfo.Languages,
|
||||
Languages = pendingRelease.RemoteEpisode.Languages,
|
||||
Quality = pendingRelease.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||
Title = pendingRelease.Title,
|
||||
Size = pendingRelease.RemoteEpisode.Release.Size,
|
||||
|
@ -330,7 +335,8 @@ namespace NzbDrone.Core.Download.Pending
|
|||
release.RemoteEpisode.Episodes = new List<Episode>();
|
||||
}
|
||||
|
||||
release.RemoteEpisode.CustomFormats = _customFormatCalculationService.ParseCustomFormat(release.RemoteEpisode.ParsedEpisodeInfo, release.RemoteEpisode.Series);
|
||||
_aggregationService.Augment(release.RemoteEpisode);
|
||||
release.RemoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(release.RemoteEpisode);
|
||||
|
||||
result.Add(release);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ using NLog;
|
|||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Download.Aggregation;
|
||||
using NzbDrone.Core.Download.History;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
@ -31,6 +32,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
private readonly IHistoryService _historyService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IDownloadHistoryService _downloadHistoryService;
|
||||
private readonly IRemoteEpisodeAggregationService _aggregationService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly Logger _logger;
|
||||
private readonly ICached<TrackedDownload> _cache;
|
||||
|
@ -41,6 +43,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
ICustomFormatCalculationService formatCalculator,
|
||||
IEventAggregator eventAggregator,
|
||||
IDownloadHistoryService downloadHistoryService,
|
||||
IRemoteEpisodeAggregationService aggregationService,
|
||||
Logger logger)
|
||||
{
|
||||
_parsingService = parsingService;
|
||||
|
@ -48,6 +51,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
_formatCalculator = formatCalculator;
|
||||
_eventAggregator = eventAggregator;
|
||||
_downloadHistoryService = downloadHistoryService;
|
||||
_aggregationService = aggregationService;
|
||||
_cache = cacheManager.GetCache<TrackedDownload>(GetType());
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -111,6 +115,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
if (parsedEpisodeInfo != null)
|
||||
{
|
||||
trackedDownload.RemoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||
|
||||
_aggregationService.Augment(trackedDownload.RemoteEpisode);
|
||||
}
|
||||
|
||||
var downloadHistory = _downloadHistoryService.GetLatestDownloadHistoryItem(downloadItem.DownloadId);
|
||||
|
@ -147,7 +153,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
// Calculate custom formats
|
||||
if (trackedDownload.RemoteEpisode != null)
|
||||
{
|
||||
trackedDownload.RemoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(parsedEpisodeInfo, trackedDownload.RemoteEpisode.Series);
|
||||
trackedDownload.RemoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(trackedDownload.RemoteEpisode);
|
||||
}
|
||||
|
||||
// Track it so it can be displayed in the queue even though we can't determine which series it is for
|
||||
|
@ -206,6 +212,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
|
||||
|
||||
trackedDownload.RemoteEpisode = parsedEpisodeInfo == null ? null : _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||
|
||||
_aggregationService.Augment(trackedDownload.RemoteEpisode);
|
||||
}
|
||||
|
||||
private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType)
|
||||
|
|
|
@ -148,7 +148,7 @@ namespace NzbDrone.Core.History
|
|||
SeriesId = episode.SeriesId,
|
||||
EpisodeId = episode.Id,
|
||||
DownloadId = message.DownloadId,
|
||||
Languages = message.Episode.ParsedEpisodeInfo.Languages,
|
||||
Languages = message.Episode.Languages,
|
||||
};
|
||||
|
||||
history.Data.Add("Indexer", message.Episode.Release.Indexer);
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace NzbDrone.Core.Organizer
|
|||
private readonly INamingConfigService _namingConfigService;
|
||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
||||
private readonly IUpdateMediaInfo _mediaInfoUpdater;
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly ICached<EpisodeFormat[]> _episodeFormatCache;
|
||||
private readonly ICached<AbsoluteEpisodeFormat[]> _absoluteEpisodeFormatCache;
|
||||
private readonly ICached<bool> _requiresEpisodeTitleCache;
|
||||
|
@ -115,13 +115,13 @@ namespace NzbDrone.Core.Organizer
|
|||
IQualityDefinitionService qualityDefinitionService,
|
||||
ICacheManager cacheManager,
|
||||
IUpdateMediaInfo mediaInfoUpdater,
|
||||
ICustomFormatService formatService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
Logger logger)
|
||||
{
|
||||
_namingConfigService = namingConfigService;
|
||||
_qualityDefinitionService = qualityDefinitionService;
|
||||
_mediaInfoUpdater = mediaInfoUpdater;
|
||||
_formatService = formatService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_episodeFormatCache = cacheManager.GetCache<EpisodeFormat[]>(GetType(), "episodeFormat");
|
||||
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
|
||||
_requiresEpisodeTitleCache = cacheManager.GetCache<bool>(GetType(), "requiresEpisodeTitle");
|
||||
|
@ -691,7 +691,7 @@ namespace NzbDrone.Core.Organizer
|
|||
if (customFormats == null)
|
||||
{
|
||||
episodeFile.Series = series;
|
||||
customFormats = CustomFormatCalculationService.ParseCustomFormat(episodeFile, _formatService.All());
|
||||
customFormats = _formatCalculator.ParseCustomFormat(episodeFile, series);
|
||||
}
|
||||
|
||||
tokenHandlers["{Custom Formats}"] = m => string.Join(" ", customFormats.Where(x => x.IncludeCustomFormatWhenRenaming));
|
||||
|
|
|
@ -30,9 +30,6 @@ namespace NzbDrone.Core.Parser.Model
|
|||
public string ReleaseTokens { get; set; }
|
||||
public int? DailyPart { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, object> ExtraInfo { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
public ParsedEpisodeInfo()
|
||||
{
|
||||
EpisodeNumbers = new int[0];
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Parser.Model
|
||||
|
@ -23,11 +24,13 @@ namespace NzbDrone.Core.Parser.Model
|
|||
public List<CustomFormat> CustomFormats { get; set; }
|
||||
public int CustomFormatScore { get; set; }
|
||||
public SeriesMatchType SeriesMatchType { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
|
||||
public RemoteEpisode()
|
||||
{
|
||||
Episodes = new List<Episode>();
|
||||
CustomFormats = new List<CustomFormat>();
|
||||
Languages = new List<Language>();
|
||||
}
|
||||
|
||||
public bool IsRecentEpisode()
|
||||
|
|
|
@ -184,30 +184,9 @@ namespace NzbDrone.Core.Parser
|
|||
{
|
||||
remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, remoteEpisode.MappedSeasonNumber, sceneSource, searchCriteria);
|
||||
}
|
||||
|
||||
parsedEpisodeInfo.ExtraInfo["OriginalLanguage"] = series.OriginalLanguage;
|
||||
}
|
||||
|
||||
// Use series language as fallback if we could't parse a language (more accurate than just using English)
|
||||
if (parsedEpisodeInfo.Languages.Count <= 1 && parsedEpisodeInfo.Languages.First() == Language.Unknown && series != null)
|
||||
{
|
||||
parsedEpisodeInfo.Languages = new List<Language> { series.OriginalLanguage };
|
||||
_logger.Debug("Language couldn't be parsed from release, fallback to series original language: {0}", series.OriginalLanguage.Name);
|
||||
}
|
||||
|
||||
if (parsedEpisodeInfo.Languages.Contains(Language.Original))
|
||||
{
|
||||
parsedEpisodeInfo.Languages.Remove(Language.Original);
|
||||
|
||||
if (series != null && !parsedEpisodeInfo.Languages.Contains(series.OriginalLanguage))
|
||||
{
|
||||
parsedEpisodeInfo.Languages.Add(series.OriginalLanguage);
|
||||
}
|
||||
else
|
||||
{
|
||||
parsedEpisodeInfo.Languages.Add(Language.Unknown);
|
||||
}
|
||||
}
|
||||
remoteEpisode.Languages = parsedEpisodeInfo.Languages;
|
||||
|
||||
if (remoteEpisode.Episodes == null)
|
||||
{
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace Sonarr.Api.V3.Blocklist
|
|||
SourceTitle = model.SourceTitle,
|
||||
Languages = model.Languages,
|
||||
Quality = model.Quality,
|
||||
CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(),
|
||||
CustomFormats = formatCalculator.ParseCustomFormat(model, model.Series).ToResource(),
|
||||
Date = model.Date,
|
||||
Protocol = model.Protocol,
|
||||
Indexer = model.Indexer,
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace Sonarr.Api.V3.History
|
|||
SourceTitle = model.SourceTitle,
|
||||
Languages = model.Languages,
|
||||
Quality = model.Quality,
|
||||
CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(),
|
||||
CustomFormats = formatCalculator.ParseCustomFormat(model, model.Series).ToResource(),
|
||||
|
||||
// QualityCutoffNotMet
|
||||
Date = model.Date,
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace Sonarr.Api.V3.Indexers
|
|||
Title = releaseInfo.Title,
|
||||
FullSeason = parsedEpisodeInfo.FullSeason,
|
||||
SeasonNumber = parsedEpisodeInfo.SeasonNumber,
|
||||
Languages = parsedEpisodeInfo.Languages,
|
||||
Languages = remoteEpisode.Languages,
|
||||
AirDate = parsedEpisodeInfo.AirDate,
|
||||
SeriesTitle = parsedEpisodeInfo.SeriesTitle,
|
||||
EpisodeNumbers = parsedEpisodeInfo.EpisodeNumbers,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Download.Aggregation;
|
||||
using NzbDrone.Core.Parser;
|
||||
using Sonarr.Api.V3.Episodes;
|
||||
using Sonarr.Api.V3.Series;
|
||||
|
@ -11,10 +12,13 @@ namespace Sonarr.Api.V3.Parse
|
|||
public class ParseController : Controller
|
||||
{
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IRemoteEpisodeAggregationService _aggregationService;
|
||||
|
||||
public ParseController(IParsingService parsingService)
|
||||
public ParseController(IParsingService parsingService,
|
||||
IRemoteEpisodeAggregationService aggregationService)
|
||||
{
|
||||
_parsingService = parsingService;
|
||||
_aggregationService = aggregationService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
@ -38,6 +42,8 @@ namespace Sonarr.Api.V3.Parse
|
|||
|
||||
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||
|
||||
_aggregationService.Augment(remoteEpisode);
|
||||
|
||||
if (remoteEpisode != null)
|
||||
{
|
||||
return new ParseResource
|
||||
|
|
Loading…
Reference in New Issue