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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.CustomFormats;
|
using NzbDrone.Core.CustomFormats;
|
||||||
using NzbDrone.Core.Profiles;
|
using NzbDrone.Core.Profiles;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.CustomFormats
|
namespace NzbDrone.Core.Test.CustomFormats
|
||||||
{
|
{
|
||||||
[TestFixture]
|
public class CustomFormatsTestHelpers : CoreTest
|
||||||
public class CustomFormatsFixture : CoreTest
|
|
||||||
{
|
{
|
||||||
private static List<CustomFormat> _customFormats { get; set; }
|
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)) },
|
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
|
||||||
};
|
};
|
||||||
|
|
||||||
CustomFormatsFixture.GivenCustomFormats(_format1, _format2);
|
CustomFormatsTestHelpers.GivenCustomFormats(_format1, _format2);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_allow_if_format_score_greater_than_min()
|
public void should_allow_if_format_score_greater_than_min()
|
||||||
{
|
{
|
||||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format1 };
|
_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);
|
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
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()
|
public void should_deny_if_format_score_not_greater_than_min()
|
||||||
{
|
{
|
||||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format2 };
|
_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);
|
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||||
|
|
||||||
Console.WriteLine(_remoteEpisode.CustomFormatScore);
|
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()
|
public void should_deny_if_format_score_not_greater_than_min_2()
|
||||||
{
|
{
|
||||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
_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);
|
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
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()
|
public void should_allow_if_all_format_is_defined_in_profile()
|
||||||
{
|
{
|
||||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
_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);
|
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
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()
|
public void should_deny_if_no_format_was_parsed_and_min_score_positive()
|
||||||
{
|
{
|
||||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { };
|
_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);
|
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
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()
|
public void should_allow_if_no_format_was_parsed_min_score_is_zero()
|
||||||
{
|
{
|
||||||
_remoteEpisode.CustomFormats = new List<CustomFormat> { };
|
_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.Series.QualityProfile.Value.MinFormatScore = 0;
|
||||||
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
_remoteEpisode.CustomFormatScore = _remoteEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(_remoteEpisode.CustomFormats);
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
|
|
||||||
private void GivenProfile(QualityProfile profile)
|
private void GivenProfile(QualityProfile profile)
|
||||||
{
|
{
|
||||||
CustomFormatsFixture.GivenCustomFormats();
|
CustomFormatsTestHelpers.GivenCustomFormats();
|
||||||
profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems();
|
profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems();
|
||||||
profile.MinFormatScore = 0;
|
profile.MinFormatScore = 0;
|
||||||
_remoteMovie.Series.QualityProfile = profile;
|
_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 };
|
_customFormat = new CustomFormat("My Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 1 };
|
||||||
|
|
||||||
CustomFormatsFixture.GivenCustomFormats(_customFormat);
|
CustomFormatsTestHelpers.GivenCustomFormats(_customFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -221,7 +221,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
Cutoff = Quality.HDTV720p.Id,
|
Cutoff = Quality.HDTV720p.Id,
|
||||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||||
MinFormatScore = 0,
|
MinFormatScore = 0,
|
||||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("My Format"),
|
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("My Format"),
|
||||||
UpgradeAllowed = true
|
UpgradeAllowed = true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -36,14 +36,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
{
|
{
|
||||||
Mocker.Resolve<UpgradableSpecification>();
|
Mocker.Resolve<UpgradableSpecification>();
|
||||||
|
|
||||||
CustomFormatsFixture.GivenCustomFormats();
|
CustomFormatsTestHelpers.GivenCustomFormats();
|
||||||
|
|
||||||
_series = Builder<Series>.CreateNew()
|
_series = Builder<Series>.CreateNew()
|
||||||
.With(e => e.QualityProfile = new QualityProfile
|
.With(e => e.QualityProfile = new QualityProfile
|
||||||
{
|
{
|
||||||
UpgradeAllowed = true,
|
UpgradeAllowed = true,
|
||||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems(),
|
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(),
|
||||||
MinFormatScore = 0
|
MinFormatScore = 0
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
.Setup(x => x.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
.Setup(x => x.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
private void GivenQueueFormats(List<CustomFormat> formats)
|
private void GivenQueueFormats(List<CustomFormat> formats)
|
||||||
{
|
{
|
||||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
.Setup(x => x.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
.Setup(x => x.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||||
.Returns(formats);
|
.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 } };
|
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);
|
GivenQueueFormats(lowFormat);
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||||
Mocker.Resolve<UpgradableSpecification>();
|
Mocker.Resolve<UpgradableSpecification>();
|
||||||
_upgradeHistory = Mocker.Resolve<HistorySpecification>();
|
_upgradeHistory = Mocker.Resolve<HistorySpecification>();
|
||||||
|
|
||||||
CustomFormatsFixture.GivenCustomFormats();
|
CustomFormatsTestHelpers.GivenCustomFormats();
|
||||||
|
|
||||||
var singleEpisodeList = new List<Episode> { new Episode { Id = FIRST_EPISODE_ID, SeasonNumber = 12, EpisodeNumber = 3 } };
|
var singleEpisodeList = new List<Episode> { new Episode { Id = FIRST_EPISODE_ID, SeasonNumber = 12, EpisodeNumber = 3 } };
|
||||||
var doubleEpisodeList = new List<Episode>
|
var doubleEpisodeList = new List<Episode>
|
||||||
|
@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||||
{
|
{
|
||||||
UpgradeAllowed = true,
|
UpgradeAllowed = true,
|
||||||
Cutoff = Quality.Bluray1080p.Id,
|
Cutoff = Quality.Bluray1080p.Id,
|
||||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("None"),
|
||||||
MinFormatScore = 0,
|
MinFormatScore = 0,
|
||||||
Items = Qualities.QualityFixture.GetDefaultQualities()
|
Items = Qualities.QualityFixture.GetDefaultQualities()
|
||||||
})
|
})
|
||||||
|
@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
.Setup(x => x.ParseCustomFormat(It.IsAny<EpisodeHistory>()))
|
.Setup(x => x.ParseCustomFormat(It.IsAny<EpisodeHistory>(), It.IsAny<Series>()))
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
Mocker.Resolve<UpgradableSpecification>();
|
Mocker.Resolve<UpgradableSpecification>();
|
||||||
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
|
_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 } };
|
_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 } };
|
_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,
|
UpgradeAllowed = true,
|
||||||
Cutoff = Quality.Bluray1080p.Id,
|
Cutoff = Quality.Bluray1080p.Id,
|
||||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("None"),
|
||||||
MinFormatScore = 0,
|
MinFormatScore = 0,
|
||||||
})
|
})
|
||||||
.Build();
|
.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.Languages;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||||
using NzbDrone.Core.Parser;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Profiles.Qualities;
|
using NzbDrone.Core.Profiles.Qualities;
|
||||||
using NzbDrone.Core.Profiles.Releases;
|
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Test.CustomFormats;
|
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
|
@ -272,7 +269,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
_localEpisode.Quality = new QualityModel(Quality.Bluray2160p);
|
_localEpisode.Quality = new QualityModel(Quality.Bluray2160p);
|
||||||
|
@ -306,7 +303,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
||||||
|
@ -384,7 +381,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
||||||
|
@ -417,7 +414,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||||
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Series>()))
|
.Setup(s => s.ParseCustomFormat(It.IsAny<RemoteEpisode>()))
|
||||||
.Returns(new List<CustomFormat>());
|
.Returns(new List<CustomFormat>());
|
||||||
|
|
||||||
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Blocklisting;
|
using NzbDrone.Core.Blocklisting;
|
||||||
|
using NzbDrone.Core.Datastore.Migration;
|
||||||
using NzbDrone.Core.History;
|
using NzbDrone.Core.History;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Parser;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
|
@ -14,28 +13,101 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
{
|
{
|
||||||
public interface ICustomFormatCalculationService
|
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(EpisodeFile episodeFile);
|
||||||
List<CustomFormat> ParseCustomFormat(Blocklist blocklist);
|
List<CustomFormat> ParseCustomFormat(Blocklist blocklist, Series series);
|
||||||
List<CustomFormat> ParseCustomFormat(EpisodeHistory history);
|
List<CustomFormat> ParseCustomFormat(EpisodeHistory history, Series series);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CustomFormatCalculationService : ICustomFormatCalculationService
|
public class CustomFormatCalculationService : ICustomFormatCalculationService
|
||||||
{
|
{
|
||||||
private readonly ICustomFormatService _formatService;
|
private readonly ICustomFormatService _formatService;
|
||||||
private readonly IParsingService _parsingService;
|
|
||||||
private readonly ISeriesService _seriesService;
|
|
||||||
|
|
||||||
public CustomFormatCalculationService(ICustomFormatService formatService,
|
public CustomFormatCalculationService(ICustomFormatService formatService)
|
||||||
IParsingService parsingService,
|
|
||||||
ISeriesService seriesService)
|
|
||||||
{
|
{
|
||||||
_formatService = 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>();
|
var matches = new List<CustomFormat>();
|
||||||
|
|
||||||
|
@ -45,7 +117,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
.GroupBy(t => t.GetType())
|
.GroupBy(t => t.GetType())
|
||||||
.Select(g => new SpecificationMatchesGroup
|
.Select(g => new SpecificationMatchesGroup
|
||||||
{
|
{
|
||||||
Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(episodeInfo))
|
Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(input))
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
@ -58,7 +130,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
return matches;
|
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;
|
var sceneName = string.Empty;
|
||||||
if (episodeFile.SceneName.IsNotNullOrWhiteSpace())
|
if (episodeFile.SceneName.IsNotNullOrWhiteSpace())
|
||||||
|
@ -74,81 +146,25 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
sceneName = Path.GetFileName(episodeFile.RelativePath);
|
sceneName = Path.GetFileName(episodeFile.RelativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = new ParsedEpisodeInfo
|
var episodeInfo = new ParsedEpisodeInfo
|
||||||
{
|
{
|
||||||
SeriesTitle = episodeFile.Series.Value.Title,
|
SeriesTitle = episodeFile.Series.Value.Title,
|
||||||
ReleaseTitle = sceneName,
|
ReleaseTitle = sceneName,
|
||||||
Quality = episodeFile.Quality,
|
Quality = episodeFile.Quality,
|
||||||
Languages = episodeFile.Languages,
|
Languages = episodeFile.Languages,
|
||||||
ReleaseGroup = episodeFile.ReleaseGroup,
|
ReleaseGroup = episodeFile.ReleaseGroup
|
||||||
ExtraInfo = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "Size", episodeFile.Size },
|
|
||||||
{ "Filename", Path.GetFileName(episodeFile.RelativePath) },
|
|
||||||
{ "OriginalLanguage", episodeFile.Series.Value.OriginalLanguage }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return ParseCustomFormat(info, allCustomFormats);
|
var input = new CustomFormatInput
|
||||||
}
|
|
||||||
|
|
||||||
public List<CustomFormat> ParseCustomFormat(ParsedEpisodeInfo episodeInfo, Series series)
|
|
||||||
{
|
|
||||||
if (series?.OriginalLanguage != null)
|
|
||||||
{
|
{
|
||||||
episodeInfo.ExtraInfo["OriginalLanguage"] = series.OriginalLanguage;
|
EpisodeInfo = episodeInfo,
|
||||||
}
|
Series = series,
|
||||||
|
Size = episodeFile.Size,
|
||||||
return ParseCustomFormat(episodeInfo, _formatService.All());
|
Languages = episodeFile.Languages,
|
||||||
}
|
Filename = Path.GetFileName(episodeFile.RelativePath)
|
||||||
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return ParseCustomFormat(info, series);
|
return ParseCustomFormat(input, allCustomFormats);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.CustomFormats
|
namespace NzbDrone.Core.CustomFormats
|
||||||
|
@ -21,9 +20,9 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
|
|
||||||
public abstract NzbDroneValidationResult Validate();
|
public abstract NzbDroneValidationResult Validate();
|
||||||
|
|
||||||
public bool IsSatisfiedBy(ParsedEpisodeInfo episodeInfo)
|
public bool IsSatisfiedBy(CustomFormatInput input)
|
||||||
{
|
{
|
||||||
var match = IsSatisfiedByWithoutNegate(episodeInfo);
|
var match = IsSatisfiedByWithoutNegate(input);
|
||||||
if (Negate)
|
if (Negate)
|
||||||
{
|
{
|
||||||
match = !match;
|
match = !match;
|
||||||
|
@ -32,6 +31,6 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo);
|
protected abstract bool IsSatisfiedByWithoutNegate(CustomFormatInput input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,6 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
NzbDroneValidationResult Validate();
|
NzbDroneValidationResult Validate();
|
||||||
|
|
||||||
ICustomFormatSpecification Clone();
|
ICustomFormatSpecification Clone();
|
||||||
bool IsSatisfiedBy(ParsedEpisodeInfo episodeInfo);
|
bool IsSatisfiedBy(CustomFormatInput input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ using System.Linq;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.Languages;
|
using NzbDrone.Core.Languages;
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.CustomFormats
|
namespace NzbDrone.Core.CustomFormats
|
||||||
|
@ -32,13 +31,13 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
|
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
|
||||||
public int Value { get; set; }
|
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")
|
var comparedLanguage = input.EpisodeInfo != null && Value == Language.Original.Id && input.Series.OriginalLanguage != Language.Unknown
|
||||||
? (Language)episodeInfo.ExtraInfo["OriginalLanguage"]
|
? input.Series.OriginalLanguage
|
||||||
: (Language)Value;
|
: (Language)Value;
|
||||||
|
|
||||||
return episodeInfo?.Languages?.Contains(comparedLanguage) ?? false;
|
return input.Languages?.Contains(comparedLanguage) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.CustomFormats
|
namespace NzbDrone.Core.CustomFormats
|
||||||
{
|
{
|
||||||
public class ReleaseGroupSpecification : RegexSpecificationBase
|
public class ReleaseGroupSpecification : RegexSpecificationBase
|
||||||
|
@ -8,9 +6,9 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
public override string ImplementationName => "Release Group";
|
public override string ImplementationName => "Release Group";
|
||||||
public override string InfoLink => "https://wiki.servarr.com/sonarr/settings#custom-formats-2";
|
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
|
namespace NzbDrone.Core.CustomFormats
|
||||||
{
|
{
|
||||||
public class ReleaseTitleSpecification : RegexSpecificationBase
|
public class ReleaseTitleSpecification : RegexSpecificationBase
|
||||||
|
@ -10,11 +6,9 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
public override string ImplementationName => "Release Title";
|
public override string ImplementationName => "Release Title";
|
||||||
public override string InfoLink => "https://wiki.servarr.com/sonarr/settings#custom-formats-2";
|
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(input.EpisodeInfo?.ReleaseTitle) || MatchString(input.Filename);
|
||||||
|
|
||||||
return MatchString(episodeInfo?.ReleaseTitle) || MatchString(filename);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ namespace NzbDrone.Core.CustomFormats
|
||||||
[FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
|
[FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
|
||||||
public int Value { get; set; }
|
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()
|
public override NzbDroneValidationResult Validate()
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.CustomFormats
|
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)]
|
[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; }
|
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();
|
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))]
|
[FieldDefinition(1, Label = "Source", Type = FieldType.Select, SelectOptions = typeof(QualitySource))]
|
||||||
public int Value { get; set; }
|
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()
|
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())
|
if (parsedEpisodeInfo != null && !parsedEpisodeInfo.SeriesTitle.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvdbId, report.TvRageId, searchCriteria);
|
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvdbId, report.TvRageId, searchCriteria);
|
||||||
remoteEpisode.Release = report;
|
remoteEpisode.Release = report;
|
||||||
|
|
||||||
remoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(parsedEpisodeInfo, remoteEpisode.Series);
|
|
||||||
remoteEpisode.CustomFormatScore = remoteEpisode?.Series?.QualityProfile?.Value.CalculateCustomFormatScore(remoteEpisode.CustomFormats) ?? 0;
|
|
||||||
|
|
||||||
if (remoteEpisode.Series == null)
|
if (remoteEpisode.Series == null)
|
||||||
{
|
{
|
||||||
var reason = "Unknown Series";
|
var reason = "Unknown Series";
|
||||||
|
@ -120,6 +112,10 @@ namespace NzbDrone.Core.DecisionEngine
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_aggregationService.Augment(remoteEpisode);
|
_aggregationService.Augment(remoteEpisode);
|
||||||
|
|
||||||
|
remoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(remoteEpisode);
|
||||||
|
remoteEpisode.CustomFormatScore = remoteEpisode?.Series?.QualityProfile?.Value.CalculateCustomFormatScore(remoteEpisode.CustomFormats) ?? 0;
|
||||||
|
|
||||||
remoteEpisode.DownloadAllowed = remoteEpisode.Episodes.Any();
|
remoteEpisode.DownloadAllowed = remoteEpisode.Episodes.Any();
|
||||||
decision = GetDecisionForReport(remoteEpisode, searchCriteria);
|
decision = GetDecisionForReport(remoteEpisode, searchCriteria);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
continue;
|
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);
|
_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;
|
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.
|
// 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.
|
// 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());
|
result.Add(remoteEpisode.ParsedEpisodeInfo.Quality.Quality.ToString());
|
||||||
break;
|
break;
|
||||||
case (int)AdditionalTags.Languages:
|
case (int)AdditionalTags.Languages:
|
||||||
result.UnionWith(remoteEpisode.ParsedEpisodeInfo.Languages.ConvertAll(language => language.ToString()));
|
result.UnionWith(remoteEpisode.Languages.ConvertAll(language => language.ToString()));
|
||||||
break;
|
break;
|
||||||
case (int)AdditionalTags.ReleaseGroup:
|
case (int)AdditionalTags.ReleaseGroup:
|
||||||
result.Add(remoteEpisode.ParsedEpisodeInfo.ReleaseGroup);
|
result.Add(remoteEpisode.ParsedEpisodeInfo.ReleaseGroup);
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace NzbDrone.Core.Download
|
||||||
{
|
{
|
||||||
SeriesId = series.Id,
|
SeriesId = series.Id,
|
||||||
EpisodeIds = episodes.Select(e => e.Id).ToList(),
|
EpisodeIds = episodes.Select(e => e.Id).ToList(),
|
||||||
Languages = trackedDownload.RemoteEpisode.ParsedEpisodeInfo.Languages,
|
Languages = trackedDownload.RemoteEpisode.Languages,
|
||||||
Quality = trackedDownload.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
Quality = trackedDownload.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||||
SourceTitle = trackedDownload.DownloadItem.Title,
|
SourceTitle = trackedDownload.DownloadItem.Title,
|
||||||
DownloadClientInfo = trackedDownload.DownloadItem.DownloadClientInfo,
|
DownloadClientInfo = trackedDownload.DownloadItem.DownloadClientInfo,
|
||||||
|
|
|
@ -7,6 +7,7 @@ using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.CustomFormats;
|
using NzbDrone.Core.CustomFormats;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
|
using NzbDrone.Core.Download.Aggregation;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.Jobs;
|
using NzbDrone.Core.Jobs;
|
||||||
using NzbDrone.Core.Languages;
|
using NzbDrone.Core.Languages;
|
||||||
|
@ -44,7 +45,8 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
private readonly IDelayProfileService _delayProfileService;
|
private readonly IDelayProfileService _delayProfileService;
|
||||||
private readonly ITaskManager _taskManager;
|
private readonly ITaskManager _taskManager;
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly ICustomFormatCalculationService _customFormatCalculationService;
|
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||||
|
private readonly IRemoteEpisodeAggregationService _aggregationService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
@ -55,7 +57,8 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
IDelayProfileService delayProfileService,
|
IDelayProfileService delayProfileService,
|
||||||
ITaskManager taskManager,
|
ITaskManager taskManager,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
ICustomFormatCalculationService customFormatCalculationService,
|
ICustomFormatCalculationService formatCalculator,
|
||||||
|
IRemoteEpisodeAggregationService aggregationService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
|
@ -66,7 +69,8 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
_delayProfileService = delayProfileService;
|
_delayProfileService = delayProfileService;
|
||||||
_taskManager = taskManager;
|
_taskManager = taskManager;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_customFormatCalculationService = customFormatCalculationService;
|
_formatCalculator = formatCalculator;
|
||||||
|
_aggregationService = aggregationService;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
@ -158,6 +162,7 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
var nextRssSync = new Lazy<DateTime>(() => _taskManager.GetNextExecution(typeof(RssSyncCommand)));
|
var nextRssSync = new Lazy<DateTime>(() => _taskManager.GetNextExecution(typeof(RssSyncCommand)));
|
||||||
|
|
||||||
var pendingReleases = IncludeRemoteEpisodes(_repository.WithoutFallback());
|
var pendingReleases = IncludeRemoteEpisodes(_repository.WithoutFallback());
|
||||||
|
|
||||||
foreach (var pendingRelease in pendingReleases)
|
foreach (var pendingRelease in pendingReleases)
|
||||||
{
|
{
|
||||||
foreach (var episode in pendingRelease.RemoteEpisode.Episodes)
|
foreach (var episode in pendingRelease.RemoteEpisode.Episodes)
|
||||||
|
@ -185,7 +190,7 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
Id = GetQueueId(pendingRelease, episode),
|
Id = GetQueueId(pendingRelease, episode),
|
||||||
Series = pendingRelease.RemoteEpisode.Series,
|
Series = pendingRelease.RemoteEpisode.Series,
|
||||||
Episode = episode,
|
Episode = episode,
|
||||||
Languages = pendingRelease.RemoteEpisode.ParsedEpisodeInfo.Languages,
|
Languages = pendingRelease.RemoteEpisode.Languages,
|
||||||
Quality = pendingRelease.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
Quality = pendingRelease.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||||
Title = pendingRelease.Title,
|
Title = pendingRelease.Title,
|
||||||
Size = pendingRelease.RemoteEpisode.Release.Size,
|
Size = pendingRelease.RemoteEpisode.Release.Size,
|
||||||
|
@ -330,7 +335,8 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
release.RemoteEpisode.Episodes = new List<Episode>();
|
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);
|
result.Add(release);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ using NLog;
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.CustomFormats;
|
using NzbDrone.Core.CustomFormats;
|
||||||
|
using NzbDrone.Core.Download.Aggregation;
|
||||||
using NzbDrone.Core.Download.History;
|
using NzbDrone.Core.Download.History;
|
||||||
using NzbDrone.Core.History;
|
using NzbDrone.Core.History;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
@ -31,6 +32,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
private readonly IHistoryService _historyService;
|
private readonly IHistoryService _historyService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly IDownloadHistoryService _downloadHistoryService;
|
private readonly IDownloadHistoryService _downloadHistoryService;
|
||||||
|
private readonly IRemoteEpisodeAggregationService _aggregationService;
|
||||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
private readonly ICached<TrackedDownload> _cache;
|
private readonly ICached<TrackedDownload> _cache;
|
||||||
|
@ -41,6 +43,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
ICustomFormatCalculationService formatCalculator,
|
ICustomFormatCalculationService formatCalculator,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
IDownloadHistoryService downloadHistoryService,
|
IDownloadHistoryService downloadHistoryService,
|
||||||
|
IRemoteEpisodeAggregationService aggregationService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_parsingService = parsingService;
|
_parsingService = parsingService;
|
||||||
|
@ -48,6 +51,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
_formatCalculator = formatCalculator;
|
_formatCalculator = formatCalculator;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_downloadHistoryService = downloadHistoryService;
|
_downloadHistoryService = downloadHistoryService;
|
||||||
|
_aggregationService = aggregationService;
|
||||||
_cache = cacheManager.GetCache<TrackedDownload>(GetType());
|
_cache = cacheManager.GetCache<TrackedDownload>(GetType());
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
@ -111,6 +115,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
if (parsedEpisodeInfo != null)
|
if (parsedEpisodeInfo != null)
|
||||||
{
|
{
|
||||||
trackedDownload.RemoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
trackedDownload.RemoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||||
|
|
||||||
|
_aggregationService.Augment(trackedDownload.RemoteEpisode);
|
||||||
}
|
}
|
||||||
|
|
||||||
var downloadHistory = _downloadHistoryService.GetLatestDownloadHistoryItem(downloadItem.DownloadId);
|
var downloadHistory = _downloadHistoryService.GetLatestDownloadHistoryItem(downloadItem.DownloadId);
|
||||||
|
@ -147,7 +153,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
// Calculate custom formats
|
// Calculate custom formats
|
||||||
if (trackedDownload.RemoteEpisode != null)
|
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
|
// 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);
|
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
|
||||||
|
|
||||||
trackedDownload.RemoteEpisode = parsedEpisodeInfo == null ? null : _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
trackedDownload.RemoteEpisode = parsedEpisodeInfo == null ? null : _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||||
|
|
||||||
|
_aggregationService.Augment(trackedDownload.RemoteEpisode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType)
|
private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType)
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace NzbDrone.Core.History
|
||||||
SeriesId = episode.SeriesId,
|
SeriesId = episode.SeriesId,
|
||||||
EpisodeId = episode.Id,
|
EpisodeId = episode.Id,
|
||||||
DownloadId = message.DownloadId,
|
DownloadId = message.DownloadId,
|
||||||
Languages = message.Episode.ParsedEpisodeInfo.Languages,
|
Languages = message.Episode.Languages,
|
||||||
};
|
};
|
||||||
|
|
||||||
history.Data.Add("Indexer", message.Episode.Release.Indexer);
|
history.Data.Add("Indexer", message.Episode.Release.Indexer);
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
private readonly INamingConfigService _namingConfigService;
|
private readonly INamingConfigService _namingConfigService;
|
||||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
private readonly IQualityDefinitionService _qualityDefinitionService;
|
||||||
private readonly IUpdateMediaInfo _mediaInfoUpdater;
|
private readonly IUpdateMediaInfo _mediaInfoUpdater;
|
||||||
private readonly ICustomFormatService _formatService;
|
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||||
private readonly ICached<EpisodeFormat[]> _episodeFormatCache;
|
private readonly ICached<EpisodeFormat[]> _episodeFormatCache;
|
||||||
private readonly ICached<AbsoluteEpisodeFormat[]> _absoluteEpisodeFormatCache;
|
private readonly ICached<AbsoluteEpisodeFormat[]> _absoluteEpisodeFormatCache;
|
||||||
private readonly ICached<bool> _requiresEpisodeTitleCache;
|
private readonly ICached<bool> _requiresEpisodeTitleCache;
|
||||||
|
@ -115,13 +115,13 @@ namespace NzbDrone.Core.Organizer
|
||||||
IQualityDefinitionService qualityDefinitionService,
|
IQualityDefinitionService qualityDefinitionService,
|
||||||
ICacheManager cacheManager,
|
ICacheManager cacheManager,
|
||||||
IUpdateMediaInfo mediaInfoUpdater,
|
IUpdateMediaInfo mediaInfoUpdater,
|
||||||
ICustomFormatService formatService,
|
ICustomFormatCalculationService formatCalculator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_namingConfigService = namingConfigService;
|
_namingConfigService = namingConfigService;
|
||||||
_qualityDefinitionService = qualityDefinitionService;
|
_qualityDefinitionService = qualityDefinitionService;
|
||||||
_mediaInfoUpdater = mediaInfoUpdater;
|
_mediaInfoUpdater = mediaInfoUpdater;
|
||||||
_formatService = formatService;
|
_formatCalculator = formatCalculator;
|
||||||
_episodeFormatCache = cacheManager.GetCache<EpisodeFormat[]>(GetType(), "episodeFormat");
|
_episodeFormatCache = cacheManager.GetCache<EpisodeFormat[]>(GetType(), "episodeFormat");
|
||||||
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
|
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
|
||||||
_requiresEpisodeTitleCache = cacheManager.GetCache<bool>(GetType(), "requiresEpisodeTitle");
|
_requiresEpisodeTitleCache = cacheManager.GetCache<bool>(GetType(), "requiresEpisodeTitle");
|
||||||
|
@ -691,7 +691,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
if (customFormats == null)
|
if (customFormats == null)
|
||||||
{
|
{
|
||||||
episodeFile.Series = series;
|
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));
|
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 string ReleaseTokens { get; set; }
|
||||||
public int? DailyPart { get; set; }
|
public int? DailyPart { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Dictionary<string, object> ExtraInfo { get; set; } = new Dictionary<string, object>();
|
|
||||||
|
|
||||||
public ParsedEpisodeInfo()
|
public ParsedEpisodeInfo()
|
||||||
{
|
{
|
||||||
EpisodeNumbers = new int[0];
|
EpisodeNumbers = new int[0];
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||||
using NzbDrone.Core.CustomFormats;
|
using NzbDrone.Core.CustomFormats;
|
||||||
using NzbDrone.Core.DataAugmentation.Scene;
|
using NzbDrone.Core.DataAugmentation.Scene;
|
||||||
using NzbDrone.Core.Download.Clients;
|
using NzbDrone.Core.Download.Clients;
|
||||||
|
using NzbDrone.Core.Languages;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Parser.Model
|
namespace NzbDrone.Core.Parser.Model
|
||||||
|
@ -23,11 +24,13 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
public List<CustomFormat> CustomFormats { get; set; }
|
public List<CustomFormat> CustomFormats { get; set; }
|
||||||
public int CustomFormatScore { get; set; }
|
public int CustomFormatScore { get; set; }
|
||||||
public SeriesMatchType SeriesMatchType { get; set; }
|
public SeriesMatchType SeriesMatchType { get; set; }
|
||||||
|
public List<Language> Languages { get; set; }
|
||||||
|
|
||||||
public RemoteEpisode()
|
public RemoteEpisode()
|
||||||
{
|
{
|
||||||
Episodes = new List<Episode>();
|
Episodes = new List<Episode>();
|
||||||
CustomFormats = new List<CustomFormat>();
|
CustomFormats = new List<CustomFormat>();
|
||||||
|
Languages = new List<Language>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRecentEpisode()
|
public bool IsRecentEpisode()
|
||||||
|
|
|
@ -184,30 +184,9 @@ namespace NzbDrone.Core.Parser
|
||||||
{
|
{
|
||||||
remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, remoteEpisode.MappedSeasonNumber, sceneSource, searchCriteria);
|
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)
|
remoteEpisode.Languages = parsedEpisodeInfo.Languages;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remoteEpisode.Episodes == null)
|
if (remoteEpisode.Episodes == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace Sonarr.Api.V3.Blocklist
|
||||||
SourceTitle = model.SourceTitle,
|
SourceTitle = model.SourceTitle,
|
||||||
Languages = model.Languages,
|
Languages = model.Languages,
|
||||||
Quality = model.Quality,
|
Quality = model.Quality,
|
||||||
CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(),
|
CustomFormats = formatCalculator.ParseCustomFormat(model, model.Series).ToResource(),
|
||||||
Date = model.Date,
|
Date = model.Date,
|
||||||
Protocol = model.Protocol,
|
Protocol = model.Protocol,
|
||||||
Indexer = model.Indexer,
|
Indexer = model.Indexer,
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace Sonarr.Api.V3.History
|
||||||
SourceTitle = model.SourceTitle,
|
SourceTitle = model.SourceTitle,
|
||||||
Languages = model.Languages,
|
Languages = model.Languages,
|
||||||
Quality = model.Quality,
|
Quality = model.Quality,
|
||||||
CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(),
|
CustomFormats = formatCalculator.ParseCustomFormat(model, model.Series).ToResource(),
|
||||||
|
|
||||||
// QualityCutoffNotMet
|
// QualityCutoffNotMet
|
||||||
Date = model.Date,
|
Date = model.Date,
|
||||||
|
|
|
@ -104,7 +104,7 @@ namespace Sonarr.Api.V3.Indexers
|
||||||
Title = releaseInfo.Title,
|
Title = releaseInfo.Title,
|
||||||
FullSeason = parsedEpisodeInfo.FullSeason,
|
FullSeason = parsedEpisodeInfo.FullSeason,
|
||||||
SeasonNumber = parsedEpisodeInfo.SeasonNumber,
|
SeasonNumber = parsedEpisodeInfo.SeasonNumber,
|
||||||
Languages = parsedEpisodeInfo.Languages,
|
Languages = remoteEpisode.Languages,
|
||||||
AirDate = parsedEpisodeInfo.AirDate,
|
AirDate = parsedEpisodeInfo.AirDate,
|
||||||
SeriesTitle = parsedEpisodeInfo.SeriesTitle,
|
SeriesTitle = parsedEpisodeInfo.SeriesTitle,
|
||||||
EpisodeNumbers = parsedEpisodeInfo.EpisodeNumbers,
|
EpisodeNumbers = parsedEpisodeInfo.EpisodeNumbers,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Download.Aggregation;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
using Sonarr.Api.V3.Episodes;
|
using Sonarr.Api.V3.Episodes;
|
||||||
using Sonarr.Api.V3.Series;
|
using Sonarr.Api.V3.Series;
|
||||||
|
@ -11,10 +12,13 @@ namespace Sonarr.Api.V3.Parse
|
||||||
public class ParseController : Controller
|
public class ParseController : Controller
|
||||||
{
|
{
|
||||||
private readonly IParsingService _parsingService;
|
private readonly IParsingService _parsingService;
|
||||||
|
private readonly IRemoteEpisodeAggregationService _aggregationService;
|
||||||
|
|
||||||
public ParseController(IParsingService parsingService)
|
public ParseController(IParsingService parsingService,
|
||||||
|
IRemoteEpisodeAggregationService aggregationService)
|
||||||
{
|
{
|
||||||
_parsingService = parsingService;
|
_parsingService = parsingService;
|
||||||
|
_aggregationService = aggregationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
@ -38,6 +42,8 @@ namespace Sonarr.Api.V3.Parse
|
||||||
|
|
||||||
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||||
|
|
||||||
|
_aggregationService.Augment(remoteEpisode);
|
||||||
|
|
||||||
if (remoteEpisode != null)
|
if (remoteEpisode != null)
|
||||||
{
|
{
|
||||||
return new ParseResource
|
return new ParseResource
|
||||||
|
|
Loading…
Reference in New Issue