New: Language Profiles

Closes #274
This commit is contained in:
vawen 2015-07-12 18:44:33 +02:00 committed by Taloth Saldono
parent 7297c1b8e4
commit 068ea1e934
147 changed files with 3026 additions and 681 deletions

View File

@ -4,6 +4,7 @@ using Sonarr.Http.REST;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Api.Series; using NzbDrone.Api.Series;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Languages;
namespace NzbDrone.Api.Blacklist namespace NzbDrone.Api.Blacklist
{ {
@ -17,6 +18,7 @@ namespace NzbDrone.Api.Blacklist
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
public string Indexer { get; set; } public string Indexer { get; set; }
public string Message { get; set; } public string Message { get; set; }
public Language Language { get; set; }
public SeriesResource Series { get; set; } public SeriesResource Series { get; set; }
} }

View File

@ -12,9 +12,9 @@ namespace NzbDrone.Api.Calendar
{ {
public CalendarModule(IEpisodeService episodeService, public CalendarModule(IEpisodeService episodeService,
ISeriesService seriesService, ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster) IBroadcastSignalRMessage signalRBroadcaster)
: base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "calendar") : base(episodeService, seriesService, upgradableSpecification, signalRBroadcaster, "calendar")
{ {
GetResourceAll = GetCalendar; GetResourceAll = GetCalendar;
} }

View File

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Sonarr.Http.REST;
using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
@ -8,6 +7,7 @@ using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Exceptions; using NzbDrone.Core.Exceptions;
using NzbDrone.SignalR; using NzbDrone.SignalR;
using Sonarr.Http;
using HttpStatusCode = System.Net.HttpStatusCode; using HttpStatusCode = System.Net.HttpStatusCode;
namespace NzbDrone.Api.EpisodeFiles namespace NzbDrone.Api.EpisodeFiles
@ -18,19 +18,19 @@ namespace NzbDrone.Api.EpisodeFiles
private readonly IMediaFileService _mediaFileService; private readonly IMediaFileService _mediaFileService;
private readonly IDeleteMediaFiles _mediaFileDeletionService; private readonly IDeleteMediaFiles _mediaFileDeletionService;
private readonly ISeriesService _seriesService; private readonly ISeriesService _seriesService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; private readonly IUpgradableSpecification _upgradableSpecification;
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster, public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService, IMediaFileService mediaFileService,
IDeleteMediaFiles mediaFileDeletionService, IDeleteMediaFiles mediaFileDeletionService,
ISeriesService seriesService, ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification) IUpgradableSpecification upgradableSpecification)
: base(signalRBroadcaster) : base(signalRBroadcaster)
{ {
_mediaFileService = mediaFileService; _mediaFileService = mediaFileService;
_mediaFileDeletionService = mediaFileDeletionService; _mediaFileDeletionService = mediaFileDeletionService;
_seriesService = seriesService; _seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = upgradableSpecification;
GetResourceById = GetEpisodeFile; GetResourceById = GetEpisodeFile;
GetResourceAll = GetEpisodeFiles; GetResourceAll = GetEpisodeFiles;
UpdateResource = SetQuality; UpdateResource = SetQuality;
@ -42,7 +42,7 @@ namespace NzbDrone.Api.EpisodeFiles
var episodeFile = _mediaFileService.Get(id); var episodeFile = _mediaFileService.Get(id);
var series = _seriesService.GetSeries(episodeFile.SeriesId); var series = _seriesService.GetSeries(episodeFile.SeriesId);
return episodeFile.ToResource(series, _qualityUpgradableSpecification); return episodeFile.ToResource(series, _upgradableSpecification);
} }
private List<EpisodeFileResource> GetEpisodeFiles() private List<EpisodeFileResource> GetEpisodeFiles()
@ -56,13 +56,14 @@ namespace NzbDrone.Api.EpisodeFiles
var series = _seriesService.GetSeries(seriesId); var series = _seriesService.GetSeries(seriesId);
return _mediaFileService.GetFilesBySeries(seriesId).ConvertAll(f => f.ToResource(series, _qualityUpgradableSpecification)); return _mediaFileService.GetFilesBySeries(seriesId).ConvertAll(f => f.ToResource(series, _upgradableSpecification));
} }
private void SetQuality(EpisodeFileResource episodeFileResource) private void SetQuality(EpisodeFileResource episodeFileResource)
{ {
var episodeFile = _mediaFileService.Get(episodeFileResource.Id); var episodeFile = _mediaFileService.Get(episodeFileResource.Id);
episodeFile.Quality = episodeFileResource.Quality; episodeFile.Quality = episodeFileResource.Quality;
episodeFile.Language = episodeFileResource.Language;
_mediaFileService.Update(episodeFile); _mediaFileService.Update(episodeFile);
} }

View File

@ -4,6 +4,7 @@ using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using Sonarr.Http.REST; using Sonarr.Http.REST;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages;
namespace NzbDrone.Api.EpisodeFiles namespace NzbDrone.Api.EpisodeFiles
{ {
@ -17,6 +18,7 @@ namespace NzbDrone.Api.EpisodeFiles
public DateTime DateAdded { get; set; } public DateTime DateAdded { get; set; }
public string SceneName { get; set; } public string SceneName { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public Language Language { get; set; }
public MediaInfoResource MediaInfo { get; set; } public MediaInfoResource MediaInfo { get; set; }
public string OriginalFilePath { get; set; } public string OriginalFilePath { get; set; }
@ -46,7 +48,7 @@ namespace NzbDrone.Api.EpisodeFiles
}; };
} }
public static EpisodeFileResource ToResource(this EpisodeFile model, Core.Tv.Series series, IQualityUpgradableSpecification qualityUpgradableSpecification) public static EpisodeFileResource ToResource(this EpisodeFile model, Core.Tv.Series series, IUpgradableSpecification upgradableSpecification)
{ {
if (model == null) return null; if (model == null) return null;
@ -62,6 +64,7 @@ namespace NzbDrone.Api.EpisodeFiles
DateAdded = model.DateAdded, DateAdded = model.DateAdded,
SceneName = model.SceneName, SceneName = model.SceneName,
Quality = model.Quality, Quality = model.Quality,
Language = model.Language,
QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(series.Profile.Value, model.Quality), QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(series.Profile.Value, model.Quality),
MediaInfo = model.MediaInfo.ToResource(model.SceneName), MediaInfo = model.MediaInfo.ToResource(model.SceneName),
OriginalFilePath = model.OriginalFilePath OriginalFilePath = model.OriginalFilePath

View File

@ -10,9 +10,9 @@ namespace NzbDrone.Api.Episodes
{ {
public EpisodeModule(ISeriesService seriesService, public EpisodeModule(ISeriesService seriesService,
IEpisodeService episodeService, IEpisodeService episodeService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster) IBroadcastSignalRMessage signalRBroadcaster)
: base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster) : base(episodeService, seriesService, upgradableSpecification, signalRBroadcaster)
{ {
GetResourceAll = GetEpisodes; GetResourceAll = GetEpisodes;
UpdateResource = SetMonitored; UpdateResource = SetMonitored;

View File

@ -19,31 +19,31 @@ namespace NzbDrone.Api.Episodes
{ {
protected readonly IEpisodeService _episodeService; protected readonly IEpisodeService _episodeService;
protected readonly ISeriesService _seriesService; protected readonly ISeriesService _seriesService;
protected readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; protected readonly IUpgradableSpecification _upgradableSpecification;
protected EpisodeModuleWithSignalR(IEpisodeService episodeService, protected EpisodeModuleWithSignalR(IEpisodeService episodeService,
ISeriesService seriesService, ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster) IBroadcastSignalRMessage signalRBroadcaster)
: base(signalRBroadcaster) : base(signalRBroadcaster)
{ {
_episodeService = episodeService; _episodeService = episodeService;
_seriesService = seriesService; _seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = upgradableSpecification;
GetResourceById = GetEpisode; GetResourceById = GetEpisode;
} }
protected EpisodeModuleWithSignalR(IEpisodeService episodeService, protected EpisodeModuleWithSignalR(IEpisodeService episodeService,
ISeriesService seriesService, ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster, IBroadcastSignalRMessage signalRBroadcaster,
string resource) string resource)
: base(signalRBroadcaster, resource) : base(signalRBroadcaster, resource)
{ {
_episodeService = episodeService; _episodeService = episodeService;
_seriesService = seriesService; _seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = upgradableSpecification;
GetResourceById = GetEpisode; GetResourceById = GetEpisode;
} }
@ -69,7 +69,7 @@ namespace NzbDrone.Api.Episodes
} }
if (includeEpisodeFile && episode.EpisodeFileId != 0) if (includeEpisodeFile && episode.EpisodeFileId != 0)
{ {
resource.EpisodeFile = episode.EpisodeFile.Value.ToResource(series, _qualityUpgradableSpecification); resource.EpisodeFile = episode.EpisodeFile.Value.ToResource(series, _upgradableSpecification);
} }
} }
@ -97,7 +97,7 @@ namespace NzbDrone.Api.Episodes
} }
if (includeEpisodeFile && episodes[i].EpisodeFileId != 0) if (includeEpisodeFile && episodes[i].EpisodeFileId != 0)
{ {
resource.EpisodeFile = episodes[i].EpisodeFile.Value.ToResource(series, _qualityUpgradableSpecification); resource.EpisodeFile = episodes[i].EpisodeFile.Value.ToResource(series, _upgradableSpecification);
} }
} }
} }

View File

@ -17,15 +17,15 @@ namespace NzbDrone.Api.History
public class HistoryModule : SonarrRestModule<HistoryResource> public class HistoryModule : SonarrRestModule<HistoryResource>
{ {
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; private readonly IUpgradableSpecification _upgradableSpecification;
private readonly IFailedDownloadService _failedDownloadService; private readonly IFailedDownloadService _failedDownloadService;
public HistoryModule(IHistoryService historyService, public HistoryModule(IHistoryService historyService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification upgradableSpecification,
IFailedDownloadService failedDownloadService) IFailedDownloadService failedDownloadService)
{ {
_historyService = historyService; _historyService = historyService;
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = upgradableSpecification;
_failedDownloadService = failedDownloadService; _failedDownloadService = failedDownloadService;
GetResourcePaged = GetHistory; GetResourcePaged = GetHistory;
@ -42,7 +42,7 @@ namespace NzbDrone.Api.History
if (model.Series != null) if (model.Series != null)
{ {
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Series.Profile.Value, model.Quality); resource.QualityCutoffNotMet = _upgradableSpecification.QualityCutoffNotMet(model.Series.Profile.Value, model.Quality);
} }
return resource; return resource;

View File

@ -5,6 +5,7 @@ using Sonarr.Http.REST;
using NzbDrone.Api.Series; using NzbDrone.Api.Series;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages;
namespace NzbDrone.Api.History namespace NzbDrone.Api.History
{ {
@ -17,6 +18,7 @@ namespace NzbDrone.Api.History
public bool QualityCutoffNotMet { get; set; } public bool QualityCutoffNotMet { get; set; }
public DateTime Date { get; set; } public DateTime Date { get; set; }
public string DownloadId { get; set; } public string DownloadId { get; set; }
public Language Language { get; set; }
public HistoryEventType EventType { get; set; } public HistoryEventType EventType { get; set; }

View File

@ -5,6 +5,7 @@ using Sonarr.Http.REST;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using System.Linq; using System.Linq;

View File

@ -112,6 +112,8 @@
<Compile Include="Profiles\Delay\DelayProfileModule.cs" /> <Compile Include="Profiles\Delay\DelayProfileModule.cs" />
<Compile Include="Profiles\Delay\DelayProfileResource.cs" /> <Compile Include="Profiles\Delay\DelayProfileResource.cs" />
<Compile Include="ProviderModuleBase.cs" /> <Compile Include="ProviderModuleBase.cs" />
<Compile Include="Profiles\ProfileModule.cs" />
<Compile Include="Profiles\ProfileResource.cs" />
<Compile Include="Queue\QueueActionModule.cs" /> <Compile Include="Queue\QueueActionModule.cs" />
<Compile Include="RemotePathMappings\RemotePathMappingModule.cs" /> <Compile Include="RemotePathMappings\RemotePathMappingModule.cs" />
<Compile Include="RemotePathMappings\RemotePathMappingResource.cs" /> <Compile Include="RemotePathMappings\RemotePathMappingResource.cs" />
@ -164,8 +166,6 @@
<Compile Include="Profiles\Languages\LanguageModule.cs" /> <Compile Include="Profiles\Languages\LanguageModule.cs" />
<Compile Include="Profiles\Languages\LanguageResource.cs" /> <Compile Include="Profiles\Languages\LanguageResource.cs" />
<Compile Include="Profiles\LegacyProfileModule.cs" /> <Compile Include="Profiles\LegacyProfileModule.cs" />
<Compile Include="Profiles\ProfileModule.cs" />
<Compile Include="Profiles\ProfileResource.cs" />
<Compile Include="Profiles\ProfileSchemaModule.cs" /> <Compile Include="Profiles\ProfileSchemaModule.cs" />
<Compile Include="Profiles\ProfileValidation.cs" /> <Compile Include="Profiles\ProfileValidation.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Parser; using NzbDrone.Core.Languages;
using Sonarr.Http; using Sonarr.Http;
namespace NzbDrone.Api.Profiles.Languages namespace NzbDrone.Api.Profiles.Languages

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Validation;
using Sonarr.Http; using Sonarr.Http;
using Sonarr.Http.Mapping; using Sonarr.Http.Mapping;
@ -17,7 +16,6 @@ namespace NzbDrone.Api.Profiles
SharedValidator.RuleFor(c => c.Name).NotEmpty(); SharedValidator.RuleFor(c => c.Name).NotEmpty();
SharedValidator.RuleFor(c => c.Cutoff).NotNull(); SharedValidator.RuleFor(c => c.Cutoff).NotNull();
SharedValidator.RuleFor(c => c.Items).MustHaveAllowedQuality(); SharedValidator.RuleFor(c => c.Items).MustHaveAllowedQuality();
SharedValidator.RuleFor(c => c.Language).ValidLanguage();
GetResourceAll = GetAll; GetResourceAll = GetAll;
GetResourceById = GetById; GetResourceById = GetById;

View File

@ -3,6 +3,7 @@ using System.Linq;
using Sonarr.Http.REST; using Sonarr.Http.REST;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
namespace NzbDrone.Api.Profiles namespace NzbDrone.Api.Profiles
@ -12,7 +13,6 @@ namespace NzbDrone.Api.Profiles
public string Name { get; set; } public string Name { get; set; }
public Quality Cutoff { get; set; } public Quality Cutoff { get; set; }
public List<ProfileQualityItemResource> Items { get; set; } public List<ProfileQualityItemResource> Items { get; set; }
public Language Language { get; set; }
} }
public class ProfileQualityItemResource : RestResource public class ProfileQualityItemResource : RestResource
@ -33,8 +33,7 @@ namespace NzbDrone.Api.Profiles
Name = model.Name, Name = model.Name,
Cutoff = model.Cutoff, Cutoff = model.Cutoff,
Items = model.Items.ConvertAll(ToResource), Items = model.Items.ConvertAll(ToResource)
Language = model.Language
}; };
} }
@ -59,8 +58,7 @@ namespace NzbDrone.Api.Profiles
Name = resource.Name, Name = resource.Name,
Cutoff = (Quality)resource.Cutoff.Id, Cutoff = (Quality)resource.Cutoff.Id,
Items = resource.Items.ConvertAll(ToModel), Items = resource.Items.ConvertAll(ToModel)
Language = resource.Language
}; };
} }

View File

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Parser; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using Sonarr.Http; using Sonarr.Http;
using Sonarr.Http.Mapping; using Sonarr.Http.Mapping;
@ -30,7 +29,6 @@ namespace NzbDrone.Api.Profiles
var profile = new Profile(); var profile = new Profile();
profile.Cutoff = Quality.Unknown; profile.Cutoff = Quality.Unknown;
profile.Items = items; profile.Items = items;
profile.Language = Language.English;
return new List<ProfileResource> { profile.ToResource() }; return new List<ProfileResource> { profile.ToResource() };
} }

View File

@ -1,20 +1,22 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Nancy; using Nancy;
using NzbDrone.Core.Profiles.Languages;
using Sonarr.Http.Extensions; using Sonarr.Http.Extensions;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using Sonarr.Http.Mapping;
namespace NzbDrone.Api.Series namespace NzbDrone.Api.Series
{ {
public class SeriesEditorModule : NzbDroneApiModule public class SeriesEditorModule : NzbDroneApiModule
{ {
private readonly ISeriesService _seriesService; private readonly ISeriesService _seriesService;
private readonly ILanguageProfileService _languageProfileService;
public SeriesEditorModule(ISeriesService seriesService) public SeriesEditorModule(ISeriesService seriesService, ILanguageProfileService languageProfileService)
: base("/series/editor") : base("/series/editor")
{ {
_seriesService = seriesService; _seriesService = seriesService;
_languageProfileService = languageProfileService;
Put["/"] = series => SaveAll(); Put["/"] = series => SaveAll();
} }
@ -22,9 +24,24 @@ namespace NzbDrone.Api.Series
{ {
var resources = Request.Body.FromJson<List<SeriesResource>>(); var resources = Request.Body.FromJson<List<SeriesResource>>();
var series = resources.Select(seriesResource => seriesResource.ToModel(_seriesService.GetSeries(seriesResource.Id))).ToList(); var seriesToUpdate = resources.Select(seriesResource =>
{
var series = _seriesService.GetSeries(seriesResource.Id);
var updatedSeries = seriesResource.ToModel(series);
return _seriesService.UpdateSeries(series, true) // If the new language profile doens't exist, keep it the same.
// This could happen if a 3rd-party app uses this endpoint to update a
// series and doesn't pass the languageProfileI as well.
if (!_languageProfileService.Exists(updatedSeries.LanguageProfileId))
{
updatedSeries.LanguageProfileId = series.LanguageProfileId;
}
return updatedSeries;
}).ToList();
return _seriesService.UpdateSeries(seriesToUpdate, true)
.ToResource(false) .ToResource(false)
.AsResponse(HttpStatusCode.Accepted); .AsResponse(HttpStatusCode.Accepted);
} }

View File

@ -13,6 +13,7 @@ using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events; using NzbDrone.Core.Tv.Events;
using NzbDrone.Core.Validation.Paths; using NzbDrone.Core.Validation.Paths;
using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using NzbDrone.SignalR; using NzbDrone.SignalR;
using Sonarr.Http; using Sonarr.Http;
@ -35,6 +36,7 @@ namespace NzbDrone.Api.Series
private readonly ISeriesStatisticsService _seriesStatisticsService; private readonly ISeriesStatisticsService _seriesStatisticsService;
private readonly ISceneMappingService _sceneMappingService; private readonly ISceneMappingService _sceneMappingService;
private readonly IMapCoversToLocal _coverMapper; private readonly IMapCoversToLocal _coverMapper;
private readonly ILanguageProfileService _languageProfileService;
public SeriesModule(IBroadcastSignalRMessage signalRBroadcaster, public SeriesModule(IBroadcastSignalRMessage signalRBroadcaster,
ISeriesService seriesService, ISeriesService seriesService,
@ -42,13 +44,15 @@ namespace NzbDrone.Api.Series
ISeriesStatisticsService seriesStatisticsService, ISeriesStatisticsService seriesStatisticsService,
ISceneMappingService sceneMappingService, ISceneMappingService sceneMappingService,
IMapCoversToLocal coverMapper, IMapCoversToLocal coverMapper,
ILanguageProfileService languageProfileService,
RootFolderValidator rootFolderValidator, RootFolderValidator rootFolderValidator,
SeriesPathValidator seriesPathValidator, SeriesPathValidator seriesPathValidator,
SeriesExistsValidator seriesExistsValidator, SeriesExistsValidator seriesExistsValidator,
DroneFactoryValidator droneFactoryValidator, DroneFactoryValidator droneFactoryValidator,
SeriesAncestorValidator seriesAncestorValidator, SeriesAncestorValidator seriesAncestorValidator,
SystemFolderValidator systemFolderValidator, SystemFolderValidator systemFolderValidator,
ProfileExistsValidator profileExistsValidator ProfileExistsValidator profileExistsValidator,
LanguageProfileExistsValidator languageProfileExistsValidator
) )
: base(signalRBroadcaster) : base(signalRBroadcaster)
{ {
@ -58,6 +62,7 @@ namespace NzbDrone.Api.Series
_sceneMappingService = sceneMappingService; _sceneMappingService = sceneMappingService;
_coverMapper = coverMapper; _coverMapper = coverMapper;
_languageProfileService = languageProfileService;
GetResourceAll = AllSeries; GetResourceAll = AllSeries;
GetResourceById = GetSeries; GetResourceById = GetSeries;
@ -66,6 +71,7 @@ namespace NzbDrone.Api.Series
DeleteResource = DeleteSeries; DeleteResource = DeleteSeries;
SharedValidator.RuleFor(s => s.ProfileId).ValidId(); SharedValidator.RuleFor(s => s.ProfileId).ValidId();
SharedValidator.RuleFor(s => s.LanguageProfileId);
SharedValidator.RuleFor(s => s.Path) SharedValidator.RuleFor(s => s.Path)
.Cascade(CascadeMode.StopOnFirstFailure) .Cascade(CascadeMode.StopOnFirstFailure)
@ -82,8 +88,12 @@ namespace NzbDrone.Api.Series
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace()); PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.TvdbId).GreaterThan(0).SetValidator(seriesExistsValidator); PostValidator.RuleFor(s => s.TvdbId).GreaterThan(0).SetValidator(seriesExistsValidator);
PostValidator.RuleFor(s => s.LanguageProfileId).SetValidator(languageProfileExistsValidator).When(s => s.LanguageProfileId != 0);
PutValidator.RuleFor(s => s.Path).IsValidPath(); PutValidator.RuleFor(s => s.Path).IsValidPath();
// Ensure any editing has a valid LanguageProfile
PutValidator.RuleFor(s => s.LanguageProfileId).SetValidator(languageProfileExistsValidator);
} }
private SeriesResource GetSeries(int id) private SeriesResource GetSeries(int id)
@ -111,6 +121,12 @@ namespace NzbDrone.Api.Series
{ {
var model = seriesResource.ToModel(); var model = seriesResource.ToModel();
// Set a default LanguageProfileId to maintain backwards compatibility with apps using the v2 API
if (model.LanguageProfileId == 0 || !_languageProfileService.Exists(model.LanguageProfileId))
{
model.LanguageProfileId = _languageProfileService.All().First().Id;
}
return _addSeriesService.AddSeries(model).Id; return _addSeriesService.AddSeries(model).Id;
} }

View File

@ -52,6 +52,7 @@ namespace NzbDrone.Api.Series
//View & Edit //View & Edit
public string Path { get; set; } public string Path { get; set; }
public int ProfileId { get; set; } public int ProfileId { get; set; }
public int LanguageProfileId { get; set; }
//Editing Only //Editing Only
public bool SeasonFolder { get; set; } public bool SeasonFolder { get; set; }
@ -124,6 +125,7 @@ namespace NzbDrone.Api.Series
Path = model.Path, Path = model.Path,
ProfileId = model.ProfileId, ProfileId = model.ProfileId,
LanguageProfileId = model.LanguageProfileId,
SeasonFolder = model.SeasonFolder, SeasonFolder = model.SeasonFolder,
Monitored = model.Monitored, Monitored = model.Monitored,
@ -178,6 +180,7 @@ namespace NzbDrone.Api.Series
Path = resource.Path, Path = resource.Path,
ProfileId = resource.ProfileId, ProfileId = resource.ProfileId,
LanguageProfileId = resource.LanguageProfileId,
SeasonFolder = resource.SeasonFolder, SeasonFolder = resource.SeasonFolder,
Monitored = resource.Monitored, Monitored = resource.Monitored,

View File

@ -15,9 +15,9 @@ namespace NzbDrone.Api.Wanted
public CutoffModule(IEpisodeCutoffService episodeCutoffService, public CutoffModule(IEpisodeCutoffService episodeCutoffService,
IEpisodeService episodeService, IEpisodeService episodeService,
ISeriesService seriesService, ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster) IBroadcastSignalRMessage signalRBroadcaster)
: base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "wanted/cutoff") : base(episodeService, seriesService, upgradableSpecification, signalRBroadcaster, "wanted/cutoff")
{ {
_episodeCutoffService = episodeCutoffService; _episodeCutoffService = episodeCutoffService;
GetResourcePaged = GetCutoffUnmetEpisodes; GetResourcePaged = GetCutoffUnmetEpisodes;

View File

@ -12,9 +12,9 @@ namespace NzbDrone.Api.Wanted
{ {
public MissingModule(IEpisodeService episodeService, public MissingModule(IEpisodeService episodeService,
ISeriesService seriesService, ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster) IBroadcastSignalRMessage signalRBroadcaster)
: base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "wanted/missing") : base(episodeService, seriesService, upgradableSpecification, signalRBroadcaster, "wanted/missing")
{ {
GetResourcePaged = GetMissingEpisodes; GetResourcePaged = GetMissingEpisodes;
} }

View File

@ -6,6 +6,7 @@ using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Test.Datastore namespace NzbDrone.Core.Test.Datastore
{ {
@ -17,6 +18,7 @@ namespace NzbDrone.Core.Test.Datastore
{ {
var episodeFile = Builder<EpisodeFile>.CreateNew() var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(c => c.Quality = new QualityModel()) .With(c => c.Quality = new QualityModel())
.With(c => c.Language = Language.English)
.BuildNew(); .BuildNew();
Db.Insert(episodeFile); Db.Insert(episodeFile);

View File

@ -1,11 +1,15 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using NUnit.Framework; using NUnit.Framework;
using System.Linq;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Test.Languages;
namespace NzbDrone.Core.Test.Datastore namespace NzbDrone.Core.Test.Datastore
{ {
@ -20,15 +24,23 @@ namespace NzbDrone.Core.Test.Datastore
{ {
Name = "Test", Name = "Test",
Cutoff = Quality.WEBDL720p, Cutoff = Quality.WEBDL720p,
Items = Qualities.QualityFixture.GetDefaultQualities() Items = Qualities.QualityFixture.GetDefaultQualities(),
}; };
var languageProfile = new LanguageProfile
{
Name = "Test",
Languages = LanguageFixture.GetDefaultLanguages(Language.English),
Cutoff = Language.English
};
profile = Db.Insert(profile); profile = Db.Insert(profile);
languageProfile = Db.Insert(languageProfile);
var series = Builder<Series>.CreateListOfSize(1) var series = Builder<Series>.CreateListOfSize(1)
.All() .All()
.With(v => v.ProfileId = profile.Id) .With(v => v.ProfileId = profile.Id)
.With(v => v.LanguageProfileId = languageProfile.Id)
.BuildListOfNew(); .BuildListOfNew();
Db.InsertMany(series); Db.InsertMany(series);
@ -65,6 +77,7 @@ namespace NzbDrone.Core.Test.Datastore
{ {
Assert.IsNotNull(episode.Series); Assert.IsNotNull(episode.Series);
Assert.IsFalse(episode.Series.Profile.IsLoaded); Assert.IsFalse(episode.Series.Profile.IsLoaded);
Assert.IsFalse(episode.Series.LanguageProfile.IsLoaded);
} }
} }
@ -100,8 +113,27 @@ namespace NzbDrone.Core.Test.Datastore
{ {
Assert.IsNotNull(episode.Series); Assert.IsNotNull(episode.Series);
Assert.IsTrue(episode.Series.Profile.IsLoaded); Assert.IsTrue(episode.Series.Profile.IsLoaded);
Assert.IsFalse(episode.Series.LanguageProfile.IsLoaded);
} }
} }
[Test]
public void should_explicit_load_languageprofile_if_joined()
{
var db = Mocker.Resolve<IDatabase>();
var DataMapper = db.GetDataMapper();
var episodes = DataMapper.Query<Episode>()
.Join<Episode, Series>(Marr.Data.QGen.JoinType.Inner, v => v.Series, (l, r) => l.SeriesId == r.Id)
.Join<Series, LanguageProfile>(Marr.Data.QGen.JoinType.Inner, v => v.LanguageProfile, (l, r) => l.ProfileId == r.Id)
.ToList();
foreach (var episode in episodes)
{
Assert.IsNotNull(episode.Series);
Assert.IsFalse(episode.Series.Profile.IsLoaded);
Assert.IsTrue(episode.Series.LanguageProfile.IsLoaded);
}
}
} }
} }

View File

@ -55,6 +55,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{ {
var db = WithMigrationTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new
{
Name = "Profile1",
CutOff = 0,
Items = "[]",
Language = 1
});
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
Tvdbid = 1, Tvdbid = 1,
@ -69,7 +77,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
Runtime= 0, Runtime= 0,
SeriesType=0, SeriesType=0,
UseSceneNumbering =0, UseSceneNumbering =0,
LastInfoSync = "2000-01-01 00:00:00" LastInfoSync = "2000-01-01 00:00:00",
ProfileId = 1
}); });
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
@ -86,7 +95,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
Runtime = 0, Runtime = 0,
SeriesType = 0, SeriesType = 0,
UseSceneNumbering = 0, UseSceneNumbering = 0,
LastInfoSync = "2000-01-01 00:00:00" LastInfoSync = "2000-01-01 00:00:00",
ProfileId = 1
}); });
}); });

View File

@ -14,6 +14,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{ {
var db = WithMigrationTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new
{
Name = "Profile1",
CutOff = 0,
Items = "[]",
Language = 1
});
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
Tvdbid = 1, Tvdbid = 1,
@ -28,7 +36,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
Runtime = 0, Runtime = 0,
SeriesType = 0, SeriesType = 0,
UseSceneNumbering = 0, UseSceneNumbering = 0,
LastInfoSync = "2000-01-01 00:00:00" LastInfoSync = "2000-01-01 00:00:00",
ProfileId = 1
}); });
c.Insert.IntoTable("Tags").Row(new c.Insert.IntoTable("Tags").Row(new
@ -46,6 +55,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{ {
var db = WithMigrationTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new
{
Name = "Profile1",
CutOff = 0,
Items = "[]",
Language = 1
});
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
Tvdbid = 1, Tvdbid = 1,
@ -61,7 +78,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
SeriesType = 0, SeriesType = 0,
UseSceneNumbering = 0, UseSceneNumbering = 0,
LastInfoSync = "2000-01-01 00:00:00", LastInfoSync = "2000-01-01 00:00:00",
Tags = "[]" Tags = "[]",
ProfileId = 1
}); });
c.Insert.IntoTable("Tags").Row(new c.Insert.IntoTable("Tags").Row(new
@ -113,6 +131,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{ {
var db = WithMigrationTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new
{
Name = "Profile1",
CutOff = 0,
Items = "[]",
Language = 1
});
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
Tvdbid = 1, Tvdbid = 1,
@ -128,7 +154,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
SeriesType = 0, SeriesType = 0,
UseSceneNumbering = 0, UseSceneNumbering = 0,
LastInfoSync = "2000-01-01 00:00:00", LastInfoSync = "2000-01-01 00:00:00",
Tags = "[2]" Tags = "[2]",
ProfileId = 1
}); });
c.Insert.IntoTable("Tags").Row(new c.Insert.IntoTable("Tags").Row(new
@ -151,6 +178,14 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{ {
var db = WithMigrationTestDb(c => var db = WithMigrationTestDb(c =>
{ {
c.Insert.IntoTable("Profiles").Row(new
{
Name = "Profile1",
CutOff = 0,
Items = "[]",
Language = 1
});
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
{ {
Tvdbid = 1, Tvdbid = 1,
@ -166,7 +201,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
SeriesType = 0, SeriesType = 0,
UseSceneNumbering = 0, UseSceneNumbering = 0,
LastInfoSync = "2000-01-01 00:00:00", LastInfoSync = "2000-01-01 00:00:00",
Tags = "[2]" Tags = "[2]",
ProfileId = 1
}); });
c.Insert.IntoTable("Series").Row(new c.Insert.IntoTable("Series").Row(new
@ -184,7 +220,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
SeriesType = 0, SeriesType = 0,
UseSceneNumbering = 0, UseSceneNumbering = 0,
LastInfoSync = "2000-01-01 00:00:00", LastInfoSync = "2000-01-01 00:00:00",
Tags = "[]" Tags = "[]",
ProfileId = 1
}); });
c.Insert.IntoTable("Tags").Row(new c.Insert.IntoTable("Tags").Row(new

View File

@ -2,6 +2,7 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration; using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -45,7 +46,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
RelativePath = "Series.Title.S01E01.en.srt", RelativePath = "Series.Title.S01E01.en.srt",
Added = "2016-05-30 20:23:02.3725923", Added = "2016-05-30 20:23:02.3725923",
LastUpdated = "2016-05-30 20:23:02.3725923", LastUpdated = "2016-05-30 20:23:02.3725923",
Language = Language.English, Language = (int)Language.English,
Extension = "en.srt" Extension = "en.srt"
}); });
}); });

View File

@ -23,7 +23,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Mocker.Resolve<QualityUpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_subject = Mocker.Resolve<AnimeVersionUpgradeSpecification>(); _subject = Mocker.Resolve<AnimeVersionUpgradeSpecification>();
_episodeFile = new EpisodeFile _episodeFile = new EpisodeFile

View File

@ -1,50 +1,223 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Test.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
[TestFixture] [TestFixture]
public class CutoffSpecificationFixture : CoreTest<QualityUpgradableSpecification> public class CutoffSpecificationFixture : CoreTest<UpgradableSpecification>
{ {
[Test] [Test]
public void should_return_true_if_current_episode_is_less_than_cutoff() public void should_return_true_if_current_episode_is_less_than_cutoff()
{ {
Subject.CutoffNotMet(new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }, Subject.CutoffNotMet(
new QualityModel(Quality.DVD, new Revision(version: 2))).Should().BeTrue(); new Profile
{
Cutoff = Quality.Bluray1080p,
Items = Qualities.QualityFixture.GetDefaultQualities()
},
new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(Language.English),
Cutoff = Language.English
},
new QualityModel(Quality.DVD, new Revision(version: 2)), Language.English).Should().BeTrue();
} }
[Test] [Test]
public void should_return_false_if_current_episode_is_equal_to_cutoff() public void should_return_false_if_current_episode_is_equal_to_cutoff()
{ {
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, Subject.CutoffNotMet(
new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeFalse(); new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities()
},
new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(Language.English),
Cutoff = Language.English
},
new QualityModel(Quality.HDTV720p, new Revision(version: 2)), Language.English).Should().BeFalse();
} }
[Test] [Test]
public void should_return_false_if_current_episode_is_greater_than_cutoff() public void should_return_false_if_current_episode_is_greater_than_cutoff()
{ {
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, Subject.CutoffNotMet(
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse(); new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities()
},
new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(Language.English),
Cutoff = Language.English
},
new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), Language.English).Should().BeFalse();
} }
[Test] [Test]
public void should_return_true_when_new_episode_is_proper_but_existing_is_not() public void should_return_true_when_new_episode_is_proper_but_existing_is_not()
{ {
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, Subject.CutoffNotMet(
new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities()
},
new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(Language.English),
Cutoff = Language.English
},
new QualityModel(Quality.HDTV720p, new Revision(version: 1)), new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
Language.English,
new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeTrue(); new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeTrue();
} }
[Test] [Test]
public void should_return_false_if_cutoff_is_met_and_quality_is_higher() public void should_return_false_if_cutoff_is_met_and_quality_is_higher()
{ {
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, Subject.CutoffNotMet(
new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities()
},
new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(Language.English),
Cutoff = Language.English
},
new QualityModel(Quality.HDTV720p, new Revision(version: 2)), new QualityModel(Quality.HDTV720p, new Revision(version: 2)),
Language.English,
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse(); new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse();
} }
[Test]
public void should_return_true_if_quality_cutoff_is_met_and_quality_is_higher_but_language_is_not_met()
{
Profile _profile = new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities(),
};
LanguageProfile _langProfile = new LanguageProfile
{
Cutoff = Language.Spanish,
Languages = LanguageFixture.GetDefaultLanguages()
};
Subject.CutoffNotMet(_profile,
_langProfile,
new QualityModel(Quality.HDTV720p, new Revision(version: 2)),
Language.English,
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeTrue();
}
[Test]
public void should_return_false_if_cutoff_is_met_and_quality_is_higher_and_language_is_met()
{
Profile _profile = new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities(),
};
LanguageProfile _langProfile = new LanguageProfile
{
Cutoff = Language.Spanish,
Languages = LanguageFixture.GetDefaultLanguages()
};
Subject.CutoffNotMet(
_profile,
_langProfile,
new QualityModel(Quality.HDTV720p, new Revision(version: 2)),
Language.Spanish,
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse();
}
[Test]
public void should_return_false_if_cutoff_is_met_and_quality_is_higher_and_language_is_higher()
{
Profile _profile = new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities(),
};
LanguageProfile _langProfile = new LanguageProfile
{
Cutoff = Language.Spanish,
Languages = LanguageFixture.GetDefaultLanguages()
};
Subject.CutoffNotMet(
_profile,
_langProfile,
new QualityModel(Quality.HDTV720p, new Revision(version: 2)),
Language.French,
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse();
}
[Test]
public void should_return_true_if_cutoff_is_not_met_and_new_quality_is_higher_and_language_is_higher()
{
Profile _profile = new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities(),
};
LanguageProfile _langProfile = new LanguageProfile
{
Cutoff = Language.Spanish,
Languages = LanguageFixture.GetDefaultLanguages()
};
Subject.CutoffNotMet(
_profile,
_langProfile,
new QualityModel(Quality.SDTV, new Revision(version: 2)),
Language.French,
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeTrue();
}
[Test]
public void should_return_true_if_cutoff_is_not_met_and_language_is_higher()
{
Profile _profile = new Profile
{
Cutoff = Quality.HDTV720p,
Items = Qualities.QualityFixture.GetDefaultQualities(),
};
LanguageProfile _langProfile = new LanguageProfile
{
Cutoff = Language.Spanish,
Languages = LanguageFixture.GetDefaultLanguages()
};
Subject.CutoffNotMet(
_profile,
_langProfile,
new QualityModel(Quality.SDTV, new Revision(version: 2)),
Language.French).Should().BeTrue();
}
} }
} }

View File

@ -2,11 +2,12 @@
using Marr.Data; using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser; using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Test.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
@ -19,6 +20,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
LanguageProfile _profile = new LazyLoaded<LanguageProfile> (new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(Language.English, Language.Spanish),
Cutoff = Language.Spanish
});
_remoteEpisode = new RemoteEpisode _remoteEpisode = new RemoteEpisode
{ {
ParsedEpisodeInfo = new ParsedEpisodeInfo ParsedEpisodeInfo = new ParsedEpisodeInfo
@ -27,10 +34,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
}, },
Series = new Series Series = new Series
{ {
Profile = new LazyLoaded<Profile>(new Profile LanguageProfile = _profile
{
Language = Language.English
})
} }
}; };
} }
@ -40,6 +44,16 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_remoteEpisode.ParsedEpisodeInfo.Language = Language.English; _remoteEpisode.ParsedEpisodeInfo.Language = Language.English;
} }
private void WithSpanishRelease()
{
_remoteEpisode.ParsedEpisodeInfo.Language = Language.Spanish;
}
private void WithFrenchRelease()
{
_remoteEpisode.ParsedEpisodeInfo.Language = Language.French;
}
private void WithGermanRelease() private void WithGermanRelease()
{ {
_remoteEpisode.ParsedEpisodeInfo.Language = Language.German; _remoteEpisode.ParsedEpisodeInfo.Language = Language.German;
@ -60,5 +74,23 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
} }
[Test]
public void should_return_false_if_language_is_french()
{
WithFrenchRelease();
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_true_if_language_is_spanish()
{
WithSpanishRelease();
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
} }
} }

View File

@ -5,7 +5,7 @@ using Moq;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
@ -14,6 +14,9 @@ using FluentAssertions;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Test.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
@ -34,11 +37,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Build(); .Build();
} }
private RemoteEpisode GivenRemoteEpisode(List<Episode> episodes, QualityModel quality, int age = 0, long size = 0, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet) private RemoteEpisode GivenRemoteEpisode(List<Episode> episodes, QualityModel quality, Language language, int age = 0, long size = 0, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet)
{ {
var remoteEpisode = new RemoteEpisode(); var remoteEpisode = new RemoteEpisode();
remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
remoteEpisode.ParsedEpisodeInfo.Quality = quality; remoteEpisode.ParsedEpisodeInfo.Quality = quality;
remoteEpisode.ParsedEpisodeInfo.Language = language;
remoteEpisode.Episodes = new List<Episode>(); remoteEpisode.Episodes = new List<Episode>();
remoteEpisode.Episodes.AddRange(episodes); remoteEpisode.Episodes.AddRange(episodes);
@ -49,7 +53,15 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
remoteEpisode.Release.DownloadProtocol = downloadProtocol; remoteEpisode.Release.DownloadProtocol = downloadProtocol;
remoteEpisode.Series = Builder<Series>.CreateNew() remoteEpisode.Series = Builder<Series>.CreateNew()
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(e => e.Profile = new Profile
{
Items = Qualities.QualityFixture.GetDefaultQualities()
})
.With(l => l.LanguageProfile = new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(),
Cutoff = Language.Spanish
})
.Build(); .Build();
return remoteEpisode; return remoteEpisode;
@ -68,8 +80,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_put_propers_before_non_propers() public void should_put_propers_before_non_propers()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 1))); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 1)), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 2))); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 2)), Language.English);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode1));
@ -82,8 +94,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_put_higher_quality_before_lower() public void should_put_higher_quality_before_lower()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode1));
@ -96,8 +108,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_order_by_lowest_number_of_episodes() public void should_order_by_lowest_number_of_episodes()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(2) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(2) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode1));
@ -110,8 +122,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_order_by_lowest_number_of_episodes_with_multiple_episodes() public void should_order_by_lowest_number_of_episodes_with_multiple_episodes()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(2), GivenEpisode(3) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(2), GivenEpisode(3) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p), Language.English);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode1));
@ -124,10 +136,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_order_by_age_then_largest_rounded_to_200mb() public void should_order_by_age_then_largest_rounded_to_200mb()
{ {
var remoteEpisodeSd = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), size: 100.Megabytes(), age: 1); var remoteEpisodeSd = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), Language.English, size: 100.Megabytes(), age: 1);
var remoteEpisodeHdSmallOld = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 1200.Megabytes(), age: 1000); var remoteEpisodeHdSmallOld = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, size: 1200.Megabytes(), age: 1000);
var remoteEpisodeSmallYoung = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 1250.Megabytes(), age: 10); var remoteEpisodeSmallYoung = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, size: 1250.Megabytes(), age: 10);
var remoteEpisodeHdLargeYoung = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 3000.Megabytes(), age: 1); var remoteEpisodeHdLargeYoung = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, size: 3000.Megabytes(), age: 1);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisodeSd)); decisions.Add(new DownloadDecision(remoteEpisodeSd));
@ -142,8 +154,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_order_by_youngest() public void should_order_by_youngest()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), age: 10); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, age: 10);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), age: 5); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, age: 5);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
@ -157,8 +169,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_not_throw_if_no_episodes_are_found() public void should_not_throw_if_no_episodes_are_found()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 500.Megabytes()); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, size: 500.Megabytes());
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 500.Megabytes()); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, size: 500.Megabytes());
remoteEpisode1.Episodes = new List<Episode>(); remoteEpisode1.Episodes = new List<Episode>();
@ -174,8 +186,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenPreferredDownloadProtocol(DownloadProtocol.Usenet); GivenPreferredDownloadProtocol(DownloadProtocol.Usenet);
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Torrent); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, downloadProtocol: DownloadProtocol.Torrent);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Usenet); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, downloadProtocol: DownloadProtocol.Usenet);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode1));
@ -190,8 +202,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenPreferredDownloadProtocol(DownloadProtocol.Torrent); GivenPreferredDownloadProtocol(DownloadProtocol.Torrent);
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Torrent); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, downloadProtocol: DownloadProtocol.Torrent);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Usenet); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English, downloadProtocol: DownloadProtocol.Usenet);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode1));
@ -204,8 +216,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_season_pack_above_single_episode() public void should_prefer_season_pack_above_single_episode()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
remoteEpisode1.ParsedEpisodeInfo.FullSeason = true; remoteEpisode1.ParsedEpisodeInfo.FullSeason = true;
@ -220,8 +232,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_multiepisode_over_single_episode_for_anime() public void should_prefer_multiepisode_over_single_episode_for_anime()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
remoteEpisode1.Series.SeriesType = SeriesTypes.Anime; remoteEpisode1.Series.SeriesType = SeriesTypes.Anime;
remoteEpisode2.Series.SeriesType = SeriesTypes.Anime; remoteEpisode2.Series.SeriesType = SeriesTypes.Anime;
@ -237,8 +249,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_single_episode_over_multi_episode_for_non_anime() public void should_prefer_single_episode_over_multi_episode_for_non_anime()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode1));
@ -251,8 +263,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_releases_with_more_seeders() public void should_prefer_releases_with_more_seeders()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var torrentInfo1 = new TorrentInfo(); var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.PublishDate = DateTime.Now;
@ -277,8 +289,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_releases_with_more_peers_given_equal_number_of_seeds() public void should_prefer_releases_with_more_peers_given_equal_number_of_seeds()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var torrentInfo1 = new TorrentInfo(); var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.PublishDate = DateTime.Now;
@ -305,8 +317,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_releases_with_more_peers_no_seeds() public void should_prefer_releases_with_more_peers_no_seeds()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var torrentInfo1 = new TorrentInfo(); var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.PublishDate = DateTime.Now;
@ -334,8 +346,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_first_release_if_peers_and_size_are_too_similar() public void should_prefer_first_release_if_peers_and_size_are_too_similar()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var torrentInfo1 = new TorrentInfo(); var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.PublishDate = DateTime.Now;
@ -363,8 +375,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_first_release_if_age_and_size_are_too_similar() public void should_prefer_first_release_if_age_and_size_are_too_similar()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
remoteEpisode1.Release.PublishDate = DateTime.UtcNow.AddDays(-100); remoteEpisode1.Release.PublishDate = DateTime.UtcNow.AddDays(-100);
remoteEpisode1.Release.Size = 200.Megabytes(); remoteEpisode1.Release.Size = 200.Megabytes();
@ -383,8 +395,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_prefer_quality_over_the_number_of_peers() public void should_prefer_quality_over_the_number_of_peers()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.Bluray1080p)); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.Bluray1080p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV)); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), Language.English);
var torrentInfo1 = new TorrentInfo(); var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.PublishDate = DateTime.Now;
@ -408,5 +420,37 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var qualifiedReports = Subject.PrioritizeDecisions(decisions); var qualifiedReports = Subject.PrioritizeDecisions(decisions);
((TorrentInfo)qualifiedReports.First().RemoteEpisode.Release).Should().Be(torrentInfo1); ((TorrentInfo)qualifiedReports.First().RemoteEpisode.Release).Should().Be(torrentInfo1);
} }
[Test]
public void should_order_by_language()
{
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.French);
var remoteEpisode3 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.German);
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1));
decisions.Add(new DownloadDecision(remoteEpisode2));
decisions.Add(new DownloadDecision(remoteEpisode3));
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Language.Should().Be(Language.French);
qualifiedReports.Last().RemoteEpisode.ParsedEpisodeInfo.Language.Should().Be(Language.German);
}
[Test]
public void should_put_higher_quality_before_lower_allways()
{
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), Language.French);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.German);
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1));
decisions.Add(new DownloadDecision(remoteEpisode2));
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Quality.Should().Be(Quality.HDTV720p);
}
} }
} }

View File

@ -4,7 +4,7 @@ using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;

View File

@ -1,16 +1,19 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Test.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
[TestFixture] [TestFixture]
public class QualityUpgradeSpecificationFixture : CoreTest<QualityUpgradableSpecification> public class QualityUpgradeSpecificationFixture : CoreTest<UpgradableSpecification>
{ {
public static object[] IsUpgradeTestCases = public static object[] IsUpgradeTestCases =
{ {
@ -23,6 +26,16 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
new object[] { Quality.WEBDL1080p, 1, Quality.WEBDL1080p, 1, Quality.WEBDL1080p, false } new object[] { Quality.WEBDL1080p, 1, Quality.WEBDL1080p, 1, Quality.WEBDL1080p, false }
}; };
public static object[] IsUpgradeTestCasesLanguages =
{
new object[] { Quality.SDTV, 1, Language.English, Quality.SDTV, 2, Language.English, Quality.SDTV, Language.Spanish, true },
new object[] { Quality.SDTV, 1, Language.English, Quality.SDTV, 1, Language.Spanish, Quality.SDTV, Language.Spanish, true },
new object[] { Quality.WEBDL720p, 1, Language.French, Quality.WEBDL720p, 2, Language.English, Quality.WEBDL720p, Language.Spanish, true },
new object[] { Quality.SDTV, 1, Language.English, Quality.SDTV, 1, Language.English, Quality.SDTV, Language.English, false },
new object[] { Quality.WEBDL720p, 1, Language.English, Quality.HDTV720p, 2, Language.Spanish, Quality.Bluray720p, Language.Spanish, false },
new object[] { Quality.WEBDL720p, 1, Language.Spanish, Quality.HDTV720p, 2, Language.French, Quality.WEBDL720p, Language.Spanish, false }
};
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
@ -41,9 +54,40 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenAutoDownloadPropers(true); GivenAutoDownloadPropers(true);
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() };
Subject.IsUpgradable(profile, new QualityModel(current, new Revision(version: currentVersion)), new QualityModel(newQuality, new Revision(version: newVersion))) var profile = new Profile
{
Items = Qualities.QualityFixture.GetDefaultQualities()
};
var langProfile = new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(),
Cutoff = Language.English
};
Subject.IsUpgradable(profile, langProfile, new QualityModel(current, new Revision(version: currentVersion)), Language.English, new QualityModel(newQuality, new Revision(version: newVersion)), Language.English)
.Should().Be(expected);
}
[Test, TestCaseSource("IsUpgradeTestCasesLanguages")]
public void IsUpgradeTestLanguage(Quality current, int currentVersion, Language currentLanguage, Quality newQuality, int newVersion, Language newLanguage, Quality cutoff, Language languageCutoff, bool expected)
{
GivenAutoDownloadPropers(true);
var profile = new Profile
{
Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = cutoff,
};
var langProfile = new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(),
Cutoff = languageCutoff
};
Subject.IsUpgradable(profile, langProfile, new QualityModel(current, new Revision(version: currentVersion)), currentLanguage, new QualityModel(newQuality, new Revision(version: newVersion)), newLanguage)
.Should().Be(expected); .Should().Be(expected);
} }
@ -52,9 +96,19 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenAutoDownloadPropers(false); GivenAutoDownloadPropers(false);
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }; var profile = new Profile
{
Items = Qualities.QualityFixture.GetDefaultQualities(),
};
Subject.IsUpgradable(profile, new QualityModel(Quality.DVD, new Revision(version: 2)), new QualityModel(Quality.DVD, new Revision(version: 1))) var langProfile = new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(),
Cutoff = Language.English
};
Subject.IsUpgradable(profile, langProfile, new QualityModel(Quality.DVD, new Revision(version: 2)), Language.English, new QualityModel(Quality.DVD, new Revision(version: 1)), Language.English)
.Should().BeFalse(); .Should().BeFalse();
} }
} }

View File

@ -5,12 +5,14 @@ using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Queue; using NzbDrone.Core.Queue;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
@ -27,10 +29,18 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Mocker.Resolve<QualityUpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_series = Builder<Series>.CreateNew() _series = Builder<Series>.CreateNew()
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(e => e.Profile = new Profile
{
Items = Qualities.QualityFixture.GetDefaultQualities(),
})
.With(l => l.LanguageProfile = new LanguageProfile
{
Languages = Languages.LanguageFixture.GetDefaultLanguages(),
Cutoff = Language.Spanish
})
.Build(); .Build();
_episode = Builder<Episode>.CreateNew() _episode = Builder<Episode>.CreateNew()
@ -51,7 +61,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_remoteEpisode = Builder<RemoteEpisode>.CreateNew() _remoteEpisode = Builder<RemoteEpisode>.CreateNew()
.With(r => r.Series = _series) .With(r => r.Series = _series)
.With(r => r.Episodes = new List<Episode> { _episode }) .With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD) }) .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD) , Language = Language.Spanish})
.Build(); .Build();
} }
@ -97,13 +107,35 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_return_true_when_quality_in_queue_is_lower() public void should_return_true_when_quality_in_queue_is_lower()
{ {
_series.Profile.Value.Cutoff = Quality.Bluray1080p; _series.Profile.Value.Cutoff = Quality.Bluray1080p;
_series.LanguageProfile.Value.Cutoff = Language.Spanish;
var remoteEpisode = Builder<RemoteEpisode>.CreateNew() var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
.With(r => r.Series = _series) .With(r => r.Series = _series)
.With(r => r.Episodes = new List<Episode> { _episode }) .With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{ {
Quality = new QualityModel(Quality.SDTV) Quality = new QualityModel(Quality.SDTV),
Language = Language.Spanish
})
.Build();
GivenQueue(new List<RemoteEpisode> { remoteEpisode });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_quality_in_queue_is_lower_but_language_is_higher()
{
_series.Profile.Value.Cutoff = Quality.Bluray1080p;
_series.LanguageProfile.Value.Cutoff = Language.Spanish;
var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
.With(r => r.Series = _series)
.With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{
Quality = new QualityModel(Quality.SDTV),
Language = Language.English
}) })
.Build(); .Build();
@ -128,14 +160,15 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
} }
[Test] [Test]
public void should_return_false_when_qualities_are_the_same() public void should_return_false_when_qualities_are_the_same_and_languages_are_the_same()
{ {
var remoteEpisode = Builder<RemoteEpisode>.CreateNew() var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
.With(r => r.Series = _series) .With(r => r.Series = _series)
.With(r => r.Episodes = new List<Episode> { _episode }) .With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{ {
Quality = new QualityModel(Quality.DVD) Quality = new QualityModel(Quality.DVD),
Language = Language.Spanish,
}) })
.Build(); .Build();
@ -143,6 +176,23 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
} }
[Test]
public void should_return_true_when_qualities_are_the_same_but_language_is_better()
{
var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
.With(r => r.Series = _series)
.With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{
Quality = new QualityModel(Quality.DVD),
Language = Language.English,
})
.Build();
GivenQueue(new List<RemoteEpisode> { remoteEpisode });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test] [Test]
public void should_return_false_when_quality_in_queue_is_better() public void should_return_false_when_quality_in_queue_is_better()
{ {
@ -153,7 +203,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(r => r.Episodes = new List<Episode> { _episode }) .With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{ {
Quality = new QualityModel(Quality.HDTV720p) Quality = new QualityModel(Quality.HDTV720p),
Language = Language.English
}) })
.Build(); .Build();
@ -169,7 +220,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(r => r.Episodes = new List<Episode> { _episode, _otherEpisode }) .With(r => r.Episodes = new List<Episode> { _episode, _otherEpisode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{ {
Quality = new QualityModel(Quality.HDTV720p) Quality = new QualityModel(Quality.HDTV720p),
Language = Language.English
}) })
.Build(); .Build();
@ -185,7 +237,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(r => r.Episodes = new List<Episode> { _episode }) .With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{ {
Quality = new QualityModel(Quality.HDTV720p) Quality = new QualityModel(Quality.HDTV720p),
Language = Language.English
}) })
.Build(); .Build();
@ -203,7 +256,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(r => r.Episodes = new List<Episode> { _episode, _otherEpisode }) .With(r => r.Episodes = new List<Episode> { _episode, _otherEpisode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{ {
Quality = new QualityModel(Quality.HDTV720p) Quality = new QualityModel(Quality.HDTV720p),
Language = Language.English
}) })
.Build(); .Build();
@ -223,7 +277,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Quality = Quality =
new QualityModel( new QualityModel(
Quality.HDTV720p) Quality.HDTV720p),
Language = Language.English
}) })
.TheFirst(1) .TheFirst(1)
.With(r => r.Episodes = new List<Episode> { _episode }) .With(r => r.Episodes = new List<Episode> { _episode })
@ -237,7 +292,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
} }
[Test] [Test]
public void should_return_false_if_quality_in_queue_meets_cutoff() public void should_return_false_if_quality_and_language_in_queue_meets_cutoff()
{ {
_series.Profile.Value.Cutoff = _remoteEpisode.ParsedEpisodeInfo.Quality.Quality; _series.Profile.Value.Cutoff = _remoteEpisode.ParsedEpisodeInfo.Quality.Quality;
@ -246,7 +301,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(r => r.Episodes = new List<Episode> { _episode }) .With(r => r.Episodes = new List<Episode> { _episode })
.With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo
{ {
Quality = new QualityModel(Quality.HDTV720p) Quality = new QualityModel(Quality.HDTV720p),
Language = Language.Spanish
}) })
.Build(); .Build();

View File

@ -13,11 +13,13 @@ using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
{ {
@ -25,6 +27,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
public class DelaySpecificationFixture : CoreTest<DelaySpecification> public class DelaySpecificationFixture : CoreTest<DelaySpecification>
{ {
private Profile _profile; private Profile _profile;
private LanguageProfile _langProfile;
private DelayProfile _delayProfile; private DelayProfile _delayProfile;
private RemoteEpisode _remoteEpisode; private RemoteEpisode _remoteEpisode;
@ -34,12 +37,16 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_profile = Builder<Profile>.CreateNew() _profile = Builder<Profile>.CreateNew()
.Build(); .Build();
_langProfile = Builder<LanguageProfile>.CreateNew()
.Build();
_delayProfile = Builder<DelayProfile>.CreateNew() _delayProfile = Builder<DelayProfile>.CreateNew()
.With(d => d.PreferredProtocol = DownloadProtocol.Usenet) .With(d => d.PreferredProtocol = DownloadProtocol.Usenet)
.Build(); .Build();
var series = Builder<Series>.CreateNew() var series = Builder<Series>.CreateNew()
.With(s => s.Profile = _profile) .With(s => s.Profile = _profile)
.With(s => s.LanguageProfile = _langProfile)
.Build(); .Build();
_remoteEpisode = Builder<RemoteEpisode>.CreateNew() _remoteEpisode = Builder<RemoteEpisode>.CreateNew()
@ -53,6 +60,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_profile.Cutoff = Quality.WEBDL720p; _profile.Cutoff = Quality.WEBDL720p;
_langProfile.Cutoff = Language.Spanish;
_langProfile.Languages = Languages.LanguageFixture.GetDefaultLanguages();
_remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); _remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
_remoteEpisode.Release = new ReleaseInfo(); _remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Release.DownloadProtocol = DownloadProtocol.Usenet; _remoteEpisode.Release.DownloadProtocol = DownloadProtocol.Usenet;
@ -69,20 +79,21 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
.Returns(new List<RemoteEpisode>()); .Returns(new List<RemoteEpisode>());
} }
private void GivenExistingFile(QualityModel quality) private void GivenExistingFile(QualityModel quality, Language language)
{ {
_remoteEpisode.Episodes.First().EpisodeFileId = 1; _remoteEpisode.Episodes.First().EpisodeFileId = 1;
_remoteEpisode.Episodes.First().EpisodeFile = new LazyLoaded<EpisodeFile>(new EpisodeFile _remoteEpisode.Episodes.First().EpisodeFile = new LazyLoaded<EpisodeFile>(new EpisodeFile
{ {
Quality = quality Quality = quality,
Language = language
}); });
} }
private void GivenUpgradeForExistingFile() private void GivenUpgradeForExistingFile()
{ {
Mocker.GetMock<IQualityUpgradableSpecification>() Mocker.GetMock<IUpgradableSpecification>()
.Setup(s => s.IsUpgradable(It.IsAny<Profile>(), It.IsAny<QualityModel>(), It.IsAny<QualityModel>())) .Setup(s => s.IsUpgradable(It.IsAny<Profile>(), It.IsAny<LanguageProfile>(), It.IsAny<QualityModel>(), It.IsAny<Language>(), It.IsAny<QualityModel>(), It.IsAny<Language>()))
.Returns(true); .Returns(true);
} }
@ -112,9 +123,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
} }
[Test] [Test]
public void should_be_true_when_quality_is_last_allowed_in_profile() public void should_be_true_when_quality_and_language_is_last_allowed_in_profile()
{ {
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray720p); _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray720p);
_remoteEpisode.ParsedEpisodeInfo.Language = Language.French;
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
} }
@ -147,10 +159,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2));
_remoteEpisode.Release.PublishDate = DateTime.UtcNow; _remoteEpisode.Release.PublishDate = DateTime.UtcNow;
GivenExistingFile(new QualityModel(Quality.HDTV720p)); GivenExistingFile(new QualityModel(Quality.HDTV720p), Language.English);
GivenUpgradeForExistingFile(); GivenUpgradeForExistingFile();
Mocker.GetMock<IQualityUpgradableSpecification>() Mocker.GetMock<IUpgradableSpecification>()
.Setup(s => s.IsRevisionUpgrade(It.IsAny<QualityModel>(), It.IsAny<QualityModel>())) .Setup(s => s.IsRevisionUpgrade(It.IsAny<QualityModel>(), It.IsAny<QualityModel>()))
.Returns(true); .Returns(true);
@ -165,10 +177,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(real: 1)); _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(real: 1));
_remoteEpisode.Release.PublishDate = DateTime.UtcNow; _remoteEpisode.Release.PublishDate = DateTime.UtcNow;
GivenExistingFile(new QualityModel(Quality.HDTV720p)); GivenExistingFile(new QualityModel(Quality.HDTV720p), Language.English);
GivenUpgradeForExistingFile(); GivenUpgradeForExistingFile();
Mocker.GetMock<IQualityUpgradableSpecification>() Mocker.GetMock<IUpgradableSpecification>()
.Setup(s => s.IsRevisionUpgrade(It.IsAny<QualityModel>(), It.IsAny<QualityModel>())) .Setup(s => s.IsRevisionUpgrade(It.IsAny<QualityModel>(), It.IsAny<QualityModel>()))
.Returns(true); .Returns(true);
@ -183,7 +195,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2));
_remoteEpisode.Release.PublishDate = DateTime.UtcNow; _remoteEpisode.Release.PublishDate = DateTime.UtcNow;
GivenExistingFile(new QualityModel(Quality.SDTV)); GivenExistingFile(new QualityModel(Quality.SDTV), Language.English);
_delayProfile.UsenetDelay = 720; _delayProfile.UsenetDelay = 720;

View File

@ -18,6 +18,7 @@ using NzbDrone.Common.Disk;
using Moq; using Moq;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using System.IO; using System.IO;
using NzbDrone.Core.Profiles.Qualities;
namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
{ {

View File

@ -9,12 +9,14 @@ using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Test.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
{ {
@ -25,8 +27,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
private RemoteEpisode _parseResultMulti; private RemoteEpisode _parseResultMulti;
private RemoteEpisode _parseResultSingle; private RemoteEpisode _parseResultSingle;
private QualityModel _upgradableQuality; private Tuple<QualityModel, Language> _upgradableQuality;
private QualityModel _notupgradableQuality; private Tuple<QualityModel, Language> _notupgradableQuality;
private Series _fakeSeries; private Series _fakeSeries;
private const int FIRST_EPISODE_ID = 1; private const int FIRST_EPISODE_ID = 1;
private const int SECOND_EPISODE_ID = 2; private const int SECOND_EPISODE_ID = 2;
@ -34,7 +36,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Mocker.Resolve<QualityUpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_upgradeHistory = Mocker.Resolve<HistorySpecification>(); _upgradeHistory = Mocker.Resolve<HistorySpecification>();
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 } };
@ -46,34 +48,36 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_fakeSeries = Builder<Series>.CreateNew() _fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = LanguageFixture.GetDefaultLanguages() })
.Build(); .Build();
_parseResultMulti = new RemoteEpisode _parseResultMulti = new RemoteEpisode
{ {
Series = _fakeSeries, Series = _fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)), Language = Language.English },
Episodes = doubleEpisodeList Episodes = doubleEpisodeList
}; };
_parseResultSingle = new RemoteEpisode _parseResultSingle = new RemoteEpisode
{ {
Series = _fakeSeries, Series = _fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)), Language = Language.English },
Episodes = singleEpisodeList Episodes = singleEpisodeList
}; };
_upgradableQuality = new QualityModel(Quality.SDTV, new Revision(version: 1)); _upgradableQuality = new Tuple<QualityModel, Language> (new QualityModel(Quality.SDTV, new Revision(version: 1)), Language.English);
_notupgradableQuality = new QualityModel(Quality.HDTV1080p, new Revision(version: 2));
_notupgradableQuality = new Tuple<QualityModel, Language> (new QualityModel(Quality.HDTV1080p, new Revision(version: 2)), Language.English);
Mocker.GetMock<IConfigService>() Mocker.GetMock<IConfigService>()
.SetupGet(s => s.EnableCompletedDownloadHandling) .SetupGet(s => s.EnableCompletedDownloadHandling)
.Returns(true); .Returns(true);
} }
private void GivenMostRecentForEpisode(int episodeId, string downloadId, QualityModel quality, DateTime date, HistoryEventType eventType) private void GivenMostRecentForEpisode(int episodeId, string downloadId, Tuple<QualityModel, Language> quality, DateTime date, HistoryEventType eventType)
{ {
Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForEpisode(episodeId)) Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForEpisode(episodeId))
.Returns(new History.History { DownloadId = downloadId, Quality = quality, Date = date, EventType = eventType }); .Returns(new History.History { DownloadId = downloadId, Quality = quality.Item1, Date = date, EventType = eventType, Language = quality.Item2 });
} }
private void GivenCdhDisabled() private void GivenCdhDisabled()
@ -161,19 +165,32 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
{ {
_fakeSeries.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; _fakeSeries.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.English);
GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
} }
[Test]
public void should_be_upgradable_if_episode_is_of_same_quality_as_existing_but_new_has_better_language()
{
_fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
_parseResultSingle.ParsedEpisodeInfo.Language = Language.Spanish;
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.English);
GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue();
}
[Test] [Test]
public void should_not_be_upgradable_if_cutoff_already_met() public void should_not_be_upgradable_if_cutoff_already_met()
{ {
_fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; _fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
_upgradableQuality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)); _upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.Spanish);
GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed);
@ -201,7 +218,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
GivenCdhDisabled(); GivenCdhDisabled();
_fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; _fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1));
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.Spanish);
GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed); GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed);

View File

@ -8,7 +8,7 @@ using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
@ -29,7 +29,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Mocker.Resolve<QualityUpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now }; _firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now };
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now }; _secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now };

View File

@ -6,12 +6,13 @@ using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
@ -29,30 +30,33 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Mocker.Resolve<QualityUpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>(); _upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now }; _firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now, Language = Language.English };
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now }; _secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now, Language = Language.English };
var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
var languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English, Language.Spanish);
var fakeSeries = Builder<Series>.CreateNew() var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities()}) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities()})
.With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = languages })
.Build(); .Build();
_parseResultMulti = new RemoteEpisode _parseResultMulti = new RemoteEpisode
{ {
Series = fakeSeries, Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)), Language = Language.English },
Episodes = doubleEpisodeList Episodes = doubleEpisodeList
}; };
_parseResultSingle = new RemoteEpisode _parseResultSingle = new RemoteEpisode
{ {
Series = fakeSeries, Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)), Language = Language.English },
Episodes = singleEpisodeList Episodes = singleEpisodeList
}; };
} }

View File

@ -11,7 +11,7 @@ using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Exceptions; using NzbDrone.Core.Exceptions;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;

View File

@ -10,7 +10,7 @@ using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;

View File

@ -10,7 +10,7 @@ using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;

View File

@ -11,7 +11,7 @@ using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;

View File

@ -7,15 +7,15 @@ using NUnit.Framework;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using System.Collections.Generic;
using NzbDrone.Core.Test.Qualities; using NzbDrone.Core.Test.Qualities;
using FluentAssertions;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.HistoryTests namespace NzbDrone.Core.Test.HistoryTests
{ {
@ -23,48 +23,31 @@ namespace NzbDrone.Core.Test.HistoryTests
{ {
private Profile _profile; private Profile _profile;
private Profile _profileCustom; private Profile _profileCustom;
private LanguageProfile _languageProfile;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_profile = new Profile { Cutoff = Quality.WEBDL720p, Items = QualityFixture.GetDefaultQualities() }; _profile = new Profile
_profileCustom = new Profile { Cutoff = Quality.WEBDL720p, Items = QualityFixture.GetDefaultQualities(Quality.DVD) };
}
[Test]
public void should_return_null_if_no_history()
{ {
Mocker.GetMock<IHistoryRepository>() Cutoff = Quality.WEBDL720p,
.Setup(v => v.GetBestQualityInHistory(2)) Items = QualityFixture.GetDefaultQualities(),
.Returns(new List<QualityModel>()); };
var quality = Subject.GetBestQualityInHistory(_profile, 2); _profileCustom = new Profile
quality.Should().BeNull();
}
[Test]
public void should_return_best_quality()
{ {
Mocker.GetMock<IHistoryRepository>() Cutoff = Quality.WEBDL720p,
.Setup(v => v.GetBestQualityInHistory(2)) Items = QualityFixture.GetDefaultQualities(Quality.DVD),
.Returns(new List<QualityModel> { new QualityModel(Quality.DVD), new QualityModel(Quality.Bluray1080p) });
var quality = Subject.GetBestQualityInHistory(_profile, 2); };
quality.Should().Be(new QualityModel(Quality.Bluray1080p));
}
[Test] _languageProfile = new LanguageProfile
public void should_return_best_quality_with_custom_order()
{ {
Mocker.GetMock<IHistoryRepository>() Cutoff = Language.Spanish,
.Setup(v => v.GetBestQualityInHistory(2)) Languages = Languages.LanguageFixture.GetDefaultLanguages()
.Returns(new List<QualityModel> { new QualityModel(Quality.DVD), new QualityModel(Quality.Bluray1080p) }); };
var quality = Subject.GetBestQualityInHistory(_profileCustom, 2);
quality.Should().Be(new QualityModel(Quality.DVD));
} }
[Test] [Test]

View File

@ -0,0 +1,100 @@
using System.Linq;
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Test.Languages
{
[TestFixture]
public class LanguageFixture : CoreTest
{
public static object[] FromIntCases =
{
new object[] {1, Language.English},
new object[] {2, Language.French},
new object[] {3, Language.Spanish},
new object[] {4, Language.German},
new object[] {5, Language.Italian},
new object[] {6, Language.Danish},
new object[] {7, Language.Dutch},
new object[] {8, Language.Japanese},
new object[] {9, Language.Cantonese},
new object[] {10, Language.Mandarin},
new object[] {11, Language.Russian},
new object[] {12, Language.Polish},
new object[] {13, Language.Vietnamese},
new object[] {14, Language.Swedish},
new object[] {15, Language.Norwegian},
new object[] {16, Language.Finnish},
new object[] {17, Language.Turkish},
new object[] {18, Language.Portuguese},
new object[] {19, Language.Flemish},
new object[] {20, Language.Greek},
new object[] {21, Language.Korean},
new object[] {22, Language.Hungarian}
};
public static object[] ToIntCases =
{
new object[] {Language.English, 1},
new object[] {Language.French, 2},
new object[] {Language.Spanish, 3},
new object[] {Language.German, 4},
new object[] {Language.Italian, 5},
new object[] {Language.Danish, 6},
new object[] {Language.Dutch, 7},
new object[] {Language.Japanese, 8},
new object[] {Language.Cantonese, 9},
new object[] {Language.Mandarin, 10},
new object[] {Language.Russian, 11},
new object[] {Language.Polish, 12},
new object[] {Language.Vietnamese, 13},
new object[] {Language.Swedish, 14},
new object[] {Language.Norwegian, 15},
new object[] {Language.Finnish, 16},
new object[] {Language.Turkish, 17},
new object[] {Language.Portuguese, 18},
new object[] {Language.Flemish, 19},
new object[] {Language.Greek, 20},
new object[] {Language.Korean, 21},
new object[] {Language.Hungarian, 22}
};
[Test, TestCaseSource("FromIntCases")]
public void should_be_able_to_convert_int_to_languageTypes(int source, Language expected)
{
var language = (Language)source;
language.Should().Be(expected);
}
[Test, TestCaseSource("ToIntCases")]
public void should_be_able_to_convert_languageTypes_to_int(Language source, int expected)
{
var i = (int)source;
i.Should().Be(expected);
}
public static List<LanguageProfileItem> GetDefaultLanguages(params Language[] allowed)
{
var languages = new List<Language>
{
Language.English,
Language.Spanish,
Language.French
};
if (allowed.Length == 0)
allowed = languages.ToArray();
var items = languages
.Except(allowed)
.Concat(allowed)
.Select(v => new LanguageProfileItem { Language = v, Allowed = allowed.Contains(v) }).ToList();
return items;
}
}
}

View File

@ -0,0 +1,32 @@
using FluentAssertions;
using System.Linq;
using NUnit.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.Languages
{
[TestFixture]
public class LanguageProfileRepositoryFixture : DbTest<LanguageProfileRepository, LanguageProfile>
{
[Test]
public void should_be_able_to_read_and_write()
{
var profile = new LanguageProfile
{
Languages = Language.All.OrderByDescending(l => l.Name).Select(l => new LanguageProfileItem {Language = l, Allowed = l == Language.English}).ToList(),
Name = "TestProfile",
Cutoff = Language.English
};
Subject.Insert(profile);
StoredModel.Name.Should().Be(profile.Name);
StoredModel.Cutoff.Should().Be(profile.Cutoff);
StoredModel.Languages.Should().Equal(profile.Languages, (a, b) => a.Language == b.Language && a.Allowed == b.Allowed);
}
}
}

View File

@ -0,0 +1,75 @@
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.Languages
{
[TestFixture]
public class LanguageProfileServiceFixture : CoreTest<LanguageProfileService>
{
[Test]
public void init_should_add_default_profiles()
{
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<ILanguageProfileRepository>()
.Verify(v => v.Insert(It.IsAny<LanguageProfile>()), Times.Once());
}
[Test]
//This confirms that new profiles are added only if no other profiles exists.
//We don't want to keep adding them back if a user deleted them on purpose.
public void Init_should_skip_if_any_profiles_already_exist()
{
Mocker.GetMock<ILanguageProfileRepository>()
.Setup(s => s.All())
.Returns(Builder<LanguageProfile>.CreateListOfSize(2).Build().ToList());
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<ILanguageProfileRepository>()
.Verify(v => v.Insert(It.IsAny<LanguageProfile>()), Times.Never());
}
[Test]
public void should_not_be_able_to_delete_profile_if_assigned_to_series()
{
var seriesList = Builder<Series>.CreateListOfSize(3)
.Random(1)
.With(c => c.LanguageProfileId = 2)
.Build().ToList();
Mocker.GetMock<ISeriesService>().Setup(c => c.GetAllSeries()).Returns(seriesList);
Assert.Throws<LanguageProfileInUseException>(() => Subject.Delete(2));
Mocker.GetMock<ILanguageProfileRepository>().Verify(c => c.Delete(It.IsAny<int>()), Times.Never());
}
[Test]
public void should_delete_profile_if_not_assigned_to_series()
{
var seriesList = Builder<Series>.CreateListOfSize(3)
.All()
.With(c => c.LanguageProfileId = 2)
.Build().ToList();
Mocker.GetMock<ISeriesService>().Setup(c => c.GetAllSeries()).Returns(seriesList);
Subject.Delete(1);
Mocker.GetMock<ILanguageProfileRepository>().Verify(c => c.Delete(1), Times.Once());
}
}
}

View File

@ -0,0 +1,72 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Aggregation.Aggregators
{
[TestFixture]
public class AggregateLanguageFixture : CoreTest<AggregateLanguage>
{
private LocalEpisode _localEpisode;
[SetUp]
public void Setup()
{
_localEpisode = Builder<LocalEpisode>.CreateNew()
.With(l => l.DownloadClientEpisodeInfo = null)
.With(l => l.FolderEpisodeInfo = null)
.With(l => l.FileEpisodeInfo = null)
.Build();
}
private ParsedEpisodeInfo GetParsedEpisodeInfo(Language language)
{
return new ParsedEpisodeInfo
{
Language = language
};
}
[Test]
public void should_return_file_language_when_only_file_info_is_known()
{
_localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.English);
Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.FileEpisodeInfo.Language);
}
[Test]
public void should_return_folder_language_when_folder_info_is_known()
{
_localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(Language.English);
_localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.English);
Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.FolderEpisodeInfo.Language);
}
[Test]
public void should_return_download_client_item_language_when_download_client_item_info_is_known()
{
_localEpisode.DownloadClientEpisodeInfo = GetParsedEpisodeInfo(Language.English);
_localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(Language.English);
_localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.English);
Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.DownloadClientEpisodeInfo.Language);
}
[Test]
public void should_return_file_language_when_file_language_is_higher_than_others()
{
_localEpisode.DownloadClientEpisodeInfo = GetParsedEpisodeInfo(Language.English);
_localEpisode.FolderEpisodeInfo = GetParsedEpisodeInfo(Language.English);
_localEpisode.FileEpisodeInfo = GetParsedEpisodeInfo(Language.French);
Subject.Aggregate(_localEpisode, false).Language.Should().Be(_localEpisode.FileEpisodeInfo.Language);
}
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -7,14 +7,16 @@ using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{ {
@ -56,6 +58,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_series = Builder<Series>.CreateNew() _series = Builder<Series>.CreateNew()
.With(e => e.Path = @"C:\Test\Series".AsOsAgnostic()) .With(e => e.Path = @"C:\Test\Series".AsOsAgnostic())
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(e => e.LanguageProfile = new LanguageProfile { Languages = Languages.LanguageFixture.GetDefaultLanguages() })
.Build(); .Build();
_quality = new QualityModel(Quality.DVD); _quality = new QualityModel(Quality.DVD);
@ -64,11 +67,12 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{ {
Series = _series, Series = _series,
Quality = _quality, Quality = _quality,
Language = Language.Spanish,
Episodes = new List<Episode> { new Episode() }, Episodes = new List<Episode> { new Episode() },
Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.Spanish.XviD-OSiTV.avi"
}; };
GivenVideoFiles(new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi".AsOsAgnostic() }); GivenVideoFiles(new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.Spanish.XviD-OSiTV.avi".AsOsAgnostic() });
} }
private void GivenSpecifications(params Mock<IImportDecisionEngineSpecification>[] mocks) private void GivenSpecifications(params Mock<IImportDecisionEngineSpecification>[] mocks)
@ -179,7 +183,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
ExceptionVerification.ExpectedErrors(3); ExceptionVerification.ExpectedErrors(3);
} }
[Test]
public void should_not_throw_if_episodes_are_not_found() public void should_not_throw_if_episodes_are_not_found()
{ {
GivenSpecifications(_pass1); GivenSpecifications(_pass1);

View File

@ -6,10 +6,12 @@ using NUnit.Framework;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{ {
@ -24,13 +26,22 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{ {
_series = Builder<Series>.CreateNew() _series = Builder<Series>.CreateNew()
.With(s => s.SeriesType = SeriesTypes.Standard) .With(s => s.SeriesType = SeriesTypes.Standard)
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(e => e.Profile = new Profile
{
Items = Qualities.QualityFixture.GetDefaultQualities(),
})
.With(l => l.LanguageProfile = new LanguageProfile
{
Languages = Languages.LanguageFixture.GetDefaultLanguages(),
Cutoff = Language.Spanish,
})
.Build(); .Build();
_localEpisode = new LocalEpisode _localEpisode = new LocalEpisode
{ {
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 1)), Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
Language = Language.Spanish,
Series = _series Series = _series
}; };
} }
@ -70,7 +81,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>( .With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile new EpisodeFile
{ {
Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)) Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)),
Language = Language.Spanish
})) }))
.Build() .Build()
.ToList(); .ToList();
@ -78,6 +90,43 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
} }
[Test]
public void should_return_true_if_language_upgrade_for_existing_episodeFile_and_quality_is_same()
{
_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.HDTV720p, new Revision(version: 1)),
Language = Language.English
}))
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_if_language_upgrade_for_existing_episodeFile_and_quality_is_worse()
{
_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, new Revision(version: 1)),
Language = Language.English
}))
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test] [Test]
public void should_return_true_if_upgrade_for_existing_episodeFile_for_multi_episodes() public void should_return_true_if_upgrade_for_existing_episodeFile_for_multi_episodes()
{ {
@ -95,6 +144,43 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
} }
[Test]
public void should_return_true_if_language_upgrade_for_existing_episodeFile_for_multi_episodes_and_quality_is_same()
{
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(2)
.All()
.With(e => e.EpisodeFileId = 1)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
Language = Language.English
}))
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_if_language_upgrade_for_existing_episodeFile_for_multi_episodes_and_quality_is_worse()
{
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(2)
.All()
.With(e => e.EpisodeFileId = 1)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)),
Language = Language.English
}))
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test] [Test]
public void should_return_false_if_not_an_upgrade_for_existing_episodeFile() public void should_return_false_if_not_an_upgrade_for_existing_episodeFile()
{ {

View File

@ -13,11 +13,13 @@ using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.MediaFiles namespace NzbDrone.Core.Test.MediaFiles
{ {
@ -39,6 +41,11 @@ namespace NzbDrone.Core.Test.MediaFiles
var series = Builder<Series>.CreateNew() var series = Builder<Series>.CreateNew()
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(l => l.LanguageProfile = new LanguageProfile
{
Cutoff = Language.Spanish,
Languages = Languages.LanguageFixture.GetDefaultLanguages()
})
.With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic()) .With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic())
.Build(); .Build();

View File

@ -301,6 +301,7 @@
<Compile Include="Instrumentation\DatabaseTargetFixture.cs" /> <Compile Include="Instrumentation\DatabaseTargetFixture.cs" />
<Compile Include="JobTests\JobRepositoryFixture.cs" /> <Compile Include="JobTests\JobRepositoryFixture.cs" />
<Compile Include="JobTests\TestJobs.cs" /> <Compile Include="JobTests\TestJobs.cs" />
<Compile Include="Languages\LanguageFixture.cs" />
<Compile Include="MediaCoverTests\CoverExistsSpecificationFixture.cs" /> <Compile Include="MediaCoverTests\CoverExistsSpecificationFixture.cs" />
<Compile Include="MediaCoverTests\ImageResizerFixture.cs" /> <Compile Include="MediaCoverTests\ImageResizerFixture.cs" />
<Compile Include="MediaCoverTests\MediaCoverServiceFixture.cs" /> <Compile Include="MediaCoverTests\MediaCoverServiceFixture.cs" />
@ -309,6 +310,7 @@
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" /> <Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" /> <Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateEpisodesFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateEpisodesFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateLanguageFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateQualityFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateQualityFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromMediaInfoFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\Augmenters\Quality\AugmentQualityFromMediaInfoFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" /> <Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
@ -392,6 +394,8 @@
<Compile Include="ParserTests\SceneCheckerFixture.cs" /> <Compile Include="ParserTests\SceneCheckerFixture.cs" />
<Compile Include="Profiles\ProfileRepositoryFixture.cs" /> <Compile Include="Profiles\ProfileRepositoryFixture.cs" />
<Compile Include="Profiles\ProfileServiceFixture.cs" /> <Compile Include="Profiles\ProfileServiceFixture.cs" />
<Compile Include="Languages\LanguageProfileRepositoryFixture.cs" />
<Compile Include="Languages\LanguageProfileServiceFixture.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\XemProxyFixture.cs" /> <Compile Include="Providers\XemProxyFixture.cs" />
<Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" /> <Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" />

View File

@ -1,5 +1,6 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -9,61 +10,208 @@ namespace NzbDrone.Core.Test.ParserTests
[TestFixture] [TestFixture]
public class LanguageParserFixture : CoreTest public class LanguageParserFixture : CoreTest
{ {
[TestCase("Castle.2009.S01E14.English.HDTV.XviD-LOL", Language.English)] [TestCase("Castle.2009.S01E14.English.HDTV.XviD-LOL")]
[TestCase("Castle.2009.S01E14.French.HDTV.XviD-LOL", Language.French)] [TestCase("Castle.2009.S01E14.Germany.HDTV.XviD-LOL")]
[TestCase("Castle.2009.S01E14.Spanish.HDTV.XviD-LOL", Language.Spanish)] [TestCase("Castle.2009.S01E14.HDTV.XviD-LOL")]
[TestCase("Castle.2009.S01E14.German.HDTV.XviD-LOL", Language.German)] [TestCase("Two.Greedy.Italians.S01E01.The.Family.720p.HDTV.x264-FTP")]
[TestCase("Castle.2009.S01E14.Germany.HDTV.XviD-LOL", Language.English)] [TestCase("The.Trip.To.Italy.S02E01.720p.HDTV.x264-TLA")]
[TestCase("Castle.2009.S01E14.Italian.HDTV.XviD-LOL", Language.Italian)] [TestCase("2 Broke Girls - S01E01 - Pilot.en.sub")]
[TestCase("Castle.2009.S01E14.Danish.HDTV.XviD-LOL", Language.Danish)] [TestCase("2 Broke Girls - S01E01 - Pilot.eng.sub")]
[TestCase("Castle.2009.S01E14.Dutch.HDTV.XviD-LOL", Language.Dutch)] [TestCase("2 Broke Girls - S01E01 - Pilot.English.sub")]
[TestCase("Castle.2009.S01E14.Japanese.HDTV.XviD-LOL", Language.Japanese)] [TestCase("2 Broke Girls - S01E01 - Pilot.english.sub")]
[TestCase("Castle.2009.S01E14.Cantonese.HDTV.XviD-LOL", Language.Cantonese)] public void should_parse_language_english(string postTitle)
[TestCase("Castle.2009.S01E14.Mandarin.HDTV.XviD-LOL", Language.Mandarin)]
[TestCase("Castle.2009.S01E14.Korean.HDTV.XviD-LOL", Language.Korean)]
[TestCase("Castle.2009.S01E14.Russian.HDTV.XviD-LOL", Language.Russian)]
[TestCase("Castle.2009.S01E14.Polish.HDTV.XviD-LOL", Language.Polish)]
[TestCase("Castle.2009.S01E14.Vietnamese.HDTV.XviD-LOL", Language.Vietnamese)]
[TestCase("Castle.2009.S01E14.Swedish.HDTV.XviD-LOL", Language.Swedish)]
[TestCase("Castle.2009.S01E14.Norwegian.HDTV.XviD-LOL", Language.Norwegian)]
[TestCase("Castle.2009.S01E14.Finnish.HDTV.XviD-LOL", Language.Finnish)]
[TestCase("Castle.2009.S01E14.Turkish.HDTV.XviD-LOL", Language.Turkish)]
[TestCase("Castle.2009.S01E14.Portuguese.HDTV.XviD-LOL", Language.Portuguese)]
[TestCase("Castle.2009.S01E14.HDTV.XviD-LOL", Language.English)]
[TestCase("person.of.interest.1x19.ita.720p.bdmux.x264-novarip", Language.Italian)]
[TestCase("Salamander.S01E01.FLEMISH.HDTV.x264-BRiGAND", Language.Flemish)]
[TestCase("H.Polukatoikia.S03E13.Greek.PDTV.XviD-Ouzo", Language.Greek)]
[TestCase("Burn.Notice.S04E15.Brotherly.Love.GERMAN.DUBBED.WS.WEBRiP.XviD.REPACK-TVP", Language.German)]
[TestCase("Ray Donovan - S01E01.720p.HDtv.x264-Evolve (NLsub)", Language.Dutch)]
[TestCase("Shield,.The.1x13.Tueurs.De.Flics.FR.DVDRip.XviD", Language.French)]
[TestCase("True.Detective.S01E01.1080p.WEB-DL.Rus.Eng.TVKlondike", Language.Russian)]
[TestCase("The.Trip.To.Italy.S02E01.720p.HDTV.x264-TLA", Language.English)]
[TestCase("Revolution S01E03 No Quarter 2012 WEB-DL 720p Nordic-philipo mkv", Language.Norwegian)]
[TestCase("Extant.S01E01.VOSTFR.HDTV.x264-RiDERS", Language.French)]
[TestCase("Constantine.2014.S01E01.WEBRiP.H264.AAC.5.1-NL.SUBS", Language.Dutch)]
[TestCase("Elementary - S02E16 - Kampfhaehne - mkv - by Videomann", Language.German)]
[TestCase("Two.Greedy.Italians.S01E01.The.Family.720p.HDTV.x264-FTP", Language.English)]
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUNDUB-LOL", Language.Hungarian)]
[TestCase("Castle.2009.S01E14.HDTV.XviD.ENG.HUN-LOL", Language.Hungarian)]
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUN-LOL", Language.Hungarian)]
[TestCase("Avatar.The.Last.Airbender.S01-03.DVDRip.HebDub",Language.Hebrew)]
[TestCase("Prison.Break.S05E01.WEBRip.x264.AC3.LT.EN-CNN", Language.Lithuanian)]
[TestCase("The.Walking.Dead.S07E11.WEB Rip.XviD.Louige-CZ.EN.5.1", Language.Czech)]
public void should_parse_language(string postTitle, Language language)
{ {
var result = LanguageParser.ParseLanguage(postTitle); var result = LanguageParser.ParseLanguage(postTitle);
result.Should().Be(language); result.Should().Be(Language.English);
} }
[TestCase("2 Broke Girls - S01E01 - Pilot.en.sub", Language.English)] [TestCase("2 Broke Girls - S01E01 - Pilot.sub")]
[TestCase("2 Broke Girls - S01E01 - Pilot.eng.sub", Language.English)] public void should_parse_subtitle_language_unknown(string fileName)
[TestCase("2 Broke Girls - S01E01 - Pilot.English.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot.english.sub", Language.English)]
[TestCase("2 Broke Girls - S01E01 - Pilot.sub", Language.Unknown)]
public void should_parse_subtitle_language(string fileName, Language language)
{ {
var result = LanguageParser.ParseSubtitleLanguage(fileName); var result = LanguageParser.ParseSubtitleLanguage(fileName);
result.Should().Be(language); result.Should().Be(Language.Unknown);
} }
[TestCase("Castle.2009.S01E14.French.HDTV.XviD-LOL")]
[TestCase("Extant.S01E01.VOSTFR.HDTV.x264-RiDERS")]
[TestCase("Shield,.The.1x13.Tueurs.De.Flics.FR.DVDRip.XviD")]
public void should_parse_language_french(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.French.Id);
}
[TestCase("Castle.2009.S01E14.Spanish.HDTV.XviD-LOL")]
public void should_parse_language_spanish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Spanish.Id);
}
[TestCase("Castle.2009.S01E14.German.HDTV.XviD-LOL")]
[TestCase("Burn.Notice.S04E15.Brotherly.Love.GERMAN.DUBBED.WS.WEBRiP.XviD.REPACK-TVP")]
[TestCase("Elementary - S02E16 - Kampfhaehne - mkv - by Videomann")]
public void should_parse_language_german(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.German.Id);
}
[TestCase("Castle.2009.S01E14.Italian.HDTV.XviD-LOL")]
[TestCase("person.of.interest.1x19.ita.720p.bdmux.x264-novarip")]
public void should_parse_language_italian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Italian.Id);
}
[TestCase("Castle.2009.S01E14.Danish.HDTV.XviD-LOL")]
public void should_parse_language_danish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Danish.Id);
}
[TestCase("Castle.2009.S01E14.Dutch.HDTV.XviD-LOL")]
[TestCase("Constantine.2014.S01E01.WEBRiP.H264.AAC.5.1-NL.SUBS")]
[TestCase("Ray Donovan - S01E01.720p.HDtv.x264-Evolve (NLsub)")]
public void should_parse_language_dutch(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Dutch.Id);
}
[TestCase("Castle.2009.S01E14.Japanese.HDTV.XviD-LOL")]
public void should_parse_language_japanese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Japanese.Id);
}
[TestCase("Castle.2009.S01E14.Cantonese.HDTV.XviD-LOL")]
public void should_parse_language_cantonese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Cantonese.Id);
}
[TestCase("Castle.2009.S01E14.Mandarin.HDTV.XviD-LOL")]
public void should_parse_language_mandarin(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Mandarin.Id);
}
[TestCase("Castle.2009.S01E14.Korean.HDTV.XviD-LOL")]
public void should_parse_language_korean(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Korean.Id);
}
[TestCase("Castle.2009.S01E14.Russian.HDTV.XviD-LOL")]
[TestCase("True.Detective.S01E01.1080p.WEB-DL.Rus.Eng.TVKlondike")]
public void should_parse_language_russian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Russian.Id);
}
[TestCase("Castle.2009.S01E14.Polish.HDTV.XviD-LOL")]
public void should_parse_language_polish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Polish.Id);
}
[TestCase("Castle.2009.S01E14.Vietnamese.HDTV.XviD-LOL")]
public void should_parse_language_vietnamese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Vietnamese.Id);
}
[TestCase("Castle.2009.S01E14.Swedish.HDTV.XviD-LOL")]
public void should_parse_language_swedish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Swedish.Id);
}
[TestCase("Castle.2009.S01E14.Norwegian.HDTV.XviD-LOL")]
[TestCase("Revolution S01E03 No Quarter 2012 WEB-DL 720p Nordic-philipo mkv")]
public void should_parse_language_norwegian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Norwegian.Id);
}
[TestCase("Castle.2009.S01E14.Finnish.HDTV.XviD-LOL")]
public void should_parse_language_finnish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Finnish.Id);
}
[TestCase("Castle.2009.S01E14.Turkish.HDTV.XviD-LOL")]
public void should_parse_language_turkish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Turkish.Id);
}
[TestCase("Castle.2009.S01E14.Portuguese.HDTV.XviD-LOL")]
public void should_parse_language_portuguese(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Portuguese.Id);
}
[TestCase("Salamander.S01E01.FLEMISH.HDTV.x264-BRiGAND")]
public void should_parse_language_flemish(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Flemish.Id);
}
[TestCase("H.Polukatoikia.S03E13.Greek.PDTV.XviD-Ouzo")]
public void should_parse_language_greek(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Greek.Id);
}
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUNDUB-LOL")]
[TestCase("Castle.2009.S01E14.HDTV.XviD.ENG.HUN-LOL")]
[TestCase("Castle.2009.S01E14.HDTV.XviD.HUN-LOL")]
public void should_parse_language_hungarian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Hungarian.Id);
}
[TestCase("Avatar.The.Last.Airbender.S01-03.DVDRip.HebDub")]
public void should_parse_language_hebrew(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Hebrew.Id);
}
[TestCase("Prison.Break.S05E01.WEBRip.x264.AC3.LT.EN-CNN")]
public void should_parse_language_lithuanian(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Lithuanian.Id);
}
[TestCase("The.Walking.Dead.S07E11.WEB Rip.XviD.Louige-CZ.EN.5.1")]
public void should_parse_language_czech(string postTitle)
{
var result = Parser.Parser.ParseTitle(postTitle);
result.Language.Id.Should().Be(Language.Czech.Id);
}
} }
} }

View File

@ -1,6 +1,6 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;

View File

@ -3,7 +3,7 @@ using FizzWare.NBuilder;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;

View File

@ -1,6 +1,6 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;

View File

@ -5,11 +5,13 @@ using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{ {
@ -20,6 +22,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
private Series _unmonitoredSeries; private Series _unmonitoredSeries;
private PagingSpec<Episode> _pagingSpec; private PagingSpec<Episode> _pagingSpec;
private List<QualitiesBelowCutoff> _qualitiesBelowCutoff; private List<QualitiesBelowCutoff> _qualitiesBelowCutoff;
private List<LanguagesBelowCutoff> _languagesBelowCutoff;
private List<Episode> _unairedEpisodes; private List<Episode> _unairedEpisodes;
[SetUp] [SetUp]
@ -37,12 +40,20 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
} }
}; };
var langProfile = new LanguageProfile
{
Id = 1,
Languages = Languages.LanguageFixture.GetDefaultLanguages(),
Cutoff = Language.Spanish
};
_monitoredSeries = Builder<Series>.CreateNew() _monitoredSeries = Builder<Series>.CreateNew()
.With(s => s.TvRageId = RandomNumber) .With(s => s.TvRageId = RandomNumber)
.With(s => s.Runtime = 30) .With(s => s.Runtime = 30)
.With(s => s.Monitored = true) .With(s => s.Monitored = true)
.With(s => s.TitleSlug = "Title3") .With(s => s.TitleSlug = "Title3")
.With(s => s.Id = profile.Id) .With(s => s.ProfileId = profile.Id)
.With(s => s.LanguageProfileId = langProfile.Id)
.BuildNew(); .BuildNew();
_unmonitoredSeries = Builder<Series>.CreateNew() _unmonitoredSeries = Builder<Series>.CreateNew()
@ -50,7 +61,8 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
.With(s => s.Runtime = 30) .With(s => s.Runtime = 30)
.With(s => s.Monitored = false) .With(s => s.Monitored = false)
.With(s => s.TitleSlug = "Title2") .With(s => s.TitleSlug = "Title2")
.With(s => s.Id = profile.Id) .With(s => s.ProfileId = profile.Id)
.With(s => s.LanguageProfileId = langProfile.Id)
.BuildNew(); .BuildNew();
_monitoredSeries.Id = Db.Insert(_monitoredSeries).Id; _monitoredSeries.Id = Db.Insert(_monitoredSeries).Id;
@ -69,15 +81,32 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
new QualitiesBelowCutoff(profile.Id, new[] {Quality.SDTV.Id}) new QualitiesBelowCutoff(profile.Id, new[] {Quality.SDTV.Id})
}; };
var qualityMet = new EpisodeFile { RelativePath = "a", Quality = new QualityModel { Quality = Quality.WEBDL480p } }; _languagesBelowCutoff = new List<LanguagesBelowCutoff>
var qualityUnmet = new EpisodeFile { RelativePath = "b", Quality = new QualityModel { Quality = Quality.SDTV } }; {
var qualityRawHD = new EpisodeFile { RelativePath = "c", Quality = new QualityModel { Quality = Quality.RAWHD } }; new LanguagesBelowCutoff(profile.Id, new[] {Language.English.Id})
};
var qualityMetLanguageUnmet = new EpisodeFile { RelativePath = "a", Quality = new QualityModel { Quality = Quality.WEBDL480p } , Language = Language.English };
var qualityMetLanguageMet = new EpisodeFile { RelativePath = "b", Quality = new QualityModel { Quality = Quality.WEBDL480p }, Language = Language.Spanish };
var qualityMetLanguageExceed = new EpisodeFile { RelativePath = "c", Quality = new QualityModel { Quality = Quality.WEBDL480p }, Language = Language.French };
var qualityUnmetLanguageUnmet = new EpisodeFile { RelativePath = "d", Quality = new QualityModel { Quality = Quality.SDTV }, Language = Language.English };
var qualityUnmetLanguageMet = new EpisodeFile { RelativePath = "e", Quality = new QualityModel { Quality = Quality.SDTV }, Language = Language.Spanish };
var qualityUnmetLanguageExceed = new EpisodeFile { RelativePath = "f", Quality = new QualityModel { Quality = Quality.SDTV }, Language = Language.French };
var qualityRawHDLanguageUnmet = new EpisodeFile { RelativePath = "g", Quality = new QualityModel { Quality = Quality.RAWHD }, Language = Language.English };
var qualityRawHDLanguageMet = new EpisodeFile { RelativePath = "h", Quality = new QualityModel { Quality = Quality.RAWHD }, Language = Language.Spanish };
var qualityRawHDLanguageExceed = new EpisodeFile { RelativePath = "i", Quality = new QualityModel { Quality = Quality.RAWHD }, Language = Language.French };
MediaFileRepository fileRepository = Mocker.Resolve<MediaFileRepository>(); MediaFileRepository fileRepository = Mocker.Resolve<MediaFileRepository>();
qualityMet = fileRepository.Insert(qualityMet); qualityMetLanguageUnmet = fileRepository.Insert(qualityMetLanguageUnmet);
qualityUnmet = fileRepository.Insert(qualityUnmet); qualityMetLanguageMet = fileRepository.Insert(qualityMetLanguageMet);
qualityRawHD = fileRepository.Insert(qualityRawHD); qualityMetLanguageExceed = fileRepository.Insert(qualityMetLanguageExceed);
qualityUnmetLanguageUnmet = fileRepository.Insert(qualityUnmetLanguageUnmet);
qualityUnmetLanguageMet = fileRepository.Insert(qualityUnmetLanguageMet);
qualityUnmetLanguageExceed = fileRepository.Insert(qualityUnmetLanguageExceed);
qualityRawHDLanguageUnmet = fileRepository.Insert(qualityRawHDLanguageUnmet);
qualityRawHDLanguageMet = fileRepository.Insert(qualityRawHDLanguageMet);
qualityRawHDLanguageExceed = fileRepository.Insert(qualityRawHDLanguageExceed);
var monitoredSeriesEpisodes = Builder<Episode>.CreateListOfSize(4) var monitoredSeriesEpisodes = Builder<Episode>.CreateListOfSize(4)
.All() .All()
@ -85,12 +114,12 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
.With(e => e.SeriesId = _monitoredSeries.Id) .With(e => e.SeriesId = _monitoredSeries.Id)
.With(e => e.AirDateUtc = DateTime.Now.AddDays(-5)) .With(e => e.AirDateUtc = DateTime.Now.AddDays(-5))
.With(e => e.Monitored = true) .With(e => e.Monitored = true)
.With(e => e.EpisodeFileId = qualityUnmet.Id) .With(e => e.EpisodeFileId = qualityUnmetLanguageUnmet.Id)
.TheFirst(1) .TheFirst(1)
.With(e => e.Monitored = false) .With(e => e.Monitored = false)
.With(e => e.EpisodeFileId = qualityMet.Id) .With(e => e.EpisodeFileId = qualityMetLanguageMet.Id)
.TheNext(1) .TheNext(1)
.With(e => e.EpisodeFileId = qualityRawHD.Id) .With(e => e.EpisodeFileId = qualityRawHDLanguageExceed.Id)
.TheLast(1) .TheLast(1)
.With(e => e.SeasonNumber = 0) .With(e => e.SeasonNumber = 0)
.Build(); .Build();
@ -101,10 +130,10 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
.With(e => e.SeriesId = _unmonitoredSeries.Id) .With(e => e.SeriesId = _unmonitoredSeries.Id)
.With(e => e.AirDateUtc = DateTime.Now.AddDays(-5)) .With(e => e.AirDateUtc = DateTime.Now.AddDays(-5))
.With(e => e.Monitored = true) .With(e => e.Monitored = true)
.With(e => e.EpisodeFileId = qualityUnmet.Id) .With(e => e.EpisodeFileId = qualityRawHDLanguageUnmet.Id)
.TheFirst(1) .TheFirst(1)
.With(e => e.Monitored = false) .With(e => e.Monitored = false)
.With(e => e.EpisodeFileId = qualityMet.Id) .With(e => e.EpisodeFileId = qualityMetLanguageMet.Id)
.TheLast(1) .TheLast(1)
.With(e => e.SeasonNumber = 0) .With(e => e.SeasonNumber = 0)
.Build(); .Build();
@ -116,7 +145,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
.With(e => e.SeriesId = _monitoredSeries.Id) .With(e => e.SeriesId = _monitoredSeries.Id)
.With(e => e.AirDateUtc = DateTime.Now.AddDays(5)) .With(e => e.AirDateUtc = DateTime.Now.AddDays(5))
.With(e => e.Monitored = true) .With(e => e.Monitored = true)
.With(e => e.EpisodeFileId = qualityUnmet.Id) .With(e => e.EpisodeFileId = qualityUnmetLanguageUnmet.Id)
.Build() .Build()
.ToList(); .ToList();
@ -139,7 +168,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{ {
GivenMonitoredFilterExpression(); GivenMonitoredFilterExpression();
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, false); var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
spec.Records.Should().HaveCount(1); spec.Records.Should().HaveCount(1);
spec.Records.Should().OnlyContain(e => e.EpisodeFile.Value.Quality.Quality == Quality.SDTV); spec.Records.Should().OnlyContain(e => e.EpisodeFile.Value.Quality.Quality == Quality.SDTV);
@ -150,7 +179,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{ {
GivenMonitoredFilterExpression(); GivenMonitoredFilterExpression();
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, false); var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
spec.Records.Should().HaveCount(1); spec.Records.Should().HaveCount(1);
spec.Records.Should().OnlyContain(e => e.Monitored); spec.Records.Should().OnlyContain(e => e.Monitored);
@ -161,7 +190,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
{ {
GivenMonitoredFilterExpression(); GivenMonitoredFilterExpression();
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, false); var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
spec.Records.Should().HaveCount(1); spec.Records.Should().HaveCount(1);
spec.Records.Should().OnlyContain(e => e.Series.Monitored); spec.Records.Should().OnlyContain(e => e.Series.Monitored);
@ -174,7 +203,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
GivenMonitoredFilterExpression(); GivenMonitoredFilterExpression();
var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, false); var spec = Subject.EpisodesWhereCutoffUnmet(_pagingSpec, _qualitiesBelowCutoff, _languagesBelowCutoff, false);
spec.Records.Should().HaveCount(2); spec.Records.Should().HaveCount(2);
spec.Records.Should().OnlyContain(e => e.Series.Monitored); spec.Records.Should().OnlyContain(e => e.Series.Monitored);

View File

@ -1,10 +1,13 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Profiles; using System.Linq;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests
{ {
@ -23,16 +26,26 @@ namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests
Name = "TestProfile" Name = "TestProfile"
}; };
var langProfile = new LanguageProfile
{
Name = "TestProfile",
Languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English),
Cutoff = Language.English
};
Mocker.Resolve<ProfileRepository>().Insert(profile); Mocker.Resolve<ProfileRepository>().Insert(profile);
Mocker.Resolve<LanguageProfileRepository>().Insert(langProfile);
var series = Builder<Series>.CreateNew().BuildNew(); var series = Builder<Series>.CreateNew().BuildNew();
series.ProfileId = profile.Id; series.ProfileId = profile.Id;
series.LanguageProfileId = langProfile.Id;
Subject.Insert(series); Subject.Insert(series);
StoredModel.Profile.Should().NotBeNull(); StoredModel.Profile.Should().NotBeNull();
StoredModel.LanguageProfile.Should().NotBeNull();
} }

View File

@ -4,6 +4,7 @@ using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Blacklisting namespace NzbDrone.Core.Blacklisting
{ {
@ -21,5 +22,6 @@ namespace NzbDrone.Core.Blacklisting
public string Indexer { get; set; } public string Indexer { get; set; }
public string Message { get; set; } public string Message { get; set; }
public string TorrentInfoHash { get; set; } public string TorrentInfoHash { get; set; }
public Language Language { get; set; }
} }
} }

View File

@ -138,7 +138,8 @@ namespace NzbDrone.Core.Blacklisting
Indexer = message.Data.GetValueOrDefault("indexer"), Indexer = message.Data.GetValueOrDefault("indexer"),
Protocol = (DownloadProtocol)Convert.ToInt32(message.Data.GetValueOrDefault("protocol")), Protocol = (DownloadProtocol)Convert.ToInt32(message.Data.GetValueOrDefault("protocol")),
Message = message.Message, Message = message.Message,
TorrentInfoHash = message.Data.GetValueOrDefault("torrentInfoHash") TorrentInfoHash = message.Data.GetValueOrDefault("torrentInfoHash"),
Language = message.Language
}; };
_blacklistRepository.Insert(blacklist); _blacklistRepository.Insert(blacklist);

View File

@ -0,0 +1,65 @@
using System;
using Marr.Data.Converters;
using Marr.Data.Mapping;
using Newtonsoft.Json;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Datastore.Converters
{
public class LanguageIntConverter : JsonConverter, IConverter
{
public object FromDB(ConverterContext context)
{
if (context.DbValue == DBNull.Value)
{
return Language.Unknown;
}
var val = Convert.ToInt32(context.DbValue);
return (Language)val;
}
public object FromDB(ColumnMap map, object dbValue)
{
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
}
public object ToDB(object clrValue)
{
if (clrValue == DBNull.Value) return 0;
if (clrValue as Language == null)
{
throw new InvalidOperationException("Attempted to save a language that isn't really a language");
}
var language = clrValue as Language;
return (int)language;
}
public Type DbType
{
get
{
return typeof(int);
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Language);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var item = reader.Value;
return (Language)Convert.ToInt32(item);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(ToDB(value));
}
}
}

View File

@ -3,7 +3,7 @@ using NzbDrone.Core.Datastore.Migration.Framework;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;

View File

@ -1 +1,97 @@
// This is a placeholder for migration 102 using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Languages;
using System;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(102)]
public class add_language_to_episodeFiles_history_and_blacklist : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("EpisodeFiles")
.AddColumn("Language").AsInt32().NotNullable().WithDefaultValue(0);
Alter.Table("History")
.AddColumn("Language").AsInt32().NotNullable().WithDefaultValue(0);
Alter.Table("Blacklist")
.AddColumn("Language").AsInt32().NotNullable().WithDefaultValue(0);
Execute.WithConnection(UpdateLanguage);
}
private void UpdateLanguage(IDbConnection conn, IDbTransaction tran)
{
var LanguageConverter = new EmbeddedDocumentConverter(new LanguageIntConverter());
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, ProfileId FROM Series";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var seriesId = seriesReader.GetInt32(0);
var seriesProfileId = seriesReader.GetInt32(1);
using (IDbCommand getProfileCmd = conn.CreateCommand())
{
getProfileCmd.Transaction = tran;
getProfileCmd.CommandText = "SELECT Language FROM Profiles WHERE Id = ?";
getProfileCmd.AddParameter(seriesProfileId);
IDataReader profilesReader = getProfileCmd.ExecuteReader();
while (profilesReader.Read())
{
var episodeLanguage = Language.English.Id;
try
{
episodeLanguage = profilesReader.GetInt32(0);
} catch (InvalidCastException e)
{
_logger.Debug("Language field not found in Profiles, using English as default." + e.Message);
}
var validJson = LanguageConverter.ToDB(Language.FindById(episodeLanguage));
using (IDbCommand updateEpisodeFilesCmd = conn.CreateCommand())
{
updateEpisodeFilesCmd.Transaction = tran;
updateEpisodeFilesCmd.CommandText = "UPDATE EpisodeFiles SET Language = ? WHERE SeriesId = ?";
updateEpisodeFilesCmd.AddParameter(validJson);
updateEpisodeFilesCmd.AddParameter(seriesId);
updateEpisodeFilesCmd.ExecuteNonQuery();
}
using (IDbCommand updateHistoryCmd = conn.CreateCommand())
{
updateHistoryCmd.Transaction = tran;
updateHistoryCmd.CommandText = "UPDATE History SET Language = ? WHERE SeriesId = ?";
updateHistoryCmd.AddParameter(validJson);
updateHistoryCmd.AddParameter(seriesId);
updateHistoryCmd.ExecuteNonQuery();
}
using (IDbCommand updateBlacklistCmd = conn.CreateCommand())
{
updateBlacklistCmd.Transaction = tran;
updateBlacklistCmd.CommandText = "UPDATE Blacklist SET Language = ? WHERE SeriesId = ?";
updateBlacklistCmd.AddParameter(validJson);
updateBlacklistCmd.AddParameter(seriesId);
updateBlacklistCmd.ExecuteNonQuery();
}
}
}
}
}
}
}
}
}

View File

@ -1 +1,180 @@
// This is a placeholder for migration 111 using FluentMigrator;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Languages;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(111)]
public class create_language_profiles : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("LanguageProfiles").WithColumn("Name").AsString().Unique()
.WithColumn("Languages").AsString()
.WithColumn("Cutoff").AsInt32();
Alter.Table("Series").AddColumn("LanguageProfileId").AsInt32().WithDefaultValue(1);
Execute.WithConnection(InsertDefaultLanguages);
Delete.Column("Language").FromTable("Profiles");
}
private void InsertDefaultLanguages(IDbConnection conn, IDbTransaction tran)
{
var profiles = GetLanguageProfiles(conn, tran);
var languageConverter = new EmbeddedDocumentConverter(new LanguageIntConverter());
foreach (var profile in profiles.OrderBy(p => p.Id))
{
using (IDbCommand insertNewLanguageProfileCmd = conn.CreateCommand())
{
var itemsJson = languageConverter.ToDB(profile.Languages);
insertNewLanguageProfileCmd.Transaction = tran;
insertNewLanguageProfileCmd.CommandText = "INSERT INTO LanguageProfiles (Id, Name, Cutoff, Languages) VALUES (?, ?, ?, ?)";
insertNewLanguageProfileCmd.AddParameter(profile.Id);
insertNewLanguageProfileCmd.AddParameter(profile.Name);
insertNewLanguageProfileCmd.AddParameter(profile.Cutoff.Id);
insertNewLanguageProfileCmd.AddParameter(itemsJson);
insertNewLanguageProfileCmd.ExecuteNonQuery();
}
using (IDbCommand updateSeriesCmd = conn.CreateCommand())
{
foreach (var profileId in profile.ProfileIds)
{
updateSeriesCmd.Transaction = tran;
updateSeriesCmd.CommandText = "UPDATE Series SET LanguageProfileId = ? WHERE ProfileId = ?";
updateSeriesCmd.AddParameter(profile.Id);
updateSeriesCmd.AddParameter(profileId);
updateSeriesCmd.ExecuteNonQuery();
}
}
}
}
private List<LanguageProfile111> GetDefaultLanguageProfiles()
{
var profiles = new List<LanguageProfile111>();
var languages = GetOrderedLanguages().Select(v => new LanguageProfileItem111 { Language = v, Allowed = v == Language.English })
.ToList();
profiles.Add(new LanguageProfile111
{
Id = 1,
Name = "English",
Cutoff = Language.English,
Languages = languages
});
return profiles;
}
private List<LanguageProfile111> GetLanguageProfiles(IDbConnection conn, IDbTransaction tran)
{
var profiles = GetDefaultLanguageProfiles();
var thereAreProfiles = false;
using (IDbCommand getProfilesCmd = conn.CreateCommand())
{
getProfilesCmd.Transaction = tran;
getProfilesCmd.CommandText = @"SELECT Id, Language FROM Profiles";
using (IDataReader profileReader = getProfilesCmd.ExecuteReader())
{
while (profileReader.Read())
{
thereAreProfiles = true;
var profileId = profileReader.GetInt32(0);
var lang = Language.English.Id;
try
{
lang = profileReader.GetInt32(1);
}
catch (InvalidCastException e)
{
_logger.Debug("Language field not found in Profiles, using English as default." + e.Message);
}
if (profiles.None(p => p.Cutoff.Id == lang))
{
var language = Language.FindById(lang);
var languages = GetOrderedLanguages().Select(l => new LanguageProfileItem111 { Language = l, Allowed = l.Id == lang })
.ToList();
profiles.Add(new LanguageProfile111
{
Id = profiles.Count + 1,
Name = language.Name,
Cutoff = language,
Languages = languages,
ProfileIds = new List<int> { profileId }
});
}
else
{
profiles = profiles.Select(p =>
{
if (p.Cutoff.Id == lang)
{
p.ProfileIds.Add(profileId);
}
return p;
}).ToList();
}
}
}
}
if (!thereAreProfiles)
{
return new List<LanguageProfile111>();
}
return profiles;
}
private List<Language> GetOrderedLanguages()
{
var orderedLanguages = Language.All
.Where(l => l != Language.Unknown)
.OrderByDescending(l => l.Name)
.ToList();
orderedLanguages.Insert(0, Language.Unknown);
return orderedLanguages;
}
private class LanguageProfile111
{
public int Id { get; set; }
public List<int> ProfileIds { get; set; }
public string Name { get; set; }
public Language Cutoff { get; set; }
public List<LanguageProfileItem111> Languages { get; set; }
public LanguageProfile111 ()
{
ProfileIds = new List<int>();
}
}
private class LanguageProfileItem111
{
public Language Language { get; set; }
public bool Allowed { get; set; }
}
}
}

View File

@ -19,7 +19,7 @@ using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Notifications; using NzbDrone.Core.Notifications;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Restrictions; using NzbDrone.Core.Restrictions;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
@ -35,6 +35,8 @@ using NzbDrone.Core.Extras.Metadata.Files;
using NzbDrone.Core.Extras.Others; using NzbDrone.Core.Extras.Others;
using NzbDrone.Core.Extras.Subtitles; using NzbDrone.Core.Extras.Subtitles;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Datastore namespace NzbDrone.Core.Datastore
{ {
@ -82,7 +84,8 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<Series>().RegisterModel("Series") Mapper.Entity<Series>().RegisterModel("Series")
.Ignore(s => s.RootFolderPath) .Ignore(s => s.RootFolderPath)
.Relationship() .Relationship()
.HasOne(s => s.Profile, s => s.ProfileId); .HasOne(s => s.Profile, s => s.ProfileId)
.HasOne(s => s.LanguageProfile, s => s.LanguageProfileId);
Mapper.Entity<EpisodeFile>().RegisterModel("EpisodeFiles") Mapper.Entity<EpisodeFile>().RegisterModel("EpisodeFiles")
.Ignore(f => f.Path) .Ignore(f => f.Path)
@ -103,6 +106,7 @@ namespace NzbDrone.Core.Datastore
.Ignore(d => d.Weight); .Ignore(d => d.Weight);
Mapper.Entity<Profile>().RegisterModel("Profiles"); Mapper.Entity<Profile>().RegisterModel("Profiles");
Mapper.Entity<LanguageProfile>().RegisterModel("LanguageProfiles");
Mapper.Entity<Log>().RegisterModel("Logs"); Mapper.Entity<Log>().RegisterModel("Logs");
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig"); Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
Mapper.Entity<SeasonStatistics>().MapResultSet(); Mapper.Entity<SeasonStatistics>().MapResultSet();
@ -144,7 +148,9 @@ namespace NzbDrone.Core.Datastore
MapRepository.Instance.RegisterTypeConverter(typeof(QualityModel), new EmbeddedDocumentConverter(new QualityIntConverter())); MapRepository.Instance.RegisterTypeConverter(typeof(QualityModel), new EmbeddedDocumentConverter(new QualityIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<string, string>), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<string, string>), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<int>), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(List<int>), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(Language), new LanguageIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<string>), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(List<string>), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<LanguageProfileItem>), new EmbeddedDocumentConverter(new LanguageIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedEpisodeInfo), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(ParsedEpisodeInfo), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(HashSet<int>), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(HashSet<int>), new EmbeddedDocumentConverter());

View File

@ -24,6 +24,7 @@ namespace NzbDrone.Core.DecisionEngine
var comparers = new List<CompareDelegate> var comparers = new List<CompareDelegate>
{ {
CompareQuality, CompareQuality,
CompareLanguage,
CompareProtocol, CompareProtocol,
CompareEpisodeCount, CompareEpisodeCount,
CompareEpisodeNumber, CompareEpisodeNumber,
@ -62,6 +63,11 @@ namespace NzbDrone.Core.DecisionEngine
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version)); CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version));
} }
private int CompareLanguage(DownloadDecision x, DownloadDecision y)
{
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.LanguageProfile.Value.Languages.FindIndex(l => l.Language == remoteEpisode.ParsedEpisodeInfo.Language));
}
private int CompareProtocol(DownloadDecision x, DownloadDecision y) private int CompareProtocol(DownloadDecision x, DownloadDecision y)
{ {
var result = CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => var result = CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>

View File

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.DecisionEngine namespace NzbDrone.Core.DecisionEngine
{ {

View File

@ -0,0 +1,75 @@
using NLog;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine
{
public interface ILanguageUpgradableSpecification
{
bool IsUpgradable(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null);
bool CutoffNotMet(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null);
bool IsRevisionUpgrade(LanguageModel currentLanguage, LanguageModel newLanguage);
}
public class LanguageUpgradableSpecification : ILanguageUpgradableSpecification
{
private readonly Logger _logger;
public LanguageUpgradableSpecification(Logger logger)
{
_logger = logger;
}
public bool IsUpgradable(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null)
{
if (newLanguage != null)
{
int compare = new LanguageModelComparer(profile).Compare(newLanguage, currentLanguage);
if (compare <= 0)
{
_logger.Debug("existing item has better or equal language. skipping");
return false;
}
if (IsRevisionUpgrade(currentLanguage, newLanguage))
{
return true;
}
}
return true;
}
public bool CutoffNotMet(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null)
{
int compare = new LanguageModelComparer(profile).Compare(currentLanguage.Language, profile.Languages.Find(v => v.Allowed == true).Language);
if (compare >= 0)
{
if (newLanguage != null && IsRevisionUpgrade(currentLanguage, newLanguage))
{
return true;
}
_logger.Debug("Existing item meets cut-off. skipping.");
return false;
}
return true;
}
public bool IsRevisionUpgrade(LanguageModel currentLanguage, LanguageModel newLanguage)
{
int compare = newLanguage.Revision.CompareTo(currentLanguage.Revision);
if (currentLanguage.Language == newLanguage.Language && compare > 0)
{
_logger.Debug("New language is a better revision for existing quality");
return true;
}
return false;
}
}
}

View File

@ -1,72 +0,0 @@
using NLog;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine
{
public interface IQualityUpgradableSpecification
{
bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality);
}
public class QualityUpgradableSpecification : IQualityUpgradableSpecification
{
private readonly Logger _logger;
public QualityUpgradableSpecification(Logger logger)
{
_logger = logger;
}
public bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
{
if (newQuality != null)
{
int compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality);
if (compare <= 0)
{
return false;
}
if (IsRevisionUpgrade(currentQuality, newQuality))
{
return true;
}
}
return true;
}
public bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
{
var compare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff);
if (compare < 0)
{
return true;
}
if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality))
{
return true;
}
return false;
}
public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality)
{
var compare = newQuality.Revision.CompareTo(currentQuality.Revision);
if (currentQuality.Quality == newQuality.Quality && compare > 0)
{
return true;
}
return false;
}
}
}

View File

@ -9,12 +9,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
public class AnimeVersionUpgradeSpecification : IDecisionEngineSpecification public class AnimeVersionUpgradeSpecification : IDecisionEngineSpecification
{ {
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly UpgradableSpecification _upgradableSpecification;
private readonly Logger _logger; private readonly Logger _logger;
public AnimeVersionUpgradeSpecification(QualityUpgradableSpecification qualityUpgradableSpecification, Logger logger) public AnimeVersionUpgradeSpecification(UpgradableSpecification UpgradableSpecification, Logger logger)
{ {
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = UpgradableSpecification;
_logger = logger; _logger = logger;
} }
@ -32,7 +32,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{ {
if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality)) if (_upgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality))
{ {
if (file.ReleaseGroup.IsNullOrWhiteSpace()) if (file.ReleaseGroup.IsNullOrWhiteSpace())
{ {

View File

@ -7,12 +7,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
public class CutoffSpecification : IDecisionEngineSpecification public class CutoffSpecification : IDecisionEngineSpecification
{ {
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly UpgradableSpecification _upgradableSpecification;
private readonly Logger _logger; private readonly Logger _logger;
public CutoffSpecification(QualityUpgradableSpecification qualityUpgradableSpecification, Logger logger) public CutoffSpecification(UpgradableSpecification UpgradableSpecification, Logger logger)
{ {
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = UpgradableSpecification;
_logger = logger; _logger = logger;
} }
@ -28,13 +28,16 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
_logger.Debug("File is no longer available, skipping this file."); _logger.Debug("File is no longer available, skipping this file.");
continue; continue;
} }
_logger.Debug("Comparing file quality and language with report. Existing file is {0} - {1}", file.Quality, file.Language);
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality); if (!_upgradableSpecification.CutoffNotMet(subject.Series.Profile,
subject.Series.LanguageProfile,
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, file.Quality, subject.ParsedEpisodeInfo.Quality)) file.Quality,
file.Language,
subject.ParsedEpisodeInfo.Quality))
{ {
_logger.Debug("Cutoff already met, rejecting."); _logger.Debug("Cutoff already met, rejecting.");
return Decision.Reject("Existing file meets cutoff: {0}", subject.Series.Profile.Value.Cutoff); return Decision.Reject("Existing file meets cutoff: {0} - {1}", subject.Series.Profile.Value.Cutoff, subject.Series.LanguageProfile.Value.Cutoff);
} }
} }

View File

@ -1,4 +1,5 @@
using NLog; using NLog;
using System.Linq;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -18,14 +19,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
var wantedLanguage = subject.Series.Profile.Value.Language; var wantedLanguage = subject.Series.LanguageProfile.Value.Languages;
var _language = subject.ParsedEpisodeInfo.Language;
_logger.Debug("Checking if report meets language requirements. {0}", subject.ParsedEpisodeInfo.Language); _logger.Debug("Checking if report meets language requirements. {0}", subject.ParsedEpisodeInfo.Language);
if (subject.ParsedEpisodeInfo.Language != wantedLanguage) if (!wantedLanguage.Exists(v => v.Allowed && v.Language == _language))
{ {
_logger.Debug("Report Language: {0} rejected because it is not wanted, wanted {1}", subject.ParsedEpisodeInfo.Language, wantedLanguage); _logger.Debug("Report Language: {0} rejected because it is not wanted in profile {1}", _language, subject.Series.LanguageProfile.Value.Name);
return Decision.Reject("{0} is wanted, but found {1}", wantedLanguage, subject.ParsedEpisodeInfo.Language); return Decision.Reject("{0} is not allowed in profile {1}", _language, subject.Series.LanguageProfile.Value.Name);
} }
return Decision.Accept(); return Decision.Accept();

View File

@ -9,15 +9,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public class QueueSpecification : IDecisionEngineSpecification public class QueueSpecification : IDecisionEngineSpecification
{ {
private readonly IQueueService _queueService; private readonly IQueueService _queueService;
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly UpgradableSpecification _upgradableSpecification;
private readonly Logger _logger; private readonly Logger _logger;
public QueueSpecification(IQueueService queueService, public QueueSpecification(IQueueService queueService,
QualityUpgradableSpecification qualityUpgradableSpecification, UpgradableSpecification UpgradableSpecification,
Logger logger) Logger logger)
{ {
_queueService = queueService; _queueService = queueService;
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = UpgradableSpecification;
_logger = logger; _logger = logger;
} }
@ -34,18 +34,27 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
foreach (var remoteEpisode in matchingEpisode) foreach (var remoteEpisode in matchingEpisode)
{ {
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); _logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0} - {1}", remoteEpisode.ParsedEpisodeInfo.Quality, remoteEpisode.ParsedEpisodeInfo.Language);
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, remoteEpisode.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.Quality)) if (!_upgradableSpecification.CutoffNotMet(subject.Series.Profile,
subject.Series.LanguageProfile,
remoteEpisode.ParsedEpisodeInfo.Quality,
remoteEpisode.ParsedEpisodeInfo.Language,
subject.ParsedEpisodeInfo.Quality))
{ {
return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); return Decision.Reject("Quality for release in queue already meets cutoff: {0} - {1}", remoteEpisode.ParsedEpisodeInfo.Quality, remoteEpisode.ParsedEpisodeInfo.Language);
} }
_logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); _logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0} - {1}", remoteEpisode.ParsedEpisodeInfo.Quality, remoteEpisode.ParsedEpisodeInfo.Language);
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, remoteEpisode.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.Quality)) if (!_upgradableSpecification.IsUpgradable(subject.Series.Profile,
subject.Series.LanguageProfile,
remoteEpisode.ParsedEpisodeInfo.Quality,
remoteEpisode.ParsedEpisodeInfo.Language,
subject.ParsedEpisodeInfo.Quality,
subject.ParsedEpisodeInfo.Language))
{ {
return Decision.Reject("Quality for release in queue is of equal or higher preference: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); return Decision.Reject("Quality for release in queue is of equal or higher preference: {0} - {1}", remoteEpisode.ParsedEpisodeInfo.Quality, remoteEpisode.ParsedEpisodeInfo.Language);
} }
} }

View File

@ -5,23 +5,24 @@ using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
{ {
public class DelaySpecification : IDecisionEngineSpecification public class DelaySpecification : IDecisionEngineSpecification
{ {
private readonly IPendingReleaseService _pendingReleaseService; private readonly IPendingReleaseService _pendingReleaseService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; private readonly IUpgradableSpecification _upgradableSpecification;
private readonly IDelayProfileService _delayProfileService; private readonly IDelayProfileService _delayProfileService;
private readonly Logger _logger; private readonly Logger _logger;
public DelaySpecification(IPendingReleaseService pendingReleaseService, public DelaySpecification(IPendingReleaseService pendingReleaseService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IUpgradableSpecification UpgradableSpecification,
IDelayProfileService delayProfileService, IDelayProfileService delayProfileService,
Logger logger) Logger logger)
{ {
_pendingReleaseService = pendingReleaseService; _pendingReleaseService = pendingReleaseService;
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = UpgradableSpecification;
_delayProfileService = delayProfileService; _delayProfileService = delayProfileService;
_logger = logger; _logger = logger;
} }
@ -38,6 +39,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
} }
var profile = subject.Series.Profile.Value; var profile = subject.Series.Profile.Value;
var languageProfile = subject.Series.LanguageProfile.Value;
var delayProfile = _delayProfileService.BestForTags(subject.Series.Tags); var delayProfile = _delayProfileService.BestForTags(subject.Series.Tags);
var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol); var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol);
var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol; var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol;
@ -49,33 +51,35 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
} }
var comparer = new QualityModelComparer(profile); var comparer = new QualityModelComparer(profile);
var comparerLanguage = new LanguageComparer(languageProfile);
if (isPreferredProtocol) if (isPreferredProtocol)
{ {
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{ {
var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, file.Quality, subject.ParsedEpisodeInfo.Quality); var upgradable = _upgradableSpecification.IsUpgradable(profile,
languageProfile,
file.Quality,
file.Language,
subject.ParsedEpisodeInfo.Quality,
subject.ParsedEpisodeInfo.Language);
if (upgradable) if (upgradable)
{
var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality);
if (revisionUpgrade)
{ {
_logger.Debug("New quality is a better revision for existing quality, skipping delay"); _logger.Debug("New quality is a better revision for existing quality, skipping delay");
return Decision.Accept(); return Decision.Accept();
} }
} }
} }
}
// If quality meets or exceeds the best allowed quality in the profile accept it immediately // If quality meets or exceeds the best allowed quality in the profile accept it immediately
var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality()); var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality());
var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile) >= 0; var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile) >= 0;
var isBestInProfileLanguage = comparerLanguage.Compare(subject.ParsedEpisodeInfo.Language, languageProfile.LastAllowedLanguage()) >= 0;
if (isBestInProfile && isPreferredProtocol) if (isBestInProfile && isBestInProfileLanguage && isPreferredProtocol)
{ {
_logger.Debug("Quality is highest in profile for preferred protocol, will not delay"); _logger.Debug("Quality and language is highest in profile for preferred protocol, will not delay");
return Decision.Accept(); return Decision.Accept();
} }

View File

@ -11,17 +11,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
public class HistorySpecification : IDecisionEngineSpecification public class HistorySpecification : IDecisionEngineSpecification
{ {
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly UpgradableSpecification _upgradableSpecification;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly Logger _logger; private readonly Logger _logger;
public HistorySpecification(IHistoryService historyService, public HistorySpecification(IHistoryService historyService,
QualityUpgradableSpecification qualityUpgradableSpecification, UpgradableSpecification upgradableSpecification,
IConfigService configService, IConfigService configService,
Logger logger) Logger logger)
{ {
_historyService = historyService; _historyService = historyService;
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = upgradableSpecification;
_configService = configService; _configService = configService;
_logger = logger; _logger = logger;
} }
@ -48,8 +48,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed) if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed)
{ {
var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12)); var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12));
var cutoffUnmet = _qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality); var cutoffUnmet = _upgradableSpecification.CutoffNotMet(subject.Series.Profile, subject.Series.LanguageProfile, mostRecent.Quality, mostRecent.Language, subject.ParsedEpisodeInfo.Quality);
var upgradeable = _qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality); var upgradeable = _upgradableSpecification.IsUpgradable(subject.Series.Profile, subject.Series.LanguageProfile, mostRecent.Quality, mostRecent.Language, subject.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.Language);
if (!recent && cdhEnabled) if (!recent && cdhEnabled)
{ {

View File

@ -9,13 +9,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
{ {
public class ProperSpecification : IDecisionEngineSpecification public class ProperSpecification : IDecisionEngineSpecification
{ {
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly UpgradableSpecification _upgradableSpecification;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly Logger _logger; private readonly Logger _logger;
public ProperSpecification(QualityUpgradableSpecification qualityUpgradableSpecification, IConfigService configService, Logger logger) public ProperSpecification(UpgradableSpecification upgradableSpecification, IConfigService configService, Logger logger)
{ {
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = upgradableSpecification;
_configService = configService; _configService = configService;
_logger = logger; _logger = logger;
} }
@ -32,7 +32,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{ {
if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality)) if (_upgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality))
{ {
if (file.DateAdded < DateTime.Today.AddDays(-7)) if (file.DateAdded < DateTime.Today.AddDays(-7))
{ {

View File

@ -7,12 +7,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
public class UpgradeDiskSpecification : IDecisionEngineSpecification public class UpgradeDiskSpecification : IDecisionEngineSpecification
{ {
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly UpgradableSpecification _upgradableSpecification;
private readonly Logger _logger; private readonly Logger _logger;
public UpgradeDiskSpecification(QualityUpgradableSpecification qualityUpgradableSpecification, Logger logger) public UpgradeDiskSpecification(UpgradableSpecification upgradableSpecification, Logger logger)
{ {
_qualityUpgradableSpecification = qualityUpgradableSpecification; _upgradableSpecification = upgradableSpecification;
_logger = logger; _logger = logger;
} }
@ -29,11 +29,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
continue; continue;
} }
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality); _logger.Debug("Comparing file quality and language with report. Existing file is {0} - {1}", file.Quality, file.Language);
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, file.Quality, subject.ParsedEpisodeInfo.Quality))
if (!_upgradableSpecification.IsUpgradable(subject.Series.Profile,
subject.Series.LanguageProfile,
file.Quality,
file.Language,
subject.ParsedEpisodeInfo.Quality,
subject.ParsedEpisodeInfo.Language))
{ {
return Decision.Reject("Quality for existing file on disk is of equal or higher preference: {0}", file.Quality); return Decision.Reject("Quality for existing file on disk is of equal or higher preference: {0} - {1}", file.Quality, file.Language);
} }
} }

View File

@ -0,0 +1,128 @@
using NLog;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine
{
public interface IUpgradableSpecification
{
bool IsUpgradable(Profile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, QualityModel newQuality, Language newLanguage);
bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
bool LanguageCutoffNotMet(LanguageProfile languageProfile, Language currentLanguage);
bool CutoffNotMet(Profile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, QualityModel newQuality = null);
bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality);
}
public class UpgradableSpecification : IUpgradableSpecification
{
private readonly Logger _logger;
public UpgradableSpecification(Logger logger)
{
_logger = logger;
}
private bool IsLanguageUpgradable(LanguageProfile profile, Language currentLanguage, Language newLanguage = null)
{
if (newLanguage != null)
{
var compare = new LanguageComparer(profile).Compare(newLanguage, currentLanguage);
if (compare <= 0)
{
return false;
}
}
return true;
}
private bool IsQualityUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
{
if (newQuality != null)
{
var compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality);
if (compare <= 0)
{
_logger.Debug("existing item has better quality. skipping");
return false;
}
}
return true;
}
public bool IsUpgradable(Profile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, QualityModel newQuality, Language newLanguage)
{
// If qualities are the same then check language
if (newQuality != null && currentQuality == newQuality)
{
return IsLanguageUpgradable(languageProfile, currentLanguage, newLanguage);
}
// If quality is worse then always return false
if (!IsQualityUpgradable(profile, currentQuality, newQuality))
{
_logger.Debug("existing item has better quality. skipping");
return false;
}
return true;
}
public bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
{
var qualityCompare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff);
if (qualityCompare < 0)
{
return true;
}
if (qualityCompare == 0 && newQuality != null && IsRevisionUpgrade(currentQuality, newQuality))
{
return true;
}
return false;
}
public bool LanguageCutoffNotMet(LanguageProfile languageProfile, Language currentLanguage)
{
var languageCompare = new LanguageComparer(languageProfile).Compare(currentLanguage, languageProfile.Cutoff);
return languageCompare < 0;
}
public bool CutoffNotMet(Profile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, QualityModel newQuality = null)
{
// If we can upgrade the language (it is not the cutoff) then doesn't matter the quality we can always get same quality with prefered language
if (LanguageCutoffNotMet(languageProfile, currentLanguage))
{
return true;
}
if (QualityCutoffNotMet(profile, currentQuality, newQuality))
{
return true;
}
_logger.Debug("Existing item meets cut-off. skipping.");
return false;
}
public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality)
{
var compare = newQuality.Revision.CompareTo(currentQuality.Revision);
if (currentQuality.Quality == newQuality.Quality && compare > 0)
{
_logger.Debug("New quality is a better revision for existing quality");
return true;
}
return false;
}
}
}

View File

@ -2,6 +2,7 @@
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Download
{ {
@ -21,5 +22,6 @@ namespace NzbDrone.Core.Download
public string Message { get; set; } public string Message { get; set; }
public Dictionary<string, string> Data { get; set; } public Dictionary<string, string> Data { get; set; }
public TrackedDownload TrackedDownload { get; set; } public TrackedDownload TrackedDownload { get; set; }
public Language Language { get; set; }
} }
} }

View File

@ -95,7 +95,8 @@ namespace NzbDrone.Core.Download
DownloadId = historyItem.DownloadId, DownloadId = historyItem.DownloadId,
Message = message, Message = message,
Data = historyItem.Data, Data = historyItem.Data,
TrackedDownload = trackedDownload TrackedDownload = trackedDownload,
Language = historyItem.Language
}; };
_eventAggregator.PublishEvent(downloadFailedEvent); _eventAggregator.PublishEvent(downloadFailedEvent);

View File

@ -1,5 +1,5 @@
using NzbDrone.Core.Extras.Files; using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.Parser; using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Extras.Subtitles namespace NzbDrone.Core.Extras.Subtitles
{ {

View File

@ -7,6 +7,7 @@ using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Extras.Files; using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History
{ {
@ -24,6 +25,7 @@ namespace NzbDrone.Core.History
public Series Series { get; set; } public Series Series { get; set; }
public HistoryEventType EventType { get; set; } public HistoryEventType EventType { get; set; }
public Dictionary<string, string> Data { get; set; } public Dictionary<string, string> Data { get; set; }
public Language Language { get; set; }
public string DownloadId { get; set; } public string DownloadId { get; set; }

View File

@ -11,7 +11,6 @@ namespace NzbDrone.Core.History
{ {
public interface IHistoryRepository : IBasicRepository<History> public interface IHistoryRepository : IBasicRepository<History>
{ {
List<QualityModel> GetBestQualityInHistory(int episodeId);
History MostRecentForEpisode(int episodeId); History MostRecentForEpisode(int episodeId);
List<History> FindByEpisodeId(int episodeId); List<History> FindByEpisodeId(int episodeId);
History MostRecentForDownloadId(string downloadId); History MostRecentForDownloadId(string downloadId);
@ -31,14 +30,6 @@ namespace NzbDrone.Core.History
{ {
} }
public List<QualityModel> GetBestQualityInHistory(int episodeId)
{
var history = Query.Where(c => c.EpisodeId == episodeId);
return history.Select(h => h.Quality).ToList();
}
public History MostRecentForEpisode(int episodeId) public History MostRecentForEpisode(int episodeId)
{ {
return Query.Where(h => h.EpisodeId == episodeId) return Query.Where(h => h.EpisodeId == episodeId)

View File

@ -11,15 +11,16 @@ using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events; using NzbDrone.Core.Tv.Events;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History
{ {
public interface IHistoryService public interface IHistoryService
{ {
QualityModel GetBestQualityInHistory(Profile profile, int episodeId);
PagingSpec<History> Paged(PagingSpec<History> pagingSpec); PagingSpec<History> Paged(PagingSpec<History> pagingSpec);
History MostRecentForEpisode(int episodeId); History MostRecentForEpisode(int episodeId);
List<History> FindByEpisodeId(int episodeId); List<History> FindByEpisodeId(int episodeId);
@ -94,14 +95,6 @@ namespace NzbDrone.Core.History
return _historyRepository.FindByDownloadId(downloadId); return _historyRepository.FindByDownloadId(downloadId);
} }
public QualityModel GetBestQualityInHistory(Profile profile, int episodeId)
{
var comparer = new QualityModelComparer(profile);
return _historyRepository.GetBestQualityInHistory(episodeId)
.OrderByDescending(q => q, comparer)
.FirstOrDefault();
}
private string FindDownloadId(EpisodeImportedEvent trackedDownload) private string FindDownloadId(EpisodeImportedEvent trackedDownload)
{ {
_logger.Debug("Trying to find downloadId for {0} from history", trackedDownload.ImportedEpisode.Path); _logger.Debug("Trying to find downloadId for {0} from history", trackedDownload.ImportedEpisode.Path);
@ -159,7 +152,8 @@ namespace NzbDrone.Core.History
SourceTitle = message.Episode.Release.Title, SourceTitle = message.Episode.Release.Title,
SeriesId = episode.SeriesId, SeriesId = episode.SeriesId,
EpisodeId = episode.Id, EpisodeId = episode.Id,
DownloadId = message.DownloadId DownloadId = message.DownloadId,
Language = message.Episode.ParsedEpisodeInfo.Language
}; };
history.Data.Add("Indexer", message.Episode.Release.Indexer); history.Data.Add("Indexer", message.Episode.Release.Indexer);
@ -217,7 +211,8 @@ namespace NzbDrone.Core.History
SourceTitle = message.ImportedEpisode.SceneName ?? Path.GetFileNameWithoutExtension(message.EpisodeInfo.Path), SourceTitle = message.ImportedEpisode.SceneName ?? Path.GetFileNameWithoutExtension(message.EpisodeInfo.Path),
SeriesId = message.ImportedEpisode.SeriesId, SeriesId = message.ImportedEpisode.SeriesId,
EpisodeId = episode.Id, EpisodeId = episode.Id,
DownloadId = downloadId DownloadId = downloadId,
Language = message.EpisodeInfo.Language
}; };
//Won't have a value since we publish this event before saving to DB. //Won't have a value since we publish this event before saving to DB.
@ -242,7 +237,8 @@ namespace NzbDrone.Core.History
SourceTitle = message.SourceTitle, SourceTitle = message.SourceTitle,
SeriesId = message.SeriesId, SeriesId = message.SeriesId,
EpisodeId = episodeId, EpisodeId = episodeId,
DownloadId = message.DownloadId DownloadId = message.DownloadId,
Language = message.Language
}; };
history.Data.Add("DownloadClient", message.DownloadClient); history.Data.Add("DownloadClient", message.DownloadClient);

View File

@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
// For some unknown reason series added through the v2 API can be added without a lanuage profile ID, which breaks things later.
// This ensures there is a language profile ID and it's valid as a safety net.
public class EnsureValidLanguageProfileId : IHousekeepingTask
{
private readonly ISeriesRepository _seriesRepository;
private readonly ILanguageProfileService _languageProfileService;
public EnsureValidLanguageProfileId(ISeriesRepository seriesRepository, ILanguageProfileService languageProfileService)
{
_seriesRepository = seriesRepository;
_languageProfileService = languageProfileService;
}
public void Clean()
{
var languageProfiles = _languageProfileService.All();
var firstLangaugeProfile = languageProfiles.First();
var series = _seriesRepository.All().ToList();
var seriesToUpdate = new List<Series>();
series.ForEach(s =>
{
if (s.LanguageProfileId == 0 || languageProfiles.None(l => l.Id == s.LanguageProfileId))
{
s.LanguageProfileId = firstLangaugeProfile.Id;
seriesToUpdate.Add(s);
}
});
_seriesRepository.UpdateMany(seriesToUpdate);
}
}
}

View File

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Languages
{
public class Language : IEmbeddedDocument, IEquatable<Language>
{
public int Id { get; set; }
public string Name { get; set; }
public Language()
{
}
private Language(int id, string name)
{
Id = id;
Name = name;
}
public override string ToString()
{
return Name;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
public bool Equals(Language other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id.Equals(other.Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj as Language);
}
public static bool operator ==(Language left, Language right)
{
return Equals(left, right);
}
public static bool operator !=(Language left, Language right)
{
return !Equals(left, right);
}
public static Language Unknown { get { return new Language(0, "Unknown"); } }
public static Language English { get { return new Language(1, "English"); } }
public static Language French { get { return new Language(2, "French"); } }
public static Language Spanish { get { return new Language(3, "Spanish"); } }
public static Language German { get { return new Language(4, "German"); } }
public static Language Italian { get { return new Language(5, "Italian"); } }
public static Language Danish { get { return new Language(6, "Danish"); } }
public static Language Dutch { get { return new Language(7, "Dutch"); } }
public static Language Japanese { get { return new Language(8, "Japanese"); } }
public static Language Cantonese { get { return new Language(9, "Cantonese"); } }
public static Language Mandarin { get { return new Language(10, "Mandarin"); } }
public static Language Russian { get { return new Language(11, "Russian"); } }
public static Language Polish { get { return new Language(12, "Polish"); } }
public static Language Vietnamese { get { return new Language(13, "Vietnamese"); } }
public static Language Swedish { get { return new Language(14, "Swedish"); } }
public static Language Norwegian { get { return new Language(15, "Norwegian"); } }
public static Language Finnish { get { return new Language(16, "Finnish"); } }
public static Language Turkish { get { return new Language(17, "Turkish"); } }
public static Language Portuguese { get { return new Language(18, "Portuguese"); } }
public static Language Flemish { get { return new Language(19, "Flemish"); } }
public static Language Greek { get { return new Language(20, "Greek"); } }
public static Language Korean { get { return new Language(21, "Korean"); } }
public static Language Hungarian { get { return new Language(22, "Hungarian"); } }
public static Language Hebrew { get { return new Language(23, "Hebrew"); } }
public static Language Lithuanian { get { return new Language(24, "Lithuanian"); } }
public static Language Czech { get { return new Language(25, "Czech"); } }
public static List<Language> All
{
get
{
return new List<Language>
{
Unknown,
English,
French,
Spanish,
German,
Italian,
Danish,
Dutch,
Japanese,
Cantonese,
Mandarin,
Russian,
Polish,
Vietnamese,
Swedish,
Norwegian,
Finnish,
Turkish,
Portuguese,
Flemish,
Greek,
Korean,
Hungarian,
Hebrew,
Lithuanian,
Czech
};
}
}
public static Language FindById(int id)
{
if (id == 0) return Unknown;
Language language = All.FirstOrDefault(v => v.Id == id);
if (language == null)
{
throw new ArgumentException("ID does not match a known language", nameof(id));
}
return language;
}
public static explicit operator Language(int id)
{
return FindById(id);
}
public static explicit operator int(Language language)
{
return language.Id;
}
public static explicit operator Language(string lang)
{
var language = All.FirstOrDefault(v => v.Name.Equals(lang, StringComparison.InvariantCultureIgnoreCase));
if (language == null)
{
throw new ArgumentException("Language does not match a known language", nameof(lang));
}
return language;
}
}
}

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Core.Profiles.Languages;
namespace NzbDrone.Core.Languages
{
public class LanguageComparer : IComparer<Language>
{
private readonly LanguageProfile _profile;
public LanguageComparer(LanguageProfile profile)
{
Ensure.That(profile, () => profile).IsNotNull();
Ensure.That(profile.Languages, () => profile.Languages).HasItems();
_profile = profile;
}
public int Compare(Language left, Language right)
{
int leftIndex = _profile.Languages.FindIndex(v => v.Language == left);
int rightIndex = _profile.Languages.FindIndex(v => v.Language == right);
return leftIndex.CompareTo(rightIndex);
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.Languages
{
public class LanguagesBelowCutoff
{
public int ProfileId { get; set; }
public IEnumerable<int> LanguageIds { get; set; }
public LanguagesBelowCutoff(int profileId, IEnumerable<int> languageIds)
{
ProfileId = profileId;
LanguageIds = languageIds;
}
}
}

View File

@ -6,6 +6,7 @@ using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.MediaFiles namespace NzbDrone.Core.MediaFiles
{ {
@ -24,6 +25,7 @@ namespace NzbDrone.Core.MediaFiles
public MediaInfoModel MediaInfo { get; set; } public MediaInfoModel MediaInfo { get; set; }
public LazyLoaded<List<Episode>> Episodes { get; set; } public LazyLoaded<List<Episode>> Episodes { get; set; }
public LazyLoaded<Series> Series { get; set; } public LazyLoaded<Series> Series { get; set; }
public Language Language { get; set; }
public override string ToString() public override string ToString()
{ {

View File

@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators
{
public class AggregateLanguage : IAggregateLocalEpisode
{
private readonly Logger _logger;
public AggregateLanguage(Logger logger)
{
_logger = logger;
}
public LocalEpisode Aggregate(LocalEpisode localEpisode, bool otherFiles)
{
// Get languages in preferred order, download client item, folder and finally file.
// Non-English languages will be preferred later, in the event there is a conflict
// between parsed languages the more preferred item will be used.
var languages = new List<Language>
{
GetLanguage(localEpisode.DownloadClientEpisodeInfo),
GetLanguage(localEpisode.FolderEpisodeInfo),
GetLanguage(localEpisode.FileEpisodeInfo)
};
var language = languages.FirstOrDefault(l => l != Language.English) ?? Language.English;
_logger.Debug("Using language: {0}", language);
localEpisode.Language = language;
return localEpisode;
}
private Language GetLanguage(ParsedEpisodeInfo parsedEpisodeInfo)
{
if (parsedEpisodeInfo == null)
{
// English is the default language when otherwise unknown
return Language.English;
}
return parsedEpisodeInfo.Language;
}
}
}

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Exceptions;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
@ -12,7 +13,7 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Extras; using NzbDrone.Core.Extras;
using NzbDrone.Common.Exceptions; using NzbDrone.Core.Languages;
namespace NzbDrone.Core.MediaFiles.EpisodeImport namespace NzbDrone.Core.MediaFiles.EpisodeImport
{ {
@ -50,6 +51,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
var qualifiedImports = decisions.Where(c => c.Approved) var qualifiedImports = decisions.Where(c => c.Approved)
.GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s .GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s
.OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.Profile)) .OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.Profile))
.ThenByDescending(c => c.LocalEpisode.Language, new LanguageComparer(s.First().LocalEpisode.Series.LanguageProfile))
.ThenByDescending(c => c.LocalEpisode.Size)) .ThenByDescending(c => c.LocalEpisode.Size))
.SelectMany(c => c) .SelectMany(c => c)
.ToList(); .ToList();
@ -84,6 +86,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
episodeFile.SeasonNumber = localEpisode.SeasonNumber; episodeFile.SeasonNumber = localEpisode.SeasonNumber;
episodeFile.Episodes = localEpisode.Episodes; episodeFile.Episodes = localEpisode.Episodes;
episodeFile.ReleaseGroup = localEpisode.ReleaseGroup; episodeFile.ReleaseGroup = localEpisode.ReleaseGroup;
episodeFile.Language = localEpisode.Language;
bool copyOnly; bool copyOnly;
switch (importMode) switch (importMode)

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;

View File

@ -4,6 +4,9 @@ using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Qualities;
using System.Collections.Generic;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
{ {
@ -19,9 +22,20 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
{ {
var qualityComparer = new QualityModelComparer(localEpisode.Series.Profile); var qualityComparer = new QualityModelComparer(localEpisode.Series.Profile);
var languageComparer = new LanguageComparer(localEpisode.Series.LanguageProfile);
var profile = localEpisode.Series.Profile.Value;
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0)) if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0))
{ {
_logger.Debug("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path); _logger.Debug("This file isn't a quality upgrade for all episodes. Skipping {0}", localEpisode.Path);
return Decision.Reject("Not an upgrade for existing episode file(s)");
}
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 &&
languageComparer.Compare(e.EpisodeFile.Value.Language, localEpisode.Language) > 0 &&
qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) == 0))
{
_logger.Debug("This file isn't a language upgrade for all episodes. Skipping {0}", localEpisode.Path);
return Decision.Reject("Not an upgrade for existing episode file(s)"); return Decision.Reject("Not an upgrade for existing episode file(s)");
} }

View File

@ -151,6 +151,10 @@
<Compile Include="CustomFilters\CustomFilterService.cs" /> <Compile Include="CustomFilters\CustomFilterService.cs" />
<Compile Include="Datastore\Migration\126_add_custom_filters.cs" /> <Compile Include="Datastore\Migration\126_add_custom_filters.cs" />
<Compile Include="Extras\Metadata\MetadataSectionType.cs" /> <Compile Include="Extras\Metadata\MetadataSectionType.cs" />
<Compile Include="Download\Aggregation\RemoteEpisodeAggregationService.cs" />
<Compile Include="Download\Aggregation\Aggregators\AggregatePreferredWordScore.cs" />
<Compile Include="Download\Aggregation\Aggregators\IAggregateRemoteEpisode.cs" />
<Compile Include="Housekeeping\Housekeepers\EnsureValidLanguageProfileId.cs" />
<Compile Include="Indexers\SeedConfigProvider.cs" /> <Compile Include="Indexers\SeedConfigProvider.cs" />
<Compile Include="DataAugmentation\DailySeries\DailySeries.cs" /> <Compile Include="DataAugmentation\DailySeries\DailySeries.cs" />
<Compile Include="DataAugmentation\DailySeries\DailySeriesDataProxy.cs" /> <Compile Include="DataAugmentation\DailySeries\DailySeriesDataProxy.cs" />
@ -175,6 +179,7 @@
<Compile Include="Datastore\Converters\DoubleConverter.cs" /> <Compile Include="Datastore\Converters\DoubleConverter.cs" />
<Compile Include="Datastore\Converters\EmbeddedDocumentConverter.cs" /> <Compile Include="Datastore\Converters\EmbeddedDocumentConverter.cs" />
<Compile Include="Datastore\Converters\EnumIntConverter.cs" /> <Compile Include="Datastore\Converters\EnumIntConverter.cs" />
<Compile Include="Datastore\Converters\LanguageIntConverter.cs" />
<Compile Include="Datastore\Converters\TimeSpanConverter.cs" /> <Compile Include="Datastore\Converters\TimeSpanConverter.cs" />
<Compile Include="Datastore\Converters\Int32Converter.cs" /> <Compile Include="Datastore\Converters\Int32Converter.cs" />
<Compile Include="Datastore\Converters\GuidConverter.cs" /> <Compile Include="Datastore\Converters\GuidConverter.cs" />
@ -268,7 +273,6 @@
<Compile Include="Datastore\Migration\069_quality_proper.cs" /> <Compile Include="Datastore\Migration\069_quality_proper.cs" />
<Compile Include="Datastore\Migration\070_delay_profile.cs" /> <Compile Include="Datastore\Migration\070_delay_profile.cs" />
<Compile Include="Datastore\Migration\116_disable_nyaa.cs" /> <Compile Include="Datastore\Migration\116_disable_nyaa.cs" />
<Compile Include="Datastore\Migration\102_add_language_to_episodeFiles_history_and_blacklist.cs" />
<Compile Include="Datastore\Migration\113_consolidate_indexer_baseurl.cs" /> <Compile Include="Datastore\Migration\113_consolidate_indexer_baseurl.cs" />
<Compile Include="Datastore\Migration\114_rename_indexer_status_id.cs" /> <Compile Include="Datastore\Migration\114_rename_indexer_status_id.cs" />
<Compile Include="Datastore\Migration\112_added_regex_to_scenemapping.cs" /> <Compile Include="Datastore\Migration\112_added_regex_to_scenemapping.cs" />
@ -312,6 +316,7 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Datastore\Migration\105_rename_torrent_downloadstation.cs" /> <Compile Include="Datastore\Migration\105_rename_torrent_downloadstation.cs" />
<Compile Include="Datastore\Migration\102_add_language_to_episodeFiles_history_and_blacklist.cs" />
<Compile Include="Datastore\Migration\111_create_language_profiles.cs" /> <Compile Include="Datastore\Migration\111_create_language_profiles.cs" />
<Compile Include="Datastore\Migration\115_add_downloadclient_status.cs" /> <Compile Include="Datastore\Migration\115_add_downloadclient_status.cs" />
<Compile Include="Datastore\Migration\121_update_animetosho_url.cs" /> <Compile Include="Datastore\Migration\121_update_animetosho_url.cs" />
@ -339,7 +344,7 @@
<Compile Include="DecisionEngine\DownloadDecisionPriorizationService.cs" /> <Compile Include="DecisionEngine\DownloadDecisionPriorizationService.cs" />
<Compile Include="DecisionEngine\IDecisionEngineSpecification.cs" /> <Compile Include="DecisionEngine\IDecisionEngineSpecification.cs" />
<Compile Include="DecisionEngine\IRejectWithReason.cs" /> <Compile Include="DecisionEngine\IRejectWithReason.cs" />
<Compile Include="DecisionEngine\QualityUpgradableSpecification.cs" /> <Compile Include="DecisionEngine\UpgradableSpecification.cs" />
<Compile Include="DecisionEngine\Rejection.cs" /> <Compile Include="DecisionEngine\Rejection.cs" />
<Compile Include="DecisionEngine\RejectionType.cs" /> <Compile Include="DecisionEngine\RejectionType.cs" />
<Compile Include="DecisionEngine\SameEpisodesSpecification.cs" /> <Compile Include="DecisionEngine\SameEpisodesSpecification.cs" />
@ -769,6 +774,12 @@
<Compile Include="Jobs\ScheduledTask.cs" /> <Compile Include="Jobs\ScheduledTask.cs" />
<Compile Include="Jobs\Scheduler.cs" /> <Compile Include="Jobs\Scheduler.cs" />
<Compile Include="Jobs\TaskManager.cs" /> <Compile Include="Jobs\TaskManager.cs" />
<Compile Include="Languages\Language.cs" />
<Compile Include="Languages\LanguageComparer.cs" />
<Compile Include="Languages\LanguagesBelowCutoff.cs" />
<Compile Include="MediaFiles\EpisodeImport\Aggregation\Aggregators\AggregateLanguage.cs" />
<Compile Include="Profiles\Languages\LanguageProfile.cs" />
<Compile Include="Profiles\Languages\LanguageProfileInUseException.cs" />
<Compile Include="Lifecycle\ApplicationShutdownRequested.cs" /> <Compile Include="Lifecycle\ApplicationShutdownRequested.cs" />
<Compile Include="Lifecycle\ApplicationStartedEvent.cs" /> <Compile Include="Lifecycle\ApplicationStartedEvent.cs" />
<Compile Include="Lifecycle\Commands\RestartCommand.cs" /> <Compile Include="Lifecycle\Commands\RestartCommand.cs" />
@ -985,7 +996,13 @@
<Compile Include="Profiles\Delay\DelayProfile.cs" /> <Compile Include="Profiles\Delay\DelayProfile.cs" />
<Compile Include="Profiles\Delay\DelayProfileService.cs" /> <Compile Include="Profiles\Delay\DelayProfileService.cs" />
<Compile Include="Profiles\Delay\DelayProfileTagInUseValidator.cs" /> <Compile Include="Profiles\Delay\DelayProfileTagInUseValidator.cs" />
<Compile Include="Profiles\ProfileRepository.cs" /> <Compile Include="Profiles\Languages\LanguageProfileItem.cs" />
<Compile Include="Profiles\Languages\LanguageProfileRepository.cs" />
<Compile Include="Profiles\Qualities\Profile.cs" />
<Compile Include="Profiles\Qualities\ProfileInUseException.cs" />
<Compile Include="Profiles\Qualities\ProfileQualityItem.cs" />
<Compile Include="Profiles\Qualities\ProfileRepository.cs" />
<Compile Include="Profiles\Qualities\ProfileService.cs" />
<Compile Include="ProgressMessaging\ProgressMessageContext.cs" /> <Compile Include="ProgressMessaging\ProgressMessageContext.cs" />
<Compile Include="Qualities\QualityDetectionSource.cs" /> <Compile Include="Qualities\QualityDetectionSource.cs" />
<Compile Include="Qualities\QualitySource.cs" /> <Compile Include="Qualities\QualitySource.cs" />
@ -1097,7 +1114,6 @@
<Compile Include="Organizer\NamingConfigService.cs" /> <Compile Include="Organizer\NamingConfigService.cs" />
<Compile Include="Organizer\SampleResult.cs" /> <Compile Include="Organizer\SampleResult.cs" />
<Compile Include="Parser\InvalidDateException.cs" /> <Compile Include="Parser\InvalidDateException.cs" />
<Compile Include="Parser\Language.cs" />
<Compile Include="Parser\Model\LocalEpisode.cs" /> <Compile Include="Parser\Model\LocalEpisode.cs" />
<Compile Include="Parser\Model\ParsedEpisodeInfo.cs" /> <Compile Include="Parser\Model\ParsedEpisodeInfo.cs" />
<Compile Include="Parser\Model\ReleaseInfo.cs" /> <Compile Include="Parser\Model\ReleaseInfo.cs" />
@ -1108,11 +1124,8 @@
<Compile Include="Parser\ParsingService.cs" /> <Compile Include="Parser\ParsingService.cs" />
<Compile Include="Parser\SceneChecker.cs" /> <Compile Include="Parser\SceneChecker.cs" />
<Compile Include="Parser\QualityParser.cs" /> <Compile Include="Parser\QualityParser.cs" />
<Compile Include="Profiles\Profile.cs" />
<Compile Include="Profiles\ProfileInUseException.cs" />
<Compile Include="Profiles\ProfileQualityItem.cs" />
<Compile Include="Profiles\Delay\DelayProfileRepository.cs" /> <Compile Include="Profiles\Delay\DelayProfileRepository.cs" />
<Compile Include="Profiles\ProfileService.cs" /> <Compile Include="Profiles\Languages\LanguageProfileService.cs" />
<Compile Include="ProgressMessaging\CommandUpdatedEvent.cs" /> <Compile Include="ProgressMessaging\CommandUpdatedEvent.cs" />
<Compile Include="ProgressMessaging\ProgressMessageTarget.cs" /> <Compile Include="ProgressMessaging\ProgressMessageTarget.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@ -1229,7 +1242,6 @@
<Compile Include="Update\UpdateVerificationFailedException.cs" /> <Compile Include="Update\UpdateVerificationFailedException.cs" />
<Compile Include="Validation\FolderValidator.cs" /> <Compile Include="Validation\FolderValidator.cs" />
<Compile Include="Validation\IpValidation.cs" /> <Compile Include="Validation\IpValidation.cs" />
<Compile Include="Validation\LanguageValidator.cs" />
<Compile Include="Validation\NzbDroneValidationExtensions.cs" /> <Compile Include="Validation\NzbDroneValidationExtensions.cs" />
<Compile Include="Validation\NzbDroneValidationFailure.cs" /> <Compile Include="Validation\NzbDroneValidationFailure.cs" />
<Compile Include="Validation\NzbDroneValidationResult.cs" /> <Compile Include="Validation\NzbDroneValidationResult.cs" />
@ -1245,6 +1257,7 @@
<Compile Include="Validation\Paths\SeriesAncestorValidator.cs" /> <Compile Include="Validation\Paths\SeriesAncestorValidator.cs" />
<Compile Include="Validation\Paths\SeriesExistsValidator.cs" /> <Compile Include="Validation\Paths\SeriesExistsValidator.cs" />
<Compile Include="Validation\Paths\SeriesPathValidator.cs" /> <Compile Include="Validation\Paths\SeriesPathValidator.cs" />
<Compile Include="Validation\LanguageProfileExistsValidator.cs" />
<Compile Include="Validation\ProfileExistsValidator.cs" /> <Compile Include="Validation\ProfileExistsValidator.cs" />
<Compile Include="Validation\RuleBuilderExtensions.cs" /> <Compile Include="Validation\RuleBuilderExtensions.cs" />
<Compile Include="Validation\UrlValidator.cs" /> <Compile Include="Validation\UrlValidator.cs" />

View File

@ -1,4 +1,6 @@
namespace NzbDrone.Core.Parser using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser
{ {
public class IsoLanguage public class IsoLanguage
{ {

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser namespace NzbDrone.Core.Parser
{ {

View File

@ -1,32 +0,0 @@
namespace NzbDrone.Core.Parser
{
public enum Language
{
Unknown = 0,
English = 1,
French = 2,
Spanish = 3,
German = 4,
Italian = 5,
Danish = 6,
Dutch = 7,
Japanese = 8,
Cantonese = 9,
Mandarin = 10,
Russian = 11,
Polish = 12,
Vietnamese = 13,
Swedish = 14,
Norwegian = 15,
Finnish = 16,
Turkish = 17,
Portuguese = 18,
Flemish = 19,
Greek = 20,
Korean = 21,
Hungarian = 22,
Hebrew = 23,
Lithuanian = 24,
Czech = 25
}
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser namespace NzbDrone.Core.Parser
{ {

View File

@ -4,6 +4,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser.Model namespace NzbDrone.Core.Parser.Model
{ {
@ -22,6 +23,7 @@ namespace NzbDrone.Core.Parser.Model
public Series Series { get; set; } public Series Series { get; set; }
public List<Episode> Episodes { get; set; } public List<Episode> Episodes { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public Language Language { get; set; }
public MediaInfoModel MediaInfo { get; set; } public MediaInfoModel MediaInfo { get; set; }
public bool ExistingFile { get; set; } public bool ExistingFile { get; set; }
public bool SceneSource { get; set; } public bool SceneSource { get; set; }

View File

@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser.Model namespace NzbDrone.Core.Parser.Model
{ {

View File

@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser namespace NzbDrone.Core.Parser
{ {
@ -322,6 +323,9 @@ namespace NzbDrone.Core.Parser
private static readonly Regex AnimeReleaseGroupRegex = new Regex(@"^(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)", private static readonly Regex AnimeReleaseGroupRegex = new Regex(@"^(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)",
RegexOptions.IgnoreCase | RegexOptions.Compiled); RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VOSTFR)(?:\W|_))|(?<russian>\brus\b)|(?<dutch>nl\W?subs?)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<spanish>\b(?:español|castellano)\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex YearInTitleRegex = new Regex(@"^(?<title>.+?)(?:\W|_)?(?<year>\d{4})", private static readonly Regex YearInTitleRegex = new Regex(@"^(?<title>.+?)(?:\W|_)?(?<year>\d{4})",
RegexOptions.IgnoreCase | RegexOptions.Compiled); RegexOptions.IgnoreCase | RegexOptions.Compiled);
@ -575,6 +579,99 @@ namespace NzbDrone.Core.Parser
return title; return title;
} }
public static Language ParseLanguage(string title)
{
var lowerTitle = title.ToLower();
if (lowerTitle.Contains("english"))
return Language.English;
if (lowerTitle.Contains("french"))
return Language.French;
if (lowerTitle.Contains("spanish"))
return Language.Spanish;
if (lowerTitle.Contains("danish"))
return Language.Danish;
if (lowerTitle.Contains("dutch"))
return Language.Dutch;
if (lowerTitle.Contains("japanese"))
return Language.Japanese;
if (lowerTitle.Contains("cantonese"))
return Language.Cantonese;
if (lowerTitle.Contains("mandarin"))
return Language.Mandarin;
if (lowerTitle.Contains("korean"))
return Language.Korean;
if (lowerTitle.Contains("russian"))
return Language.Russian;
if (lowerTitle.Contains("polish"))
return Language.Polish;
if (lowerTitle.Contains("vietnamese"))
return Language.Vietnamese;
if (lowerTitle.Contains("swedish"))
return Language.Swedish;
if (lowerTitle.Contains("norwegian"))
return Language.Norwegian;
if (lowerTitle.Contains("nordic"))
return Language.Norwegian;
if (lowerTitle.Contains("finnish"))
return Language.Finnish;
if (lowerTitle.Contains("turkish"))
return Language.Turkish;
if (lowerTitle.Contains("portuguese"))
return Language.Portuguese;
if (lowerTitle.Contains("hungarian"))
return Language.Hungarian;
var match = LanguageRegex.Match(title);
if (match.Groups["italian"].Captures.Cast<Capture>().Any())
return Language.Italian;
if (match.Groups["german"].Captures.Cast<Capture>().Any())
return Language.German;
if (match.Groups["flemish"].Captures.Cast<Capture>().Any())
return Language.Flemish;
if (match.Groups["greek"].Captures.Cast<Capture>().Any())
return Language.Greek;
if (match.Groups["spanish"].Captures.Cast<Capture>().Any())
return Language.Spanish;
if (match.Groups["french"].Success)
return Language.French;
if (match.Groups["russian"].Success)
return Language.Russian;
if (match.Groups["dutch"].Success)
return Language.Dutch;
if (match.Groups["hungarian"].Success)
return Language.Hungarian;
return Language.English;
}
private static SeriesTitleInfo GetSeriesTitleInfo(string title) private static SeriesTitleInfo GetSeriesTitleInfo(string title)
{ {
var seriesTitleInfo = new SeriesTitleInfo(); var seriesTitleInfo = new SeriesTitleInfo();

Some files were not shown because too many files have changed in this diff Show More