New: Store and use original Series language

This commit is contained in:
Qstick 2022-10-02 20:29:53 -05:00 committed by Mark McDowall
parent 5400bce129
commit be0fa73129
29 changed files with 193 additions and 113 deletions

View File

@ -11,7 +11,22 @@ function createMapStateToProps() {
return createSelector( return createSelector(
createLanguagesSelector(), createLanguagesSelector(),
(languages) => { (languages) => {
return languages; const {
isFetching,
isPopulated,
error,
items
} = languages;
const filterItems = ['Any', 'Original'];
const filteredLanguages = items.filter((lang) => !filterItems.includes(lang.name));
return {
isFetching,
isPopulated,
error,
items: filteredLanguages
};
} }
); );
} }

View File

@ -46,6 +46,15 @@ function SeriesIndexSortMenu(props) {
Network Network
</SortMenuItem> </SortMenuItem>
<SortMenuItem
name="originalLanguage"
sortKey={sortKey}
sortDirection={sortDirection}
onPress={onSortSelect}
>
Original Language
</SortMenuItem>
<SortMenuItem <SortMenuItem
name="qualityProfileId" name="qualityProfileId"
sortKey={sortKey} sortKey={sortKey}

View File

@ -30,6 +30,7 @@
flex: 2 0 90px; flex: 2 0 90px;
} }
.originalLanguage,
.qualityProfileId { .qualityProfileId {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';

View File

@ -66,6 +66,7 @@
flex: 2 0 90px; flex: 2 0 90px;
} }
.originalLanguage,
.qualityProfileId { .qualityProfileId {
composes: cell; composes: cell;

View File

@ -85,6 +85,7 @@ class SeriesIndexRow extends Component {
titleSlug, titleSlug,
seriesType, seriesType,
network, network,
originalLanguage,
qualityProfile, qualityProfile,
nextAiring, nextAiring,
previousAiring, previousAiring,
@ -212,6 +213,17 @@ class SeriesIndexRow extends Component {
); );
} }
if (name === 'originalLanguage') {
return (
<VirtualTableRowCell
key={name}
className={styles[name]}
>
{originalLanguage.name}
</VirtualTableRowCell>
);
}
if (name === 'qualityProfileId') { if (name === 'qualityProfileId') {
return ( return (
<VirtualTableRowCell <VirtualTableRowCell
@ -511,6 +523,7 @@ SeriesIndexRow.propTypes = {
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
titleSlug: PropTypes.string.isRequired, titleSlug: PropTypes.string.isRequired,
seriesType: PropTypes.string.isRequired, seriesType: PropTypes.string.isRequired,
originalLanguage: PropTypes.object.isRequired,
network: PropTypes.string, network: PropTypes.string,
qualityProfile: PropTypes.object.isRequired, qualityProfile: PropTypes.object.isRequired,
nextAiring: PropTypes.string, nextAiring: PropTypes.string,

View File

@ -131,6 +131,13 @@ export const filterPredicates = {
return predicate(item.ratings.value * 10, filterValue); return predicate(item.ratings.value * 10, filterValue);
}, },
originalLanguage: function(item, filterValue, type) {
const predicate = filterTypePredicates[type];
const { originalLanguage } = item;
return predicate(originalLanguage ? originalLanguage.name : '', filterValue);
},
releaseGroups: function(item, filterValue, type) { releaseGroups: function(item, filterValue, type) {
const { statistics = {} } = item; const { statistics = {} } = item;
@ -267,6 +274,25 @@ export const filterBuilderProps = [
return tagList.sort(sortByName); return tagList.sort(sortByName);
} }
}, },
{
name: 'originalLanguage',
label: 'Original Language',
type: filterBuilderTypes.EXACT,
optionsSelector: function(items) {
const languageList = items.reduce((acc, series) => {
if (series.originalLanguage) {
acc.push({
id: series.originalLanguage.name,
name: series.originalLanguage.name
});
}
return acc;
}, []);
return languageList.sort(sortByName);
}
},
{ {
name: 'releaseGroups', name: 'releaseGroups',
label: 'Release Groups', label: 'Release Groups',

View File

@ -95,6 +95,12 @@ export const defaultState = {
isSortable: true, isSortable: true,
isVisible: false isVisible: false
}, },
{
name: 'originalLanguage',
label: 'Original Language',
isSortable: true,
isVisible: false
},
{ {
name: 'added', name: 'added',
label: 'Added', label: 'Added',
@ -249,6 +255,12 @@ export const defaultState = {
return statistics.seasonCount; return statistics.seasonCount;
}, },
originalLanguage: function(item) {
const { originalLanguage = {} } = item;
return originalLanguage.name;
},
ratings: function(item) { ratings: function(item) {
const { ratings = {} } = item; const { ratings = {} } = item;

View File

@ -18,6 +18,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
public class AggregateLanguageFixture : CoreTest<AggregateLanguage> public class AggregateLanguageFixture : CoreTest<AggregateLanguage>
{ {
private LocalEpisode _localEpisode; private LocalEpisode _localEpisode;
private Series _series;
private string _simpleReleaseTitle = "Series.Title.S01E01.xyz-RlsGroup"; private string _simpleReleaseTitle = "Series.Title.S01E01.xyz-RlsGroup";
[SetUp] [SetUp]
@ -26,11 +27,16 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
var episodes = Builder<Episode>.CreateListOfSize(1) var episodes = Builder<Episode>.CreateListOfSize(1)
.BuildList(); .BuildList();
_series = Builder<Series>.CreateNew()
.With(m => m.OriginalLanguage = Language.English)
.Build();
_localEpisode = Builder<LocalEpisode>.CreateNew() _localEpisode = Builder<LocalEpisode>.CreateNew()
.With(l => l.DownloadClientEpisodeInfo = null) .With(l => l.DownloadClientEpisodeInfo = null)
.With(l => l.FolderEpisodeInfo = null) .With(l => l.FolderEpisodeInfo = null)
.With(l => l.FileEpisodeInfo = null) .With(l => l.FileEpisodeInfo = null)
.With(l => l.Episodes = episodes) .With(l => l.Episodes = episodes)
.With(l => l.Series = _series)
.Build(); .Build();
} }
@ -72,7 +78,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
{ {
var result = Subject.Aggregate(_localEpisode, null); var result = Subject.Aggregate(_localEpisode, null);
result.Languages.Should().Contain(Language.English); result.Languages.Should().Contain(_series.OriginalLanguage);
} }
[Test] [Test]
@ -120,13 +126,13 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
[Test] [Test]
public void should_return_file_language_when_file_language_is_higher_than_others() public void should_return_file_language_when_file_language_is_higher_than_others()
{ {
_localEpisode.DownloadClientEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.English }, _simpleReleaseTitle); _localEpisode.DownloadClientEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.Unknown }, _simpleReleaseTitle);
_localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.English }, _simpleReleaseTitle); _localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.Unknown }, _simpleReleaseTitle);
_localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.French }, _simpleReleaseTitle); _localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(new List<Language> { Language.French }, _simpleReleaseTitle);
GivenAugmenters(new List<Language> { Language.French }, GivenAugmenters(new List<Language> { Language.French },
new List<Language> { Language.English }, new List<Language> { Language.Unknown },
new List<Language> { Language.English }, new List<Language> { Language.Unknown },
null); null);
Subject.Aggregate(_localEpisode, null).Languages.Should().Contain(_localEpisode.FileEpisodeInfo.Languages); Subject.Aggregate(_localEpisode, null).Languages.Should().Contain(_localEpisode.FileEpisodeInfo.Languages);
@ -135,9 +141,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
[Test] [Test]
public void should_return_multi_language() public void should_return_multi_language()
{ {
GivenAugmenters(new List<Language> { Language.English }, GivenAugmenters(new List<Language> { Language.Unknown },
new List<Language> { Language.French, Language.German }, new List<Language> { Language.French, Language.German },
new List<Language> { Language.English }, new List<Language> { Language.Unknown },
null); null);
Subject.Aggregate(_localEpisode, null).Languages.Should().Equal(new List<Language> { Language.French, Language.German }); Subject.Aggregate(_localEpisode, null).Languages.Should().Equal(new List<Language> { Language.French, Language.German });

View File

@ -260,46 +260,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
} }
[Test]
public void should_return_false_if_it_is_a_preferred_word_downgrade_and_equal_language_and_quality()
{
var lowFormat = new List<CustomFormat> { new CustomFormat("Bad Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 2 } };
CustomFormatsFixture.GivenCustomFormats(lowFormat.First());
_series.QualityProfile.Value.FormatItems = CustomFormatsFixture.GetSampleFormatItems();
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotPrefer);
Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(s => s.ParseCustomFormat(It.IsAny<EpisodeFile>()))
.Returns(new List<CustomFormat>());
Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(s => s.ParseCustomFormat(It.IsAny<ParsedEpisodeInfo>()))
.Returns(lowFormat);
_localEpisode.Quality = new QualityModel(Quality.Bluray1080p);
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.EpisodeFileId = 1)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.Bluray1080p),
Languages = new List<Language> { Language.Spanish }
}))
.Build()
.ToList();
_localEpisode.FileEpisodeInfo = Builder<ParsedEpisodeInfo>.CreateNew().Build();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test] [Test]
public void should_return_true_if_it_is_a_preferred_word_downgrade_and_language_downgrade_and_a_quality_upgrade() public void should_return_true_if_it_is_a_preferred_word_downgrade_and_language_downgrade_and_a_quality_upgrade()
{ {

View File

@ -11,21 +11,27 @@ namespace NzbDrone.Core.Test.ParserTests
public class LanguageParserFixture : CoreTest public class LanguageParserFixture : CoreTest
{ {
[TestCase("Title.the.Series.2009.S01E14.English.HDTV.XviD-LOL")] [TestCase("Title.the.Series.2009.S01E14.English.HDTV.XviD-LOL")]
[TestCase("Series Title - S01E01 - Pilot.English.sub")]
[TestCase("Series Title - S01E01 - Pilot.english.sub")]
public void should_parse_language_english(string postTitle)
{
var result = LanguageParser.ParseLanguages(postTitle);
result.First().Should().Be(Language.English);
}
[TestCase("Spanish Killroy was Here S02E02 Flodden 720p AMZN WEB-DL DDP5 1 H 264-NTb")]
[TestCase("Title.the.Spanish.Series.S02E02.1080p.WEB.H264-CAKES")]
[TestCase("Title.the.Spanish.Series.S02E06.Field.of.Cloth.of.Gold.1080p.AMZN.WEBRip.DDP5.1.x264-NTb")]
[TestCase("Title.the.Series.2009.S01E14.Germany.HDTV.XviD-LOL")] [TestCase("Title.the.Series.2009.S01E14.Germany.HDTV.XviD-LOL")]
[TestCase("Title.the.Series.2009.S01E14.HDTV.XviD-LOL")] [TestCase("Title.the.Series.2009.S01E14.HDTV.XviD-LOL")]
[TestCase("Title.the.Italian.Series.S01E01.The.Family.720p.HDTV.x264-FTP")] [TestCase("Title.the.Italian.Series.S01E01.The.Family.720p.HDTV.x264-FTP")]
[TestCase("Title.the.Italy.Series.S02E01.720p.HDTV.x264-TLA")] [TestCase("Title.the.Italy.Series.S02E01.720p.HDTV.x264-TLA")]
[TestCase("Series Title - S01E01 - Pilot.en.sub")] [TestCase("Series Title - S01E01 - Pilot.en.sub")]
[TestCase("Series Title - S01E01 - Pilot.eng.sub")] [TestCase("Series Title - S01E01 - Pilot.eng.sub")]
[TestCase("Series Title - S01E01 - Pilot.English.sub")] public void should_parse_language_unknown(string postTitle)
[TestCase("Series Title - S01E01 - Pilot.english.sub")]
[TestCase("Spanish Killroy was Here S02E02 Flodden 720p AMZN WEB-DL DDP5 1 H 264-NTb")]
[TestCase("Title.the.Spanish.Series.S02E02.1080p.WEB.H264-CAKES")]
[TestCase("Title.the.Spanish.Series.S02E06.Field.of.Cloth.of.Gold.1080p.AMZN.WEBRip.DDP5.1.x264-NTb")]
public void should_parse_language_english(string postTitle)
{ {
var result = LanguageParser.ParseLanguages(postTitle); var result = LanguageParser.ParseLanguages(postTitle);
result.First().Should().Be(Language.English); result.First().Should().Be(Language.Unknown);
} }
[TestCase("Series Title - S01E01 - Pilot.sub")] [TestCase("Series Title - S01E01 - Pilot.sub")]
@ -314,7 +320,7 @@ namespace NzbDrone.Core.Test.ParserTests
public void should_not_parse_series_or_episode_title(string postTitle) public void should_not_parse_series_or_episode_title(string postTitle)
{ {
var result = LanguageParser.ParseLanguages(postTitle); var result = LanguageParser.ParseLanguages(postTitle);
result.First().Name.Should().Be(Language.English.Name); result.First().Name.Should().Be(Language.Unknown.Name);
} }
} }
} }

View File

@ -7,6 +7,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -41,7 +42,8 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
SeriesTitle = _series.Title, SeriesTitle = _series.Title,
SeasonNumber = 1, SeasonNumber = 1,
EpisodeNumbers = new[] { 1 }, EpisodeNumbers = new[] { 1 },
AbsoluteEpisodeNumbers = new int[0] AbsoluteEpisodeNumbers = new int[0],
Languages = new List<Language> { Language.English }
}; };
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
@ -7,6 +7,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -41,7 +42,8 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
SeriesTitle = _series.Title, SeriesTitle = _series.Title,
SeriesTitleInfo = new SeriesTitleInfo(), SeriesTitleInfo = new SeriesTitleInfo(),
SeasonNumber = 1, SeasonNumber = 1,
EpisodeNumbers = new[] { 1 } EpisodeNumbers = new[] { 1 },
Languages = new List<Language> { Language.English }
}; };
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria

View File

@ -84,7 +84,8 @@ namespace NzbDrone.Core.CustomFormats
ExtraInfo = new Dictionary<string, object> ExtraInfo = new Dictionary<string, object>
{ {
{ "Size", episodeFile.Size }, { "Size", episodeFile.Size },
{ "Filename", Path.GetFileName(episodeFile.RelativePath) } { "Filename", Path.GetFileName(episodeFile.RelativePath) },
{ "OriginalLanguage", episodeFile.Series.Value.OriginalLanguage }
} }
}; };

View File

@ -34,7 +34,11 @@ namespace NzbDrone.Core.CustomFormats
protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo) protected override bool IsSatisfiedByWithoutNegate(ParsedEpisodeInfo episodeInfo)
{ {
return episodeInfo?.Languages?.Contains((Language)Value) ?? false; var comparedLanguage = episodeInfo != null && Value == Language.Original.Id && episodeInfo.ExtraInfo.ContainsKey("OriginalLanguage")
? (Language)episodeInfo.ExtraInfo["OriginalLanguage"]
: (Language)Value;
return episodeInfo?.Languages?.Contains(comparedLanguage) ?? false;
} }
public override NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()

View File

@ -0,0 +1,16 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(176)]
public class original_language : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Series")
.AddColumn("OriginalLanguage").AsInt32().WithDefaultValue((int)Language.English);
}
}
}

View File

@ -102,6 +102,7 @@ namespace NzbDrone.Core.Languages
public static Language Malayalam => new Language(29, "Malayalam"); public static Language Malayalam => new Language(29, "Malayalam");
public static Language Ukrainian => new Language(30, "Ukrainian"); public static Language Ukrainian => new Language(30, "Ukrainian");
public static Language Slovak => new Language(31, "Slovak"); public static Language Slovak => new Language(31, "Slovak");
public static Language Original => new Language(-2, "Original");
public static List<Language> All public static List<Language> All
{ {
@ -140,7 +141,8 @@ namespace NzbDrone.Core.Languages
Bulgarian, Bulgarian,
Malayalam, Malayalam,
Ukrainian, Ukrainian,
Slovak Slovak,
Original
}; };
} }
} }

View File

@ -22,7 +22,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators
public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
{ {
var languages = new List<Language> { Language.English }; var languages = new List<Language> { localEpisode.Series?.OriginalLanguage ?? Language.Unknown };
var languagesConfidence = Confidence.Default; var languagesConfidence = Confidence.Default;
foreach (var augmentLanguage in _augmentLanguages) foreach (var augmentLanguage in _augmentLanguages)
@ -37,7 +37,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators
if (augmentedLanguage?.Languages != null && if (augmentedLanguage?.Languages != null &&
augmentedLanguage.Languages.Count > 0 && augmentedLanguage.Languages.Count > 0 &&
!(augmentedLanguage.Languages.Count == 1 && augmentedLanguage.Languages.Contains(Language.English)) &&
!(augmentedLanguage.Languages.Count == 1 && augmentedLanguage.Languages.Contains(Language.Unknown))) !(augmentedLanguage.Languages.Count == 1 && augmentedLanguage.Languages.Contains(Language.Unknown)))
{ {
languages = augmentedLanguage.Languages; languages = augmentedLanguage.Languages;

View File

@ -23,7 +23,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment
foreach (var episode in localEpisode.Episodes) foreach (var episode in localEpisode.Episodes)
{ {
var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title, false); var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title);
languages = languages.Except(episodeTitleLanguage).ToList(); languages = languages.Except(episodeTitleLanguage).ToList();
} }

View File

@ -21,7 +21,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment
foreach (var episode in localEpisode.Episodes) foreach (var episode in localEpisode.Episodes)
{ {
var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title, false); var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title);
languages = languages.Except(episodeTitleLanguage).ToList(); languages = languages.Except(episodeTitleLanguage).ToList();
} }

View File

@ -21,7 +21,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators.Augment
foreach (var episode in localEpisode.Episodes) foreach (var episode in localEpisode.Episodes)
{ {
var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title, false); var episodeTitleLanguage = LanguageParser.ParseLanguages(episode.Title);
languages = languages.Except(episodeTitleLanguage).ToList(); languages = languages.Except(episodeTitleLanguage).ToList();
} }

View File

@ -139,6 +139,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
var rootFolder = Path.GetDirectoryName(path); var rootFolder = Path.GetDirectoryName(path);
var series = _seriesService.GetSeries(seriesId); var series = _seriesService.GetSeries(seriesId);
var languageParse = LanguageParser.ParseLanguages(path);
if (languageParse.Count <= 1 && languageParse.First() == Language.Unknown && series != null)
{
languageParse = new List<Language> { series.OriginalLanguage };
_logger.Debug("Language couldn't be parsed from release, falling back to series original language: {0}", series.OriginalLanguage.Name);
}
if (episodeIds.Any()) if (episodeIds.Any())
{ {
var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem; var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem;
@ -153,7 +161,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
localEpisode.ExistingFile = series.Path.IsParentPath(path); localEpisode.ExistingFile = series.Path.IsParentPath(path);
localEpisode.Size = _diskProvider.GetFileSize(path); localEpisode.Size = _diskProvider.GetFileSize(path);
localEpisode.ReleaseGroup = releaseGroup.IsNullOrWhiteSpace() ? Parser.Parser.ParseReleaseGroup(path) : releaseGroup; localEpisode.ReleaseGroup = releaseGroup.IsNullOrWhiteSpace() ? Parser.Parser.ParseReleaseGroup(path) : releaseGroup;
localEpisode.Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? LanguageParser.ParseLanguages(path) : languages; localEpisode.Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? languageParse : languages;
localEpisode.Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality; localEpisode.Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality;
return MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null); return MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null);

View File

@ -5,9 +5,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.CustomFormats; using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Releases;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
@ -43,7 +41,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
} }
var qualityCompare = qualityComparer.Compare(localEpisode.Quality.Quality, episodeFile.Quality.Quality); var qualityCompare = qualityComparer.Compare(localEpisode.Quality.Quality, episodeFile.Quality.Quality);
var customFormatScore = GetCustomFormatScore(localEpisode);
if (qualityCompare < 0) if (qualityCompare < 0)
{ {
@ -62,46 +59,9 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
_logger.Debug("This file isn't a quality revision upgrade for all episodes. Skipping {0}", localEpisode.Path); _logger.Debug("This file isn't a quality revision upgrade for all episodes. Skipping {0}", localEpisode.Path);
return Decision.Reject("Not a quality revision upgrade for existing episode file(s)"); return Decision.Reject("Not a quality revision upgrade for existing episode file(s)");
} }
var customFormats = _customFormatCalculationService.ParseCustomFormat(episodeFile);
var episodeFileCustomFormatScore = localEpisode.Series.QualityProfile.Value.CalculateCustomFormatScore(customFormats);
if (qualityCompare == 0 && customFormatScore < episodeFileCustomFormatScore)
{
_logger.Debug("This file isn't a custom format upgrade for episode. Skipping {0}", localEpisode.Path);
return Decision.Reject("Not a custom format upgrade for existing episode file(s)");
}
} }
return Decision.Accept(); return Decision.Accept();
} }
private int GetCustomFormatScore(LocalEpisode localEpisode)
{
var series = localEpisode.Series;
var scores = new List<int>();
var fileFormats = new List<CustomFormat>();
var folderFormats = new List<CustomFormat>();
var clientFormats = new List<CustomFormat>();
if (localEpisode.FileEpisodeInfo != null)
{
fileFormats = _customFormatCalculationService.ParseCustomFormat(localEpisode.FileEpisodeInfo);
}
if (localEpisode.FolderEpisodeInfo != null)
{
folderFormats = _customFormatCalculationService.ParseCustomFormat(localEpisode.FolderEpisodeInfo);
}
if (localEpisode.DownloadClientEpisodeInfo != null)
{
clientFormats = _customFormatCalculationService.ParseCustomFormat(localEpisode.DownloadClientEpisodeInfo);
}
var formats = fileFormats.Union(folderFormats.Union(clientFormats)).ToList();
return series.QualityProfile.Value.CalculateCustomFormatScore(formats);
}
} }
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
{ {
@ -29,6 +29,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public string Network { get; set; } public string Network { get; set; }
public string ImdbId { get; set; } public string ImdbId { get; set; }
public string OriginalLanguage { get; set; }
public List<ActorResource> Actors { get; set; } public List<ActorResource> Actors { get; set; }
public List<string> Genres { get; set; } public List<string> Genres { get; set; }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
@ -9,8 +9,10 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.DataAugmentation.DailySeries; using NzbDrone.Core.DataAugmentation.DailySeries;
using NzbDrone.Core.Exceptions; using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource.SkyHook.Resource; using NzbDrone.Core.MetadataSource.SkyHook.Resource;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MetadataSource.SkyHook namespace NzbDrone.Core.MetadataSource.SkyHook
@ -158,6 +160,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
series.CleanTitle = Parser.Parser.CleanSeriesTitle(show.Title); series.CleanTitle = Parser.Parser.CleanSeriesTitle(show.Title);
series.SortTitle = SeriesTitleNormalizer.Normalize(show.Title, show.TvdbId); series.SortTitle = SeriesTitleNormalizer.Normalize(show.Title, show.TvdbId);
series.OriginalLanguage = IsoLanguages.Find(show.OriginalLanguage.ToLower())?.Language ?? Language.English;
if (show.FirstAired != null) if (show.FirstAired != null)
{ {
series.FirstAired = DateTime.ParseExact(show.FirstAired, "yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); series.FirstAired = DateTime.ParseExact(show.FirstAired, "yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);

View File

@ -27,7 +27,7 @@ namespace NzbDrone.Core.Parser
private static readonly Regex SubtitleLanguageRegex = new Regex(".+?[-_. ](?<iso_code>[a-z]{2,3})([-_. ](?<tags>full|forced|foreign|default|cc|psdh|sdh))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex SubtitleLanguageRegex = new Regex(".+?[-_. ](?<iso_code>[a-z]{2,3})([-_. ](?<tags>full|forced|foreign|default|cc|psdh|sdh))*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static List<Language> ParseLanguages(string title, bool defaultToEnglish = true) public static List<Language> ParseLanguages(string title)
{ {
foreach (var regex in CleanSeriesTitleRegex) foreach (var regex in CleanSeriesTitleRegex)
{ {
@ -175,7 +175,7 @@ namespace NzbDrone.Core.Parser
if (!languages.Any()) if (!languages.Any())
{ {
languages.Add(defaultToEnglish ? Language.English : Language.Unknown); languages.Add(Language.Unknown);
} }
return languages.DistinctBy(l => (int)l).ToList(); return languages.DistinctBy(l => (int)l).ToList();

View File

@ -6,6 +6,8 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -182,6 +184,29 @@ 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)
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)

View File

@ -91,6 +91,7 @@ namespace NzbDrone.Core.Tv
series.ImdbId = seriesInfo.ImdbId; series.ImdbId = seriesInfo.ImdbId;
series.AirTime = seriesInfo.AirTime; series.AirTime = seriesInfo.AirTime;
series.Overview = seriesInfo.Overview; series.Overview = seriesInfo.Overview;
series.OriginalLanguage = seriesInfo.OriginalLanguage;
series.Status = seriesInfo.Status; series.Status = seriesInfo.Status;
series.CleanTitle = seriesInfo.CleanTitle; series.CleanTitle = seriesInfo.CleanTitle;
series.SortTitle = seriesInfo.SortTitle; series.SortTitle = seriesInfo.SortTitle;

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
namespace NzbDrone.Core.Tv namespace NzbDrone.Core.Tv
@ -15,6 +16,7 @@ namespace NzbDrone.Core.Tv
Actors = new List<Actor>(); Actors = new List<Actor>();
Seasons = new List<Season>(); Seasons = new List<Season>();
Tags = new HashSet<int>(); Tags = new HashSet<int>();
OriginalLanguage = Language.English;
} }
public int TvdbId { get; set; } public int TvdbId { get; set; }
@ -47,6 +49,7 @@ namespace NzbDrone.Core.Tv
public DateTime Added { get; set; } public DateTime Added { get; set; }
public DateTime? FirstAired { get; set; } public DateTime? FirstAired { get; set; }
public LazyLoaded<QualityProfile> QualityProfile { get; set; } public LazyLoaded<QualityProfile> QualityProfile { get; set; }
public Language OriginalLanguage { get; set; }
public List<Season> Seasons { get; set; } public List<Season> Seasons { get; set; }
public HashSet<int> Tags { get; set; } public HashSet<int> Tags { get; set; }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using Sonarr.Http.REST; using Sonarr.Http.REST;
@ -30,7 +31,7 @@ namespace Sonarr.Api.V3.Series
public string Network { get; set; } public string Network { get; set; }
public string AirTime { get; set; } public string AirTime { get; set; }
public List<MediaCover> Images { get; set; } public List<MediaCover> Images { get; set; }
public Language OriginalLanguage { get; set; }
public string RemotePoster { get; set; } public string RemotePoster { get; set; }
public List<SeasonResource> Seasons { get; set; } public List<SeasonResource> Seasons { get; set; }
public int Year { get; set; } public int Year { get; set; }
@ -100,6 +101,7 @@ namespace Sonarr.Api.V3.Series
Seasons = model.Seasons.ToResource(includeSeasonImages), Seasons = model.Seasons.ToResource(includeSeasonImages),
Year = model.Year, Year = model.Year,
OriginalLanguage = model.OriginalLanguage,
Path = model.Path, Path = model.Path,
QualityProfileId = model.QualityProfileId, QualityProfileId = model.QualityProfileId,
@ -161,6 +163,7 @@ namespace Sonarr.Api.V3.Series
Seasons = resource.Seasons?.ToModel() ?? new List<Season>(), Seasons = resource.Seasons?.ToModel() ?? new List<Season>(),
Year = resource.Year, Year = resource.Year,
OriginalLanguage = resource.OriginalLanguage,
Path = resource.Path, Path = resource.Path,
QualityProfileId = resource.QualityProfileId, QualityProfileId = resource.QualityProfileId,