mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-10-30 15:32:31 +01:00
Profiles
Indexes are created with the same uniqueness when copying a table New: Non-English episode support New: Renamed Quality Profiles to Profiles and made them more powerful New: Configurable wait time before grabbing a release to wait for a better quality
This commit is contained in:
parent
b72678a9ad
commit
74a38415cf
@ -11,15 +11,16 @@ using NzbDrone.Api.History;
|
||||
using NzbDrone.Api.Indexers;
|
||||
using NzbDrone.Api.Logs;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Api.Qualities;
|
||||
using NzbDrone.Api.Profiles;
|
||||
using NzbDrone.Api.RootFolders;
|
||||
using NzbDrone.Api.Series;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Instrumentation;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -41,8 +42,8 @@ namespace NzbDrone.Api.Test.MappingTests
|
||||
[TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))]
|
||||
[TestCase(typeof(DownloadDecision), typeof(ReleaseResource))]
|
||||
[TestCase(typeof(Core.History.History), typeof(HistoryResource))]
|
||||
[TestCase(typeof(QualityProfile), typeof(QualityProfileResource))]
|
||||
[TestCase(typeof(QualityProfileItem), typeof(QualityProfileItemResource))]
|
||||
[TestCase(typeof(Profile), typeof(ProfileResource))]
|
||||
[TestCase(typeof(ProfileQualityItem), typeof(ProfileQualityItemResource))]
|
||||
[TestCase(typeof(Log), typeof(LogResource))]
|
||||
[TestCase(typeof(Command), typeof(CommandResource))]
|
||||
public void matching_fields(Type modelType, Type resourceType)
|
||||
@ -105,16 +106,16 @@ namespace NzbDrone.Api.Test.MappingTests
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_map_qualityprofile()
|
||||
public void should_map_profile()
|
||||
{
|
||||
var profileResource = new QualityProfileResource
|
||||
var profileResource = new ProfileResource
|
||||
{
|
||||
Cutoff = Quality.WEBDL1080p,
|
||||
Items = new List<QualityProfileItemResource> { new QualityProfileItemResource { Quality = Quality.WEBDL1080p, Allowed = true } }
|
||||
Items = new List<ProfileQualityItemResource> { new ProfileQualityItemResource { Quality = Quality.WEBDL1080p, Allowed = true } }
|
||||
};
|
||||
|
||||
|
||||
profileResource.InjectTo<QualityProfile>();
|
||||
profileResource.InjectTo<Profile>();
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ using Nancy;
|
||||
using NLog;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.Indexers;
|
||||
@ -15,7 +14,6 @@ using Omu.ValueInjecter;
|
||||
using System.Linq;
|
||||
using Nancy.ModelBinding;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
@ -106,14 +104,14 @@ namespace NzbDrone.Api.Indexers
|
||||
release.InjectFrom(downloadDecision.RemoteEpisode.Release);
|
||||
release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo);
|
||||
release.InjectFrom(downloadDecision);
|
||||
release.Rejections = downloadDecision.Rejections.ToList();
|
||||
release.Rejections = downloadDecision.Rejections.Select(r => r.Reason).ToList();
|
||||
release.DownloadAllowed = downloadDecision.RemoteEpisode.DownloadAllowed;
|
||||
|
||||
release.ReleaseWeight = result.Count;
|
||||
|
||||
if (downloadDecision.RemoteEpisode.Series != null)
|
||||
{
|
||||
release.QualityWeight = downloadDecision.RemoteEpisode.Series.QualityProfile.Value.Items.FindIndex(v => v.Quality == release.Quality.Quality) * 2;
|
||||
release.QualityWeight = downloadDecision.RemoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == release.Quality.Quality) * 2;
|
||||
}
|
||||
|
||||
if (!release.Quality.Proper)
|
||||
|
@ -28,6 +28,8 @@ namespace NzbDrone.Api.Indexers
|
||||
public int[] EpisodeNumbers { get; set; }
|
||||
public int[] AbsoluteEpisodeNumbers { get; set; }
|
||||
public Boolean Approved { get; set; }
|
||||
public Boolean TemporarilyRejected { get; set; }
|
||||
public Boolean Rejected { get; set; }
|
||||
public Int32 TvRageId { get; set; }
|
||||
public IEnumerable<String> Rejections { get; set; }
|
||||
public DateTime PublishDate { get; set; }
|
||||
|
@ -150,6 +150,9 @@
|
||||
<Compile Include="Metadata\MetadataResource.cs" />
|
||||
<Compile Include="Metadata\MetadataModule.cs" />
|
||||
<Compile Include="NzbDroneFeedModule.cs" />
|
||||
<Compile Include="Profiles\Languages\LanguageModule.cs" />
|
||||
<Compile Include="Profiles\Languages\LanguageResource.cs" />
|
||||
<Compile Include="Profiles\LegacyProfileModule.cs" />
|
||||
<Compile Include="ProviderResource.cs" />
|
||||
<Compile Include="ProviderModuleBase.cs" />
|
||||
<Compile Include="Indexers\IndexerModule.cs" />
|
||||
@ -175,7 +178,7 @@
|
||||
<Compile Include="Wanted\MissingModule.cs" />
|
||||
<Compile Include="Config\NamingSampleResource.cs" />
|
||||
<Compile Include="NzbDroneRestModuleWithSignalR.cs" />
|
||||
<Compile Include="Qualities\QualityProfileValidation.cs" />
|
||||
<Compile Include="Profiles\ProfileValidation.cs" />
|
||||
<Compile Include="Queue\QueueModule.cs" />
|
||||
<Compile Include="Queue\QueueResource.cs" />
|
||||
<Compile Include="ResourceChangeMessage.cs" />
|
||||
@ -183,7 +186,7 @@
|
||||
<Compile Include="Notifications\NotificationResource.cs" />
|
||||
<Compile Include="NzbDroneRestModule.cs" />
|
||||
<Compile Include="PagingResource.cs" />
|
||||
<Compile Include="Qualities\QualityProfileSchemaModule.cs" />
|
||||
<Compile Include="Profiles\ProfileSchemaModule.cs" />
|
||||
<Compile Include="REST\BadRequestException.cs" />
|
||||
<Compile Include="REST\ResourceValidator.cs" />
|
||||
<Compile Include="REST\RestModule.cs" />
|
||||
@ -202,8 +205,8 @@
|
||||
<Compile Include="Exceptions\InvalidApiKeyException.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="NzbDroneApiModule.cs" />
|
||||
<Compile Include="Qualities\QualityProfileResource.cs" />
|
||||
<Compile Include="Qualities\QualityProfileModule.cs" />
|
||||
<Compile Include="Profiles\ProfileResource.cs" />
|
||||
<Compile Include="Profiles\ProfileModule.cs" />
|
||||
<Compile Include="Qualities\QualityDefinitionResource.cs" />
|
||||
<Compile Include="Qualities\QualityDefinitionModule.cs" />
|
||||
<Compile Include="Extensions\ReqResExtensions.cs" />
|
||||
|
39
src/NzbDrone.Api/Profiles/Languages/LanguageModule.cs
Normal file
39
src/NzbDrone.Api/Profiles/Languages/LanguageModule.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Api.Profiles.Languages
|
||||
{
|
||||
public class LanguageModule : NzbDroneRestModule<LanguageResource>
|
||||
{
|
||||
public LanguageModule()
|
||||
{
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetById;
|
||||
}
|
||||
|
||||
private LanguageResource GetById(int id)
|
||||
{
|
||||
var language = (Language)id;
|
||||
|
||||
return new LanguageResource
|
||||
{
|
||||
Id = (int)language,
|
||||
Name = language.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private List<LanguageResource> GetAll()
|
||||
{
|
||||
return ((Language[])Enum.GetValues(typeof (Language)))
|
||||
.Select(l => new LanguageResource
|
||||
{
|
||||
Id = (int) l,
|
||||
Name = l.ToString()
|
||||
})
|
||||
.OrderBy(l => l.Name)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
14
src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs
Normal file
14
src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Api.REST;
|
||||
|
||||
namespace NzbDrone.Api.Profiles.Languages
|
||||
{
|
||||
public class LanguageResource : RestResource
|
||||
{
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
|
||||
public Int32 Id { get; set; }
|
||||
public String Name { get; set; }
|
||||
public String NameLower { get { return Name.ToLowerInvariant(); } }
|
||||
}
|
||||
}
|
36
src/NzbDrone.Api/Profiles/LegacyProfileModule.cs
Normal file
36
src/NzbDrone.Api/Profiles/LegacyProfileModule.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Nancy;
|
||||
using Nancy.Responses;
|
||||
|
||||
namespace NzbDrone.Api.Profiles
|
||||
{
|
||||
class LegacyProfileModule : NzbDroneApiModule
|
||||
{
|
||||
public LegacyProfileModule()
|
||||
: base("qualityprofile")
|
||||
{
|
||||
Get["/"] = x =>
|
||||
{
|
||||
string queryString = ConvertQueryParams(Request.Query);
|
||||
var url = String.Format("/api/profile?{0}", queryString);
|
||||
|
||||
return Response.AsRedirect(url, RedirectResponse.RedirectType.Permanent);
|
||||
};
|
||||
}
|
||||
|
||||
private string ConvertQueryParams(DynamicDictionary query)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (var key in query)
|
||||
{
|
||||
var value = query[key];
|
||||
|
||||
sb.AppendFormat("&{0}={1}", key, value);
|
||||
}
|
||||
|
||||
return sb.ToString().Trim('&');
|
||||
}
|
||||
}
|
||||
}
|
67
src/NzbDrone.Api/Profiles/ProfileModule.cs
Normal file
67
src/NzbDrone.Api/Profiles/ProfileModule.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Api.Profiles
|
||||
{
|
||||
public class ProfileModule : NzbDroneRestModule<ProfileResource>
|
||||
{
|
||||
private readonly IProfileService _profileService;
|
||||
|
||||
public ProfileModule(IProfileService profileService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.Cutoff).NotNull();
|
||||
SharedValidator.RuleFor(c => c.Items).MustHaveAllowedQuality();
|
||||
SharedValidator.RuleFor(c => c.Language).ValidLanguage();
|
||||
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetById;
|
||||
UpdateResource = Update;
|
||||
CreateResource = Create;
|
||||
DeleteResource = DeleteProfile;
|
||||
}
|
||||
|
||||
private int Create(ProfileResource resource)
|
||||
{
|
||||
var model = resource.InjectTo<Profile>();
|
||||
model = _profileService.Add(model);
|
||||
return model.Id;
|
||||
}
|
||||
|
||||
private void DeleteProfile(int id)
|
||||
{
|
||||
_profileService.Delete(id);
|
||||
}
|
||||
|
||||
private void Update(ProfileResource resource)
|
||||
{
|
||||
var model = _profileService.Get(resource.Id);
|
||||
|
||||
model.Name = resource.Name;
|
||||
model.Cutoff = (Quality)resource.Cutoff.Id;
|
||||
model.Items = resource.Items.InjectTo<List<ProfileQualityItem>>();
|
||||
model.Language = resource.Language;
|
||||
model.GrabDelay = resource.GrabDelay;
|
||||
model.GrabDelayMode = resource.GrabDelayMode;
|
||||
|
||||
_profileService.Update(model);
|
||||
}
|
||||
|
||||
private ProfileResource GetById(int id)
|
||||
{
|
||||
return _profileService.Get(id).InjectTo<ProfileResource>();
|
||||
}
|
||||
|
||||
private List<ProfileResource> GetAll()
|
||||
{
|
||||
var profiles = _profileService.All().InjectTo<List<ProfileResource>>();
|
||||
|
||||
return profiles;
|
||||
}
|
||||
}
|
||||
}
|
25
src/NzbDrone.Api/Profiles/ProfileResource.cs
Normal file
25
src/NzbDrone.Api/Profiles/ProfileResource.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Api.Profiles
|
||||
{
|
||||
public class ProfileResource : RestResource
|
||||
{
|
||||
public String Name { get; set; }
|
||||
public Quality Cutoff { get; set; }
|
||||
public List<ProfileQualityItemResource> Items { get; set; }
|
||||
public Language Language { get; set; }
|
||||
public Int32 GrabDelay { get; set; }
|
||||
public GrabDelayMode GrabDelayMode { get; set; }
|
||||
}
|
||||
|
||||
public class ProfileQualityItemResource : RestResource
|
||||
{
|
||||
public Quality Quality { get; set; }
|
||||
public bool Allowed { get; set; }
|
||||
}
|
||||
}
|
@ -1,34 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Api.Qualities
|
||||
namespace NzbDrone.Api.Profiles
|
||||
{
|
||||
public class QualityProfileSchemaModule : NzbDroneRestModule<QualityProfileResource>
|
||||
public class ProfileSchemaModule : NzbDroneRestModule<ProfileResource>
|
||||
{
|
||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
||||
|
||||
public QualityProfileSchemaModule(IQualityDefinitionService qualityDefinitionService)
|
||||
: base("/qualityprofile/schema")
|
||||
public ProfileSchemaModule(IQualityDefinitionService qualityDefinitionService)
|
||||
: base("/profile/schema")
|
||||
{
|
||||
_qualityDefinitionService = qualityDefinitionService;
|
||||
|
||||
GetResourceAll = GetAll;
|
||||
}
|
||||
|
||||
private List<QualityProfileResource> GetAll()
|
||||
private List<ProfileResource> GetAll()
|
||||
{
|
||||
var items = _qualityDefinitionService.All()
|
||||
.OrderBy(v => v.Weight)
|
||||
.Select(v => new QualityProfileItem { Quality = v.Quality, Allowed = false })
|
||||
.Select(v => new ProfileQualityItem { Quality = v.Quality, Allowed = false })
|
||||
.ToList();
|
||||
|
||||
var profile = new QualityProfile();
|
||||
var profile = new Profile();
|
||||
profile.Cutoff = Quality.Unknown;
|
||||
profile.Items = items;
|
||||
profile.Language = Language.English;
|
||||
|
||||
return new List<QualityProfileResource> { profile.InjectTo<QualityProfileResource>() };
|
||||
return new List<ProfileResource> { profile.InjectTo<ProfileResource>() };
|
||||
}
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@ using System.Linq;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Validators;
|
||||
|
||||
namespace NzbDrone.Api.Qualities
|
||||
namespace NzbDrone.Api.Profiles
|
||||
{
|
||||
public static class QualityProfileValidation
|
||||
public static class ProfileValidation
|
||||
{
|
||||
public static IRuleBuilderOptions<T, IList<QualityProfileItemResource>> MustHaveAllowedQuality<T>(this IRuleBuilder<T, IList<QualityProfileItemResource>> ruleBuilder)
|
||||
public static IRuleBuilderOptions<T, IList<ProfileQualityItemResource>> MustHaveAllowedQuality<T>(this IRuleBuilder<T, IList<ProfileQualityItemResource>> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
|
||||
@ -25,7 +25,7 @@ namespace NzbDrone.Api.Qualities
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var list = context.PropertyValue as IList<QualityProfileItemResource>;
|
||||
var list = context.PropertyValue as IList<ProfileQualityItemResource>;
|
||||
|
||||
if (list == null)
|
||||
{
|
@ -1,60 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using FluentValidation;
|
||||
|
||||
namespace NzbDrone.Api.Qualities
|
||||
{
|
||||
public class QualityProfileModule : NzbDroneRestModule<QualityProfileResource>
|
||||
{
|
||||
private readonly IQualityProfileService _qualityProfileService;
|
||||
|
||||
public QualityProfileModule(IQualityProfileService qualityProfileService)
|
||||
{
|
||||
_qualityProfileService = qualityProfileService;
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.Cutoff).NotNull();
|
||||
SharedValidator.RuleFor(c => c.Items).MustHaveAllowedQuality();//.SetValidator(new AllowedValidator<QualityProfileItemResource>());
|
||||
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetById;
|
||||
UpdateResource = Update;
|
||||
CreateResource = Create;
|
||||
DeleteResource = DeleteProfile;
|
||||
}
|
||||
|
||||
private int Create(QualityProfileResource resource)
|
||||
{
|
||||
var model = resource.InjectTo<QualityProfile>();
|
||||
model = _qualityProfileService.Add(model);
|
||||
return model.Id;
|
||||
}
|
||||
|
||||
private void DeleteProfile(int id)
|
||||
{
|
||||
_qualityProfileService.Delete(id);
|
||||
}
|
||||
|
||||
private void Update(QualityProfileResource resource)
|
||||
{
|
||||
var model = _qualityProfileService.Get(resource.Id);
|
||||
|
||||
model.Name = resource.Name;
|
||||
model.Cutoff = (Quality)resource.Cutoff.Id;
|
||||
model.Items = resource.Items.InjectTo<List<QualityProfileItem>>();
|
||||
_qualityProfileService.Update(model);
|
||||
}
|
||||
|
||||
private QualityProfileResource GetById(int id)
|
||||
{
|
||||
return _qualityProfileService.Get(id).InjectTo<QualityProfileResource>();
|
||||
}
|
||||
|
||||
private List<QualityProfileResource> GetAll()
|
||||
{
|
||||
var profiles = _qualityProfileService.All().InjectTo<List<QualityProfileResource>>();
|
||||
|
||||
return profiles;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Api.Qualities
|
||||
{
|
||||
public class QualityProfileResource : RestResource
|
||||
{
|
||||
public String Name { get; set; }
|
||||
public Quality Cutoff { get; set; }
|
||||
public List<QualityProfileItemResource> Items { get; set; }
|
||||
}
|
||||
|
||||
public class QualityProfileItemResource : RestResource
|
||||
{
|
||||
public Quality Quality { get; set; }
|
||||
public bool Allowed { get; set; }
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Queue;
|
||||
@ -7,25 +9,40 @@ using NzbDrone.Core.Queue;
|
||||
namespace NzbDrone.Api.Queue
|
||||
{
|
||||
public class QueueModule : NzbDroneRestModuleWithSignalR<QueueResource, Core.Queue.Queue>,
|
||||
IHandle<UpdateQueueEvent>
|
||||
IHandle<UpdateQueueEvent>, IHandle<PendingReleasesUpdatedEvent>
|
||||
{
|
||||
private readonly IQueueService _queueService;
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
|
||||
public QueueModule(ICommandExecutor commandExecutor, IQueueService queueService)
|
||||
public QueueModule(ICommandExecutor commandExecutor, IQueueService queueService, IPendingReleaseService pendingReleaseService)
|
||||
: base(commandExecutor)
|
||||
{
|
||||
_queueService = queueService;
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
GetResourceAll = GetQueue;
|
||||
}
|
||||
|
||||
private List<QueueResource> GetQueue()
|
||||
{
|
||||
return ToListResource(_queueService.GetQueue);
|
||||
return ToListResource(GetQueueItems);
|
||||
}
|
||||
|
||||
private IEnumerable<Core.Queue.Queue> GetQueueItems()
|
||||
{
|
||||
var queue = _queueService.GetQueue();
|
||||
var pending = _pendingReleaseService.GetPendingQueue();
|
||||
|
||||
return queue.Concat(pending);
|
||||
}
|
||||
|
||||
public void Handle(UpdateQueueEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Sync);
|
||||
}
|
||||
|
||||
public void Handle(PendingReleasesUpdatedEvent message)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Sync);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
namespace NzbDrone.Api.Series
|
||||
{
|
||||
@ -27,7 +26,6 @@ namespace NzbDrone.Api.Series
|
||||
IHandle<SeriesDeletedEvent>
|
||||
|
||||
{
|
||||
private readonly ICommandExecutor _commandExecutor;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly ISeriesStatisticsService _seriesStatisticsService;
|
||||
private readonly ISceneMappingService _sceneMappingService;
|
||||
@ -47,7 +45,6 @@ namespace NzbDrone.Api.Series
|
||||
)
|
||||
: base(commandExecutor)
|
||||
{
|
||||
_commandExecutor = commandExecutor;
|
||||
_seriesService = seriesService;
|
||||
_seriesStatisticsService = seriesStatisticsService;
|
||||
_sceneMappingService = sceneMappingService;
|
||||
@ -60,7 +57,7 @@ namespace NzbDrone.Api.Series
|
||||
UpdateResource = UpdateSeries;
|
||||
DeleteResource = DeleteSeries;
|
||||
|
||||
SharedValidator.RuleFor(s => s.QualityProfileId).ValidId();
|
||||
SharedValidator.RuleFor(s => s.ProfileId).ValidId();
|
||||
|
||||
SharedValidator.RuleFor(s => s.Path)
|
||||
.Cascade(CascadeMode.StopOnFirstFailure)
|
||||
|
@ -12,7 +12,7 @@ namespace NzbDrone.Api.Series
|
||||
{
|
||||
//Todo: Sorters should be done completely on the client
|
||||
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
|
||||
//Todo: We should get the entire QualityProfile instead of ID and Name separately
|
||||
//Todo: We should get the entire Profile instead of ID and Name separately
|
||||
|
||||
//View Only
|
||||
public String Title { get; set; }
|
||||
@ -32,7 +32,7 @@ namespace NzbDrone.Api.Series
|
||||
public Int32 EpisodeCount { get; set; }
|
||||
public Int32 EpisodeFileCount { get; set; }
|
||||
public SeriesStatusType Status { get; set; }
|
||||
public String QualityProfileName { get; set; }
|
||||
public String ProfileName { get; set; }
|
||||
public String Overview { get; set; }
|
||||
public DateTime? NextAiring { get; set; }
|
||||
public DateTime? PreviousAiring { get; set; }
|
||||
@ -46,7 +46,7 @@ namespace NzbDrone.Api.Series
|
||||
|
||||
//View & Edit
|
||||
public String Path { get; set; }
|
||||
public Int32 QualityProfileId { get; set; }
|
||||
public Int32 ProfileId { get; set; }
|
||||
|
||||
//Editing Only
|
||||
public Boolean SeasonFolder { get; set; }
|
||||
@ -65,5 +65,21 @@ namespace NzbDrone.Api.Series
|
||||
public String RootFolderPath { get; set; }
|
||||
public String Certification { get; set; }
|
||||
public List<String> Genres { get; set; }
|
||||
|
||||
//Used to support legacy consumers
|
||||
public Int32 QualityProfileId
|
||||
{
|
||||
get
|
||||
{
|
||||
return ProfileId;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 0 && ProfileId == 0)
|
||||
{
|
||||
ProfileId = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using FizzWare.NBuilder;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@ -15,19 +16,19 @@ namespace NzbDrone.Core.Test.Datastore
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var qualityProfile = new NzbDrone.Core.Qualities.QualityProfile
|
||||
var profile = new Profile
|
||||
{
|
||||
Name = "Test",
|
||||
Cutoff = Quality.WEBDL720p,
|
||||
Items = NzbDrone.Core.Test.Qualities.QualityFixture.GetDefaultQualities()
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities()
|
||||
};
|
||||
|
||||
|
||||
qualityProfile = Db.Insert(qualityProfile);
|
||||
profile = Db.Insert(profile);
|
||||
|
||||
var series = Builder<Series>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(v => v.QualityProfileId = qualityProfile.Id)
|
||||
.With(v => v.ProfileId = profile.Id)
|
||||
.BuildListOfNew();
|
||||
|
||||
Db.InsertMany(series);
|
||||
@ -50,7 +51,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_lazy_load_qualityprofile_if_not_joined()
|
||||
public void should_lazy_load_profile_if_not_joined()
|
||||
{
|
||||
var db = Mocker.Resolve<IDatabase>();
|
||||
var DataMapper = db.GetDataMapper();
|
||||
@ -62,7 +63,7 @@ namespace NzbDrone.Core.Test.Datastore
|
||||
foreach (var episode in episodes)
|
||||
{
|
||||
Assert.IsNotNull(episode.Series);
|
||||
Assert.IsFalse(episode.Series.QualityProfile.IsLoaded);
|
||||
Assert.IsFalse(episode.Series.Profile.IsLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,20 +85,20 @@ namespace NzbDrone.Core.Test.Datastore
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_explicit_load_qualityprofile_if_joined()
|
||||
public void should_explicit_load_profile_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, QualityProfile>(Marr.Data.QGen.JoinType.Inner, v => v.QualityProfile, (l, r) => l.QualityProfileId == r.Id)
|
||||
.Join<Series, Profile>(Marr.Data.QGen.JoinType.Inner, v => v.Profile, (l, r) => l.ProfileId == r.Id)
|
||||
.ToList();
|
||||
|
||||
foreach (var episode in episodes)
|
||||
{
|
||||
Assert.IsNotNull(episode.Series);
|
||||
Assert.IsTrue(episode.Series.QualityProfile.IsLoaded);
|
||||
Assert.IsTrue(episode.Series.Profile.IsLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using System.Linq;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests
|
||||
{
|
||||
@ -119,5 +120,23 @@ namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests
|
||||
newColumns.Values.Should().HaveSameCount(columns.Values);
|
||||
newIndexes.Should().Contain(i=>i.Column == "AirTime");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_create_indexes_with_the_same_uniqueness()
|
||||
{
|
||||
var columns = _subject.GetColumns("Series");
|
||||
var indexes = _subject.GetIndexes("Series");
|
||||
|
||||
var tempIndexes = indexes.JsonClone();
|
||||
|
||||
tempIndexes[0].Unique = false;
|
||||
tempIndexes[1].Unique = true;
|
||||
|
||||
_subject.CreateTable("Series_New", columns.Values, tempIndexes);
|
||||
var newIndexes = _subject.GetIndexes("Series_New");
|
||||
|
||||
newIndexes.Should().HaveSameCount(tempIndexes);
|
||||
newIndexes.ShouldAllBeEquivalentTo(tempIndexes, options => options.Excluding(o => o.IndexName).Excluding(o => o.Table));
|
||||
}
|
||||
}
|
||||
}
|
@ -25,12 +25,12 @@ namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests
|
||||
{
|
||||
var series = Builder<Series>.CreateListOfSize(10)
|
||||
.Random(3)
|
||||
.With(c => c.QualityProfileId = 100)
|
||||
.With(c => c.ProfileId = 100)
|
||||
.BuildListOfNew();
|
||||
|
||||
Db.InsertMany(series);
|
||||
|
||||
var duplicates = _subject.GetDuplicates<int>("series", "QualityProfileId").ToList();
|
||||
var duplicates = _subject.GetDuplicates<int>("series", "ProfileId").ToList();
|
||||
|
||||
|
||||
duplicates.Should().HaveCount(1);
|
||||
|
@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@ -13,35 +14,35 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
[Test]
|
||||
public void should_return_true_if_current_episode_is_less_than_cutoff()
|
||||
{
|
||||
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.DVD, true)).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_current_episode_is_equal_to_cutoff()
|
||||
{
|
||||
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.HDTV720p, true)).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_current_episode_is_greater_than_cutoff()
|
||||
{
|
||||
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_new_episode_is_proper_but_existing_is_not()
|
||||
{
|
||||
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_cutoff_is_met_and_quality_is_higher()
|
||||
{
|
||||
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using NzbDrone.Core.Download.Clients.Sabnzbd;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@ -42,7 +43,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
};
|
||||
|
||||
_fakeSeries = Builder<Series>.CreateNew()
|
||||
.With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
_parseResultMulti = new RemoteEpisode
|
||||
@ -62,9 +63,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
_upgradableQuality = new QualityModel(Quality.SDTV, false);
|
||||
_notupgradableQuality = new QualityModel(Quality.HDTV1080p, true);
|
||||
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 1)).Returns(_notupgradableQuality);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 2)).Returns(_notupgradableQuality);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 3)).Returns<QualityModel>(null);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 1)).Returns(_notupgradableQuality);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 2)).Returns(_notupgradableQuality);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 3)).Returns<QualityModel>(null);
|
||||
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(c => c.GetDownloadClients())
|
||||
@ -73,12 +74,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
||||
private void WithFirstReportUpgradable()
|
||||
{
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 1)).Returns(_upgradableQuality);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 1)).Returns(_upgradableQuality);
|
||||
}
|
||||
|
||||
private void WithSecondReportUpgradable()
|
||||
{
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 2)).Returns(_upgradableQuality);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 2)).Returns(_upgradableQuality);
|
||||
}
|
||||
|
||||
private void GivenSabnzbdDownloadClient()
|
||||
@ -132,11 +133,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
[Test]
|
||||
public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing()
|
||||
{
|
||||
_fakeSeries.QualityProfile = new QualityProfile { 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, false);
|
||||
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, false);
|
||||
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 1)).Returns(_upgradableQuality);
|
||||
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<Profile>(), 1)).Returns(_upgradableQuality);
|
||||
|
||||
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse();
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
@ -11,28 +14,35 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
||||
public class LanguageSpecificationFixture : CoreTest
|
||||
{
|
||||
private RemoteEpisode parseResult;
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
Language = Language.English
|
||||
},
|
||||
Series = new Series
|
||||
{
|
||||
Profile = new LazyLoaded<Profile>(new Profile
|
||||
{
|
||||
Language = Language.English
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void WithEnglishRelease()
|
||||
{
|
||||
parseResult = new RemoteEpisode
|
||||
{
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
Language = Language.English
|
||||
}
|
||||
};
|
||||
_remoteEpisode.ParsedEpisodeInfo.Language = Language.English;
|
||||
}
|
||||
|
||||
private void WithGermanRelease()
|
||||
{
|
||||
parseResult = new RemoteEpisode
|
||||
{
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
Language = Language.German
|
||||
}
|
||||
};
|
||||
_remoteEpisode.ParsedEpisodeInfo.Language = Language.German;
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -40,7 +50,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
WithEnglishRelease();
|
||||
|
||||
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(parseResult, null).Should().BeTrue();
|
||||
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -48,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
WithGermanRelease();
|
||||
|
||||
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(parseResult, null).Should().BeFalse();
|
||||
Mocker.Resolve<LanguageSpecification>().IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@ -27,7 +28,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
_episode = Builder<Episode>.CreateNew()
|
||||
|
@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@ -38,7 +40,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
remoteEpisode.Release.Size = size;
|
||||
|
||||
remoteEpisode.Series = Builder<Series>.CreateNew()
|
||||
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
return remoteEpisode;
|
||||
|
@ -5,6 +5,7 @@ using Marr.Data;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@ -35,7 +36,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
public void Setup()
|
||||
{
|
||||
var fakeSeries = Builder<Series>.CreateNew()
|
||||
.With(c => c.QualityProfile = (LazyLoaded<QualityProfile>)new QualityProfile { Cutoff = Quality.Bluray1080p })
|
||||
.With(c => c.Profile = (LazyLoaded<Profile>)new Profile { Cutoff = Quality.Bluray1080p })
|
||||
.Build();
|
||||
|
||||
remoteEpisode = new RemoteEpisode
|
||||
@ -49,7 +50,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
public void should_allow_if_quality_is_defined_in_profile(Quality qualityType)
|
||||
{
|
||||
remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType;
|
||||
remoteEpisode.Series.QualityProfile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
|
||||
remoteEpisode.Series.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
|
||||
|
||||
Subject.IsSatisfiedBy(remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
@ -58,7 +59,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType)
|
||||
{
|
||||
remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType;
|
||||
remoteEpisode.Series.QualityProfile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
|
||||
remoteEpisode.Series.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
|
||||
|
||||
Subject.IsSatisfiedBy(remoteEpisode, null).Should().BeFalse();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@ -43,9 +44,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
GivenAutoDownloadPropers(true);
|
||||
|
||||
var qualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
|
||||
Subject.IsUpgradable(qualityProfile, new QualityModel(current, currentProper), new QualityModel(newQuality, newProper))
|
||||
Subject.IsUpgradable(profile, new QualityModel(current, currentProper), new QualityModel(newQuality, newProper))
|
||||
.Should().Be(expected);
|
||||
}
|
||||
|
||||
@ -54,9 +55,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
GivenAutoDownloadPropers(false);
|
||||
|
||||
var qualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
|
||||
Subject.IsUpgradable(qualityProfile, new QualityModel(Quality.DVD, true), new QualityModel(Quality.DVD, false))
|
||||
Subject.IsUpgradable(profile, new QualityModel(Quality.DVD, true), new QualityModel(Quality.DVD, false))
|
||||
.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||
{
|
||||
[TestFixture]
|
||||
public class DelaySpecificationFixture : CoreTest<DelaySpecification>
|
||||
{
|
||||
private Profile _profile;
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_profile = Builder<Profile>.CreateNew()
|
||||
.Build();
|
||||
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Profile = _profile)
|
||||
.Build();
|
||||
|
||||
_remoteEpisode = Builder<RemoteEpisode>.CreateNew()
|
||||
.With(r => r.Series = series)
|
||||
.Build();
|
||||
|
||||
_profile.Items = new List<ProfileQualityItem>();
|
||||
_profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p });
|
||||
_profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.WEBDL720p });
|
||||
_profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.Bluray720p });
|
||||
|
||||
_profile.Cutoff = Quality.WEBDL720p;
|
||||
|
||||
_profile.GrabDelayMode = GrabDelayMode.Always;
|
||||
|
||||
_remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
|
||||
_remoteEpisode.Release = new ReleaseInfo();
|
||||
|
||||
_remoteEpisode.Episodes = Builder<Episode>.CreateListOfSize(1).Build().ToList();
|
||||
}
|
||||
|
||||
private void GivenExistingFile(QualityModel quality)
|
||||
{
|
||||
_remoteEpisode.Episodes[0].EpisodeFile = new LazyLoaded<EpisodeFile>(new EpisodeFile
|
||||
{
|
||||
Quality = quality
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_search()
|
||||
{
|
||||
Subject.IsSatisfiedBy(new RemoteEpisode(), new SingleEpisodeSearchCriteria()).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_profile_does_not_have_a_delay()
|
||||
{
|
||||
_profile.GrabDelay = 0;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_quality_is_last_allowed_in_profile()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray720p);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_release_is_older_than_delay()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow.AddHours(-10);
|
||||
|
||||
_profile.GrabDelay = 1;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_false_when_release_is_younger_than_delay()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.SDTV);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_release_is_proper_for_existing_episode()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, true);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
GivenExistingFile(new QualityModel(Quality.HDTV720p));
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_false_when_release_is_proper_and_no_existing_episode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_release_meets_cutoff_and_mode_is_cutoff()
|
||||
{
|
||||
_profile.GrabDelayMode = GrabDelayMode.Cutoff;
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_release_exceeds_cutoff_and_mode_is_cutoff()
|
||||
{
|
||||
_profile.GrabDelayMode = GrabDelayMode.Cutoff;
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_false_when_release_is_below_cutoff_and_mode_is_cutoff()
|
||||
{
|
||||
_profile.GrabDelayMode = GrabDelayMode.Cutoff;
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_false_when_release_is_proper_for_existing_episode_of_different_quality()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, true);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
GivenExistingFile(new QualityModel(Quality.SDTV));
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_false_when_release_is_first_detected_and_mode_is_first()
|
||||
{
|
||||
_profile.GrabDelayMode = GrabDelayMode.First;
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
Mocker.GetMock<IPendingReleaseService>()
|
||||
.Setup(s => s.GetPendingRemoteEpisodes(It.IsAny<Int32>()))
|
||||
.Returns(new List<RemoteEpisode>());
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_false_when_release_is_not_first_but_oldest_has_not_expired_and_type_is_first()
|
||||
{
|
||||
_profile.GrabDelayMode = GrabDelayMode.First;
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
var pendingRemoteEpisode = _remoteEpisode.JsonClone();
|
||||
|
||||
Mocker.GetMock<IPendingReleaseService>()
|
||||
.Setup(s => s.GetPendingRemoteEpisodes(It.IsAny<Int32>()))
|
||||
.Returns(new List<RemoteEpisode> { _remoteEpisode.JsonClone() });
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_existing_pending_release_expired_and_mode_is_first()
|
||||
{
|
||||
_profile.GrabDelayMode = GrabDelayMode.First;
|
||||
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
var pendingRemoteEpisode = _remoteEpisode.JsonClone();
|
||||
pendingRemoteEpisode.Release.PublishDate = DateTime.UtcNow.AddHours(-15);
|
||||
|
||||
Mocker.GetMock<IPendingReleaseService>()
|
||||
.Setup(s => s.GetPendingRemoteEpisodes(It.IsAny<Int32>()))
|
||||
.Returns(new List<RemoteEpisode> { pendingRemoteEpisode });
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_true_when_one_existing_pending_release_is_expired_and_mode_is_first()
|
||||
{
|
||||
_profile.GrabDelayMode = GrabDelayMode.First;
|
||||
|
||||
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL720p);
|
||||
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
_profile.GrabDelay = 12;
|
||||
|
||||
var pendingRemoteEpisode1 = _remoteEpisode.JsonClone();
|
||||
pendingRemoteEpisode1.Release.PublishDate = DateTime.UtcNow.AddHours(-15);
|
||||
|
||||
var pendingRemoteEpisode2 = _remoteEpisode.JsonClone();
|
||||
pendingRemoteEpisode2.Release.PublishDate = DateTime.UtcNow.AddHours(5);
|
||||
|
||||
Mocker.GetMock<IPendingReleaseService>()
|
||||
.Setup(s => s.GetPendingRemoteEpisodes(It.IsAny<Int32>()))
|
||||
.Returns(new List<RemoteEpisode> { pendingRemoteEpisode1, pendingRemoteEpisode2 });
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@ -37,7 +38,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
||||
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
|
||||
|
||||
var fakeSeries = Builder<Series>.CreateNew()
|
||||
.With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p })
|
||||
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p })
|
||||
.Build();
|
||||
|
||||
_parseResultMulti = new RemoteEpisode
|
||||
|
@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
@ -38,7 +39,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
|
||||
|
||||
var fakeSeries = Builder<Series>.CreateNew()
|
||||
.With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
_parseResultMulti = new RemoteEpisode
|
||||
|
@ -4,19 +4,19 @@ using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class DownloadApprovedFixture : CoreTest<DownloadApprovedReports>
|
||||
public class DownloadApprovedFixture : CoreTest<ProcessDownloadDecisions>
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
remoteEpisode.Release.PublishDate = DateTime.UtcNow;
|
||||
|
||||
remoteEpisode.Series = Builder<Series>.CreateNew()
|
||||
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
return remoteEpisode;
|
||||
@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode));
|
||||
|
||||
Subject.DownloadApproved(decisions);
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteEpisode>()), Times.Once());
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisode));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode));
|
||||
|
||||
Subject.DownloadApproved(decisions);
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteEpisode>()), Times.Once());
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
Subject.DownloadApproved(decisions);
|
||||
Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteEpisode>()), Times.Once());
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode));
|
||||
|
||||
Subject.DownloadApproved(decisions).Should().HaveCount(1);
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
Subject.DownloadApproved(decisions).Should().HaveCount(2);
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -156,7 +156,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode3));
|
||||
|
||||
Subject.DownloadApproved(decisions).Should().HaveCount(2);
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -169,7 +169,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisode));
|
||||
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteEpisode>())).Throws(new Exception());
|
||||
Subject.DownloadApproved(decisions).Should().BeEmpty();
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().BeEmpty();
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
@ -177,8 +177,8 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
public void should_return_an_empty_list_when_none_are_appproved()
|
||||
{
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(null, "Failure!"));
|
||||
decisions.Add(new DownloadDecision(null, "Failure!"));
|
||||
decisions.Add(new DownloadDecision(null, new Rejection("Failure!")));
|
||||
decisions.Add(new DownloadDecision(null, new Rejection("Failure!")));
|
||||
|
||||
Subject.GetQualifiedReports(decisions).Should().BeEmpty();
|
||||
}
|
||||
|
@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProcessFixture : CoreTest<PendingReleaseService>
|
||||
{
|
||||
private DownloadDecision _temporarilyRejected;
|
||||
private Series _series;
|
||||
private Episode _episode;
|
||||
private Profile _profile;
|
||||
private ReleaseInfo _release;
|
||||
private ParsedEpisodeInfo _parsedEpisodeInfo;
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.Build();
|
||||
|
||||
_episode = Builder<Episode>.CreateNew()
|
||||
.Build();
|
||||
|
||||
_profile = new Profile
|
||||
{
|
||||
Name = "Test",
|
||||
Cutoff = Quality.HDTV720p,
|
||||
GrabDelay = 1,
|
||||
Items = new List<ProfileQualityItem>
|
||||
{
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p },
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.WEBDL720p },
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.Bluray720p }
|
||||
},
|
||||
};
|
||||
|
||||
_series.Profile = new LazyLoaded<Profile>(_profile);
|
||||
|
||||
_release = Builder<ReleaseInfo>.CreateNew().Build();
|
||||
|
||||
_parsedEpisodeInfo = Builder<ParsedEpisodeInfo>.CreateNew().Build();
|
||||
_parsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p);
|
||||
|
||||
_remoteEpisode = new RemoteEpisode();
|
||||
_remoteEpisode.Episodes = new List<Episode>{ _episode };
|
||||
_remoteEpisode.Series = _series;
|
||||
_remoteEpisode.ParsedEpisodeInfo = _parsedEpisodeInfo;
|
||||
_remoteEpisode.Release = _release;
|
||||
|
||||
_temporarilyRejected = new DownloadDecision(_remoteEpisode, new Rejection("Temp Rejected", RejectionType.Temporary));
|
||||
|
||||
Mocker.GetMock<IPendingReleaseRepository>()
|
||||
.Setup(s => s.All())
|
||||
.Returns(new List<PendingRelease>());
|
||||
|
||||
Mocker.GetMock<ISeriesService>()
|
||||
.Setup(s => s.GetSeries(It.IsAny<Int32>()))
|
||||
.Returns(_series);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.GetEpisodes(It.IsAny<ParsedEpisodeInfo>(), _series, true, null))
|
||||
.Returns(new List<Episode> {_episode});
|
||||
|
||||
Mocker.GetMock<IPrioritizeDownloadDecision>()
|
||||
.Setup(s => s.PrioritizeDecisions(It.IsAny<List<DownloadDecision>>()))
|
||||
.Returns((List<DownloadDecision> d) => d);
|
||||
}
|
||||
|
||||
private void GivenHeldRelease(String title, String indexer, DateTime publishDate)
|
||||
{
|
||||
var release = _release.JsonClone();
|
||||
release.Indexer = indexer;
|
||||
release.PublishDate = publishDate;
|
||||
|
||||
|
||||
var heldReleases = Builder<PendingRelease>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.SeriesId = _series.Id)
|
||||
.With(h => h.Title = title)
|
||||
.With(h => h.Release = release)
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<IPendingReleaseRepository>()
|
||||
.Setup(s => s.All())
|
||||
.Returns(heldReleases);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add()
|
||||
{
|
||||
Subject.Add(_temporarilyRejected);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_add_if_it_is_the_same_release_from_the_same_indexer()
|
||||
{
|
||||
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate);
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
|
||||
VerifyNoInsert();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_if_title_is_different()
|
||||
{
|
||||
GivenHeldRelease(_release.Title + "-RP", _release.Indexer, _release.PublishDate);
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_if_indexer_is_different()
|
||||
{
|
||||
GivenHeldRelease(_release.Title, "AnotherIndexer", _release.PublishDate);
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
[Test]
|
||||
public void should_add_if_publish_date_is_different()
|
||||
{
|
||||
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate.AddHours(1));
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
|
||||
private void VerifyInsert()
|
||||
{
|
||||
Mocker.GetMock<IPendingReleaseRepository>()
|
||||
.Verify(v => v.Insert(It.IsAny<PendingRelease>()), Times.Once());
|
||||
}
|
||||
|
||||
private void VerifyNoInsert()
|
||||
{
|
||||
Mocker.GetMock<IPendingReleaseRepository>()
|
||||
.Verify(v => v.Insert(It.IsAny<PendingRelease>()), Times.Never());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@ -10,14 +11,14 @@ namespace NzbDrone.Core.Test.HistoryTests
|
||||
{
|
||||
public class HistoryServiceFixture : CoreTest<HistoryService>
|
||||
{
|
||||
private QualityProfile _profile;
|
||||
private QualityProfile _profileCustom;
|
||||
private Profile _profile;
|
||||
private Profile _profileCustom;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_profile = new QualityProfile { Cutoff = Quality.WEBDL720p, Items = QualityFixture.GetDefaultQualities() };
|
||||
_profileCustom = new QualityProfile { Cutoff = Quality.WEBDL720p, Items = QualityFixture.GetDefaultQualities(Quality.DVD) };
|
||||
_profile = new Profile { Cutoff = Quality.WEBDL720p, Items = QualityFixture.GetDefaultQualities() };
|
||||
_profileCustom = new Profile { Cutoff = Quality.WEBDL720p, Items = QualityFixture.GetDefaultQualities(Quality.DVD) };
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -0,0 +1,42 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.Housekeeping.Housekeepers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
{
|
||||
[TestFixture]
|
||||
public class CleanupOrphanedBlacklistFixture : DbTest<CleanupOrphanedBlacklist, Blacklist>
|
||||
{
|
||||
[Test]
|
||||
public void should_delete_orphaned_blacklist_items()
|
||||
{
|
||||
var blacklist = Builder<Blacklist>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(blacklist);
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_unorphaned_blacklist_items()
|
||||
{
|
||||
var series = Builder<Series>.CreateNew().BuildNew();
|
||||
|
||||
Db.Insert(series);
|
||||
|
||||
var blacklist = Builder<Blacklist>.CreateNew()
|
||||
.With(b => b.SeriesId = series.Id)
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(blacklist);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Housekeeping.Housekeepers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
{
|
||||
[TestFixture]
|
||||
public class CleanupOrphanedPendingReleasesFixture : DbTest<CleanupOrphanedPendingReleases, PendingRelease>
|
||||
{
|
||||
[Test]
|
||||
public void should_delete_orphaned_pending_items()
|
||||
{
|
||||
var pendingRelease = Builder<PendingRelease>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(pendingRelease);
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_unorphaned_pending_items()
|
||||
{
|
||||
var series = Builder<Series>.CreateNew().BuildNew();
|
||||
|
||||
Db.Insert(series);
|
||||
|
||||
var pendingRelease = Builder<PendingRelease>.CreateNew()
|
||||
.With(h => h.SeriesId = series.Id)
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(pendingRelease);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using FizzWare.NBuilder;
|
||||
@ -30,9 +31,9 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
.Setup(s => s.GetAvailableProviders())
|
||||
.Returns(new List<IIndexer> { indexer.Object });
|
||||
|
||||
Mocker.GetMock<DecisionEngine.IMakeDownloadDecision>()
|
||||
Mocker.GetMock<IMakeDownloadDecision>()
|
||||
.Setup(s => s.GetSearchDecision(It.IsAny<List<Parser.Model.ReleaseInfo>>(), It.IsAny<SearchCriteriaBase>()))
|
||||
.Returns(new List<DecisionEngine.Specifications.DownloadDecision>());
|
||||
.Returns(new List<DownloadDecision>());
|
||||
|
||||
_xemSeries = Builder<Series>.CreateNew()
|
||||
.With(v => v.UseSceneNumbering = true)
|
||||
|
@ -9,6 +9,7 @@ using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -65,7 +66,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
|
||||
_videoFiles = new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" };
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
_quality = new QualityModel(Quality.DVD);
|
||||
|
@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -22,8 +23,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
_localEpisode = new LocalEpisode
|
||||
|
@ -9,6 +9,7 @@ using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -29,7 +30,7 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
_approvedDecisions = new List<ImportDecision>();
|
||||
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(5)
|
||||
|
@ -111,6 +111,7 @@
|
||||
<Compile Include="DecisionEngineTests\NotInQueueSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\CutoffSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\NotRestrictedReleaseSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
|
||||
<Compile Include="Download\DownloadApprovedReportsTests\DownloadApprovedFixture.cs" />
|
||||
@ -123,6 +124,7 @@
|
||||
<Compile Include="Download\DownloadServiceFixture.cs" />
|
||||
<Compile Include="Download\CompletedDownloadServiceFixture.cs" />
|
||||
<Compile Include="Download\FailedDownloadServiceFixture.cs" />
|
||||
<Compile Include="Download\Pending\PendingReleaseServiceTests\ProcessFixture.cs" />
|
||||
<Compile Include="Framework\CoreTest.cs" />
|
||||
<Compile Include="Framework\DbTest.cs" />
|
||||
<Compile Include="Framework\NBuilderExtensions.cs" />
|
||||
@ -141,6 +143,8 @@
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupAdditionalNamingSpecsFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedMetadataFilesFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupDuplicateMetadataFilesFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedBlacklistFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedPendingReleasesFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\FixFutureRunScheduledTasksFixture.cs" />
|
||||
<Compile Include="IndexerSearchTests\NzbSearchServiceFixture.cs" />
|
||||
<Compile Include="IndexerSearchTests\SearchDefinitionFixture.cs" />
|
||||
@ -209,7 +213,7 @@
|
||||
<Compile Include="ParserTests\SeriesTitleInfoFixture.cs" />
|
||||
<Compile Include="Providers\XemProxyFixture.cs" />
|
||||
<Compile Include="Qualities\QualityDefinitionRepositoryFixture.cs" />
|
||||
<Compile Include="Qualities\QualityProfileRepositoryFixture.cs" />
|
||||
<Compile Include="Profiles\ProfileRepositoryFixture.cs" />
|
||||
<Compile Include="RootFolderTests\FreeSpaceOnDrivesFixture.cs" />
|
||||
<Compile Include="Qualities\QualityFixture.cs" />
|
||||
<Compile Include="ParserTests\QualityParserFixture.cs" />
|
||||
@ -242,7 +246,7 @@
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
|
||||
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
|
||||
<Compile Include="TvTests\RefreshSeriesServiceFixture.cs" />
|
||||
<Compile Include="TvTests\SeriesRepositoryTests\QualityProfileRepositoryFixture.cs" />
|
||||
<Compile Include="TvTests\SeriesRepositoryTests\SeriesRepositoryFixture.cs" />
|
||||
<Compile Include="TvTests\SeriesServiceTests\UpdateMultipleSeriesFixture.cs" />
|
||||
<Compile Include="TvTests\SeriesServiceTests\UpdateSeriesFixture.cs" />
|
||||
<Compile Include="TvTests\ShouldRefreshSeriesFixture.cs" />
|
||||
@ -260,11 +264,10 @@
|
||||
<Compile Include="HistoryTests\HistoryRepositoryFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaFileServiceTest.cs" />
|
||||
<Compile Include="Configuration\ConfigServiceFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeProviderTests\EpisodeProviderTest.cs" />
|
||||
<Compile Include="Framework\TestDbHelper.cs" />
|
||||
<Compile Include="ParserTests\ParserFixture.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Qualities\QualityProfileServiceFixture.cs" />
|
||||
<Compile Include="Profiles\ProfileServiceFixture.cs" />
|
||||
<Compile Include="TvTests\SeriesServiceTests\AddSeriesFixture.cs" />
|
||||
<Compile Include="UpdateTests\UpdatePackageProviderFixture.cs" />
|
||||
<Compile Include="XbmcVersionTests.cs" />
|
||||
|
@ -1,20 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Qualities
|
||||
namespace NzbDrone.Core.Test.Profiles
|
||||
{
|
||||
[TestFixture]
|
||||
public class QualityProfileRepositoryFixture : DbTest<QualityProfileRepository, QualityProfile>
|
||||
public class ProfileRepositoryFixture : DbTest<ProfileRepository, Profile>
|
||||
{
|
||||
[Test]
|
||||
public void should_be_able_to_read_and_write()
|
||||
{
|
||||
var profile = new QualityProfile
|
||||
var profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
|
||||
Cutoff = Quality.Bluray1080p,
|
||||
Name = "TestProfile"
|
||||
};
|
||||
@ -23,8 +23,8 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
|
||||
StoredModel.Name.Should().Be(profile.Name);
|
||||
StoredModel.Cutoff.Should().Be(profile.Cutoff);
|
||||
|
||||
StoredModel.Items.Should().Equal(profile.Items, (a,b) => a.Quality == b.Quality && a.Allowed == b.Allowed);
|
||||
|
||||
StoredModel.Items.Should().Equal(profile.Items, (a, b) => a.Quality == b.Quality && a.Allowed == b.Allowed);
|
||||
|
||||
|
||||
}
|
@ -3,23 +3,24 @@ using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.Qualities
|
||||
namespace NzbDrone.Core.Test.Profiles
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class QualityProfileServiceFixture : CoreTest<QualityProfileService>
|
||||
public class ProfileServiceFixture : CoreTest<ProfileService>
|
||||
{
|
||||
[Test]
|
||||
public void Init_should_add_two_profiles()
|
||||
{
|
||||
Subject.Handle(new ApplicationStartedEvent());
|
||||
|
||||
Mocker.GetMock<IQualityProfileRepository>()
|
||||
.Verify(v => v.Insert(It.IsAny<QualityProfile>()), Times.Exactly(4));
|
||||
Mocker.GetMock<IProfileRepository>()
|
||||
.Verify(v => v.Insert(It.IsAny<Profile>()), Times.Exactly(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -27,14 +28,14 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
//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<IQualityProfileRepository>()
|
||||
Mocker.GetMock<IProfileRepository>()
|
||||
.Setup(s => s.All())
|
||||
.Returns(Builder<QualityProfile>.CreateListOfSize(2).Build().ToList());
|
||||
.Returns(Builder<Profile>.CreateListOfSize(2).Build().ToList());
|
||||
|
||||
Subject.Handle(new ApplicationStartedEvent());
|
||||
|
||||
Mocker.GetMock<IQualityProfileRepository>()
|
||||
.Verify(v => v.Insert(It.IsAny<QualityProfile>()), Times.Never());
|
||||
Mocker.GetMock<IProfileRepository>()
|
||||
.Verify(v => v.Insert(It.IsAny<Profile>()), Times.Never());
|
||||
}
|
||||
|
||||
|
||||
@ -43,15 +44,15 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
{
|
||||
var seriesList = Builder<Series>.CreateListOfSize(3)
|
||||
.Random(1)
|
||||
.With(c => c.QualityProfileId = 2)
|
||||
.With(c => c.ProfileId = 2)
|
||||
.Build().ToList();
|
||||
|
||||
|
||||
Mocker.GetMock<ISeriesService>().Setup(c => c.GetAllSeries()).Returns(seriesList);
|
||||
|
||||
Assert.Throws<QualityProfileInUseException>(() => Subject.Delete(2));
|
||||
Assert.Throws<ProfileInUseException>(() => Subject.Delete(2));
|
||||
|
||||
Mocker.GetMock<IQualityProfileRepository>().Verify(c => c.Delete(It.IsAny<int>()), Times.Never());
|
||||
Mocker.GetMock<IProfileRepository>().Verify(c => c.Delete(It.IsAny<int>()), Times.Never());
|
||||
|
||||
}
|
||||
|
||||
@ -61,7 +62,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
{
|
||||
var seriesList = Builder<Series>.CreateListOfSize(3)
|
||||
.All()
|
||||
.With(c => c.QualityProfileId = 2)
|
||||
.With(c => c.ProfileId = 2)
|
||||
.Build().ToList();
|
||||
|
||||
|
||||
@ -69,7 +70,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
|
||||
Subject.Delete(1);
|
||||
|
||||
Mocker.GetMock<IQualityProfileRepository>().Verify(c => c.Delete(1), Times.Once());
|
||||
Mocker.GetMock<IProfileRepository>().Verify(c => c.Delete(1), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
@ -44,7 +45,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
i.Should().Be(expected);
|
||||
}
|
||||
|
||||
public static List<QualityProfileItem> GetDefaultQualities(params Quality[] allowed)
|
||||
public static List<ProfileQualityItem> GetDefaultQualities(params Quality[] allowed)
|
||||
{
|
||||
var qualities = new List<Quality>
|
||||
{
|
||||
@ -66,7 +67,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
var items = qualities
|
||||
.Except(allowed)
|
||||
.Concat(allowed)
|
||||
.Select(v => new QualityProfileItem { Quality = v, Allowed = allowed.Contains(v) }).ToList();
|
||||
.Select(v => new ProfileQualityItem { Quality = v, Allowed = allowed.Contains(v) }).ToList();
|
||||
|
||||
return items;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@ -13,20 +14,20 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
{
|
||||
public QualityModelComparer Subject { get; set; }
|
||||
|
||||
private void GivenDefaultQualityProfile()
|
||||
private void GivenDefaultProfile()
|
||||
{
|
||||
Subject = new QualityModelComparer(new QualityProfile { Items = QualityFixture.GetDefaultQualities() });
|
||||
Subject = new QualityModelComparer(new Profile { Items = QualityFixture.GetDefaultQualities() });
|
||||
}
|
||||
|
||||
private void GivenCustomQualityProfile()
|
||||
private void GivenCustomProfile()
|
||||
{
|
||||
Subject = new QualityModelComparer(new QualityProfile { Items = QualityFixture.GetDefaultQualities(Quality.Bluray720p, Quality.DVD) });
|
||||
Subject = new QualityModelComparer(new Profile { Items = QualityFixture.GetDefaultQualities(Quality.Bluray720p, Quality.DVD) });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Icomparer_greater_test()
|
||||
{
|
||||
GivenDefaultQualityProfile();
|
||||
GivenDefaultProfile();
|
||||
|
||||
var first = new QualityModel(Quality.DVD, true);
|
||||
var second = new QualityModel(Quality.Bluray1080p, true);
|
||||
@ -39,7 +40,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
[Test]
|
||||
public void Icomparer_greater_proper()
|
||||
{
|
||||
GivenDefaultQualityProfile();
|
||||
GivenDefaultProfile();
|
||||
|
||||
var first = new QualityModel(Quality.Bluray1080p, false);
|
||||
var second = new QualityModel(Quality.Bluray1080p, true);
|
||||
@ -52,7 +53,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
[Test]
|
||||
public void Icomparer_lesser()
|
||||
{
|
||||
GivenDefaultQualityProfile();
|
||||
GivenDefaultProfile();
|
||||
|
||||
var first = new QualityModel(Quality.DVD, true);
|
||||
var second = new QualityModel(Quality.Bluray1080p, true);
|
||||
@ -65,7 +66,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
[Test]
|
||||
public void Icomparer_lesser_proper()
|
||||
{
|
||||
GivenDefaultQualityProfile();
|
||||
GivenDefaultProfile();
|
||||
|
||||
var first = new QualityModel(Quality.DVD, false);
|
||||
var second = new QualityModel(Quality.DVD, true);
|
||||
@ -78,7 +79,7 @@ namespace NzbDrone.Core.Test.Qualities
|
||||
[Test]
|
||||
public void Icomparer_greater_custom_order()
|
||||
{
|
||||
GivenCustomQualityProfile();
|
||||
GivenCustomProfile();
|
||||
|
||||
var first = new QualityModel(Quality.DVD, true);
|
||||
var second = new QualityModel(Quality.Bluray720p, true);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@ using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@ -22,15 +23,15 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var qualityProfile = new QualityProfile
|
||||
var profile = new Profile
|
||||
{
|
||||
Id = 1,
|
||||
Cutoff = Quality.WEBDL480p,
|
||||
Items = new List<QualityProfileItem>
|
||||
Items = new List<ProfileQualityItem>
|
||||
{
|
||||
new QualityProfileItem { Allowed = true, Quality = Quality.SDTV },
|
||||
new QualityProfileItem { Allowed = true, Quality = Quality.WEBDL480p },
|
||||
new QualityProfileItem { Allowed = true, Quality = Quality.RAWHD }
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.SDTV },
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.WEBDL480p },
|
||||
new ProfileQualityItem { Allowed = true, Quality = Quality.RAWHD }
|
||||
}
|
||||
};
|
||||
|
||||
@ -39,7 +40,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
|
||||
.With(s => s.Runtime = 30)
|
||||
.With(s => s.Monitored = true)
|
||||
.With(s => s.TitleSlug = "Title3")
|
||||
.With(s => s.Id = qualityProfile.Id)
|
||||
.With(s => s.Id = profile.Id)
|
||||
.BuildNew();
|
||||
|
||||
_unmonitoredSeries = Builder<Series>.CreateNew()
|
||||
@ -47,7 +48,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
|
||||
.With(s => s.Runtime = 30)
|
||||
.With(s => s.Monitored = false)
|
||||
.With(s => s.TitleSlug = "Title2")
|
||||
.With(s => s.Id = qualityProfile.Id)
|
||||
.With(s => s.Id = profile.Id)
|
||||
.BuildNew();
|
||||
|
||||
_monitoredSeries.Id = Db.Insert(_monitoredSeries).Id;
|
||||
@ -63,7 +64,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
|
||||
|
||||
_qualitiesBelowCutoff = new List<QualitiesBelowCutoff>
|
||||
{
|
||||
new QualitiesBelowCutoff(qualityProfile.Id, new[] {Quality.SDTV.Id})
|
||||
new QualitiesBelowCutoff(profile.Id, new[] {Quality.SDTV.Id})
|
||||
};
|
||||
|
||||
var qualityMet = new EpisodeFile { Path = "a", Quality = new QualityModel { Quality = Quality.WEBDL480p } };
|
||||
|
@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests
|
||||
[Test]
|
||||
public void should_lazyload_quality_profile()
|
||||
{
|
||||
var profile = new QualityProfile
|
||||
var profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
|
||||
|
||||
@ -24,15 +24,15 @@ namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests
|
||||
};
|
||||
|
||||
|
||||
Mocker.Resolve<QualityProfileRepository>().Insert(profile);
|
||||
Mocker.Resolve<ProfileRepository>().Insert(profile);
|
||||
|
||||
var series = Builder<Series>.CreateNew().BuildNew();
|
||||
series.QualityProfileId = profile.Id;
|
||||
series.ProfileId = profile.Id;
|
||||
|
||||
Subject.Insert(series);
|
||||
|
||||
|
||||
StoredModel.QualityProfile.Should().NotBeNull();
|
||||
StoredModel.Profile.Should().NotBeNull();
|
||||
|
||||
|
||||
}
|
@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.TvTests.SeriesServiceTests
|
||||
{
|
||||
_series = Builder<Series>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(s => s.QualityProfileId = 1)
|
||||
.With(s => s.ProfileId = 1)
|
||||
.With(s => s.Monitored)
|
||||
.With(s => s.SeasonFolder)
|
||||
.With(s => s.Path = @"C:\Test\name".AsOsAgnostic())
|
||||
|
@ -17,7 +17,6 @@ namespace NzbDrone.Core.Datastore.Extensions
|
||||
query: (db, parent) => db.Query<TChild>().SingleOrDefault(c => c.Id == childIdSelector(parent)),
|
||||
condition: parent => childIdSelector(parent) > 0
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public static RelationshipBuilder<TParent> Relationship<TParent>(this ColumnMapBuilder<TParent> mapBuilder)
|
||||
@ -25,16 +24,12 @@ namespace NzbDrone.Core.Datastore.Extensions
|
||||
return mapBuilder.Relationships.AutoMapComplexTypeProperties<ILazyLoaded>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static RelationshipBuilder<TParent> HasMany<TParent, TChild>(this RelationshipBuilder<TParent> relationshipBuilder, Expression<Func<TParent, LazyList<TChild>>> portalExpression, Func<TParent, int> childIdSelector)
|
||||
where TParent : ModelBase
|
||||
where TChild : ModelBase
|
||||
{
|
||||
return relationshipBuilder.For(portalExpression.GetMemberName())
|
||||
.LazyLoad((db, parent) => db.Query<TChild>().Where(c => c.Id == childIdSelector(parent)).ToList());
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static string GetMemberName<T, TMember>(this Expression<Func<T, TMember>> member)
|
||||
|
@ -3,6 +3,7 @@ using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
@ -41,7 +42,7 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
|
||||
var allowed = Json.Deserialize<List<Quality>>(allowedJson);
|
||||
|
||||
var items = Quality.DefaultQualityDefinitions.OrderBy(v => v.Weight).Select(v => new QualityProfileItem { Quality = v.Quality, Allowed = allowed.Contains(v.Quality) }).ToList();
|
||||
var items = Quality.DefaultQualityDefinitions.OrderBy(v => v.Weight).Select(v => new ProfileQualityItem { Quality = v.Quality, Allowed = allowed.Contains(v.Quality) }).ToList();
|
||||
|
||||
var allowedNewJson = qualityProfileItemConverter.ToDB(items);
|
||||
|
||||
|
31
src/NzbDrone.Core/Datastore/Migration/054_rename_profiles.cs
Normal file
31
src/NzbDrone.Core/Datastore/Migration/054_rename_profiles.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(54)]
|
||||
public class rename_profiles : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Rename.Table("QualityProfiles").To("Profiles");
|
||||
|
||||
Alter.Table("Profiles").AddColumn("Language").AsInt32().Nullable();
|
||||
Alter.Table("Profiles").AddColumn("GrabDelay").AsInt32().Nullable();
|
||||
Alter.Table("Profiles").AddColumn("GrabDelayMode").AsInt32().Nullable();
|
||||
Execute.Sql("UPDATE Profiles SET Language = 1, GrabDelay = 0, GrabDelayMode = 0");
|
||||
|
||||
//Rename QualityProfileId in Series
|
||||
Alter.Table("Series").AddColumn("ProfileId").AsInt32().Nullable();
|
||||
Execute.Sql("UPDATE Series SET ProfileId = QualityProfileId");
|
||||
|
||||
//Add HeldReleases
|
||||
Create.TableForModel("PendingReleases")
|
||||
.WithColumn("SeriesId").AsInt32()
|
||||
.WithColumn("Title").AsString()
|
||||
.WithColumn("Added").AsDateTime()
|
||||
.WithColumn("ParsedEpisodeInfo").AsString()
|
||||
.WithColumn("Release").AsString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(55)]
|
||||
public class drop_old_profile_columns : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
SqLiteAlter.DropColumns("Series", new[] { "QualityProfileId" });
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,12 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
|
||||
public string CreateSql(string tableName)
|
||||
{
|
||||
return string.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName);
|
||||
if (Unique)
|
||||
{
|
||||
return String.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName);
|
||||
}
|
||||
|
||||
return String.Format(@"CREATE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName);
|
||||
}
|
||||
}
|
||||
}
|
@ -147,14 +147,12 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void DropTable(string tableName)
|
||||
{
|
||||
var dropCommand = BuildCommand("DROP TABLE {0};", tableName);
|
||||
dropCommand.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
|
||||
public void RenameTable(string tableName, string newName)
|
||||
{
|
||||
var renameCommand = BuildCommand("ALTER TABLE {0} RENAME TO {1};", tableName, newName);
|
||||
@ -184,7 +182,6 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
return Convert.ToInt32(countCommand.ExecuteScalar());
|
||||
}
|
||||
|
||||
|
||||
public SQLiteTransaction BeginTransaction()
|
||||
{
|
||||
return _connection.BeginTransaction();
|
||||
@ -197,7 +194,6 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
public void ExecuteNonQuery(string command, params string[] args)
|
||||
{
|
||||
var sqLiteCommand = new SQLiteCommand(string.Format(command, args))
|
||||
@ -226,7 +222,5 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Instrumentation;
|
||||
using NzbDrone.Core.Jobs;
|
||||
@ -17,6 +18,8 @@ using NzbDrone.Core.Metadata;
|
||||
using NzbDrone.Core.Metadata.Files;
|
||||
using NzbDrone.Core.Notifications;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.SeriesStats;
|
||||
@ -27,7 +30,6 @@ namespace NzbDrone.Core.Datastore
|
||||
{
|
||||
public static class TableMapping
|
||||
{
|
||||
|
||||
private static readonly FluentMappings Mapper = new FluentMappings(true);
|
||||
|
||||
public static void Map()
|
||||
@ -52,7 +54,7 @@ namespace NzbDrone.Core.Datastore
|
||||
Mapper.Entity<Series>().RegisterModel("Series")
|
||||
.Ignore(s => s.RootFolderPath)
|
||||
.Relationship()
|
||||
.HasOne(s => s.QualityProfile, s => s.QualityProfileId);
|
||||
.HasOne(s => s.Profile, s => s.ProfileId);
|
||||
|
||||
Mapper.Entity<Episode>().RegisterModel("Episodes")
|
||||
.Ignore(e => e.SeriesTitle)
|
||||
@ -64,14 +66,16 @@ namespace NzbDrone.Core.Datastore
|
||||
Mapper.Entity<EpisodeFile>().RegisterModel("EpisodeFiles")
|
||||
.Relationships.AutoMapICollectionOrComplexProperties();
|
||||
|
||||
Mapper.Entity<QualityProfile>().RegisterModel("QualityProfiles");
|
||||
Mapper.Entity<Profile>().RegisterModel("Profiles");
|
||||
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions");
|
||||
Mapper.Entity<Log>().RegisterModel("Logs");
|
||||
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
|
||||
Mapper.Entity<SeriesStatistics>().MapResultSet();
|
||||
Mapper.Entity<Blacklist>().RegisterModel("Blacklist");
|
||||
|
||||
Mapper.Entity<MetadataFile>().RegisterModel("MetadataFiles");
|
||||
|
||||
Mapper.Entity<PendingRelease>().RegisterModel("PendingReleases")
|
||||
.Ignore(e => e.RemoteEpisode);
|
||||
}
|
||||
|
||||
private static void RegisterMappers()
|
||||
@ -84,11 +88,13 @@ namespace NzbDrone.Core.Datastore
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(Boolean), new BooleanIntConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(Enum), new EnumIntConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(Quality), new QualityIntConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<QualityProfileItem>), new EmbeddedDocumentConverter(new QualityIntConverter()));
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<ProfileQualityItem>), 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(List<int>), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(List<string>), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(ParsedEpisodeInfo), new EmbeddedDocumentConverter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter());
|
||||
}
|
||||
|
||||
private static void RegisterProviderSettingConverter()
|
||||
|
@ -2,12 +2,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
public class DownloadDecision
|
||||
{
|
||||
public RemoteEpisode RemoteEpisode { get; private set; }
|
||||
public IEnumerable<string> Rejections { get; private set; }
|
||||
public IEnumerable<Rejection> Rejections { get; private set; }
|
||||
|
||||
public bool Approved
|
||||
{
|
||||
@ -17,13 +17,28 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public DownloadDecision(RemoteEpisode episode, params string[] rejections)
|
||||
public bool TemporarilyRejected
|
||||
{
|
||||
get
|
||||
{
|
||||
return Rejections.Any() && Rejections.All(r => r.Type == RejectionType.Temporary);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Rejected
|
||||
{
|
||||
get
|
||||
{
|
||||
return Rejections.Any() && Rejections.All(r => r.Type == RejectionType.Permanent);
|
||||
}
|
||||
}
|
||||
|
||||
public DownloadDecision(RemoteEpisode episode, params Rejection[] rejections)
|
||||
{
|
||||
RemoteEpisode = episode;
|
||||
Rejections = rejections.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Approved)
|
@ -4,7 +4,6 @@ using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Parser;
|
||||
@ -17,6 +16,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
List<DownloadDecision> GetRssDecision(List<ReleaseInfo> reports);
|
||||
List<DownloadDecision> GetSearchDecision(List<ReleaseInfo> reports, SearchCriteriaBase searchCriteriaBase);
|
||||
DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null);
|
||||
}
|
||||
|
||||
public class DownloadDecisionMaker : IMakeDownloadDecision
|
||||
@ -87,7 +87,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
}
|
||||
else
|
||||
{
|
||||
decision = new DownloadDecision(remoteEpisode, "Unknown Series");
|
||||
decision = new DownloadDecision(remoteEpisode, new Rejection("Unknown Series"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,19 +110,19 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
}
|
||||
}
|
||||
|
||||
private DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null)
|
||||
public DownloadDecision GetDecisionForReport(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria = null)
|
||||
{
|
||||
var reasons = _specifications.Select(c => EvaluateSpec(c, remoteEpisode, searchCriteria))
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c));
|
||||
.Where(c => c != null);
|
||||
|
||||
return new DownloadDecision(remoteEpisode, reasons.ToArray());
|
||||
}
|
||||
|
||||
private string EvaluateSpec(IRejectWithReason spec, RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteriaBase = null)
|
||||
private Rejection EvaluateSpec(IRejectWithReason spec, RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteriaBase = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(spec.RejectionReason))
|
||||
if (spec.RejectionReason.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new InvalidOperationException("[Need Rejection Text]");
|
||||
}
|
||||
@ -130,7 +130,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
var generalSpecification = spec as IDecisionEngineSpecification;
|
||||
if (generalSpecification != null && !generalSpecification.IsSatisfiedBy(remoteEpisode, searchCriteriaBase))
|
||||
{
|
||||
return spec.RejectionReason;
|
||||
return new Rejection(spec.RejectionReason, generalSpecification.Type);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -138,7 +138,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
e.Data.Add("report", remoteEpisode.Release.ToJson());
|
||||
e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson());
|
||||
_logger.ErrorException("Couldn't evaluate decision on " + remoteEpisode.Release.Title, e);
|
||||
return string.Format("{0}: {1}", spec.GetType().Name, e.Message);
|
||||
return new Rejection(String.Format("{0}: {1}", spec.GetType().Name, e.Message));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -19,7 +19,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
return decisions
|
||||
.Where(c => c.RemoteEpisode.Series != null)
|
||||
.GroupBy(c => c.RemoteEpisode.Series.Id, (i, s) => s
|
||||
.OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality, new QualityModelComparer(s.First().RemoteEpisode.Series.QualityProfile))
|
||||
.OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality, new QualityModelComparer(s.First().RemoteEpisode.Series.Profile))
|
||||
.ThenBy(c => c.RemoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault())
|
||||
.ThenBy(c => c.RemoteEpisode.Release.Size.Round(200.Megabytes()) / Math.Max(1, c.RemoteEpisode.Episodes.Count))
|
||||
.ThenBy(c => c.RemoteEpisode.Release.Age))
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
@ -5,6 +6,8 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
public interface IDecisionEngineSpecification : IRejectWithReason
|
||||
{
|
||||
bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria);
|
||||
RejectionType Type { get; }
|
||||
|
||||
Boolean IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria);
|
||||
}
|
||||
}
|
@ -4,4 +4,4 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
string RejectionReason { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
public interface IQualityUpgradableSpecification
|
||||
{
|
||||
bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null);
|
||||
bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null);
|
||||
bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
|
||||
bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
|
||||
bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality);
|
||||
}
|
||||
|
||||
@ -19,7 +20,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null)
|
||||
public bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
|
||||
{
|
||||
if (newQuality != null)
|
||||
{
|
||||
@ -39,7 +40,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null)
|
||||
public bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
|
||||
{
|
||||
int compare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff);
|
||||
|
||||
|
21
src/NzbDrone.Core/DecisionEngine/Rejection.cs
Normal file
21
src/NzbDrone.Core/DecisionEngine/Rejection.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
public class Rejection
|
||||
{
|
||||
public String Reason { get; set; }
|
||||
public RejectionType Type { get; set; }
|
||||
|
||||
public Rejection(string reason, RejectionType type = RejectionType.Permanent)
|
||||
{
|
||||
Reason = reason;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[{0}] {1}", Type, Reason);
|
||||
}
|
||||
}
|
||||
}
|
8
src/NzbDrone.Core/DecisionEngine/RejectionType.cs
Normal file
8
src/NzbDrone.Core/DecisionEngine/RejectionType.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
public enum RejectionType
|
||||
{
|
||||
Permanent = 0,
|
||||
Temporary = 1
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
@ -21,12 +22,14 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason
|
||||
public String RejectionReason
|
||||
{
|
||||
get { return "File size too big or small"; }
|
||||
}
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
_logger.Debug("Beginning size check for: {0}", subject);
|
||||
|
||||
|
@ -27,6 +27,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (!_configService.EnableFailedDownloadHandling)
|
||||
|
@ -24,6 +24,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
|
||||
@ -31,7 +33,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
|
||||
|
||||
|
||||
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.QualityProfile, file.Quality, subject.ParsedEpisodeInfo.Quality))
|
||||
if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, file.Quality, subject.ParsedEpisodeInfo.Quality))
|
||||
{
|
||||
_logger.Debug("Cutoff already met, rejecting.");
|
||||
return false;
|
||||
|
@ -1,6 +1,5 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
@ -18,16 +17,21 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Not English";
|
||||
return "Language is not wanted";
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var wantedLanguage = subject.Series.Profile.Value.Language;
|
||||
|
||||
_logger.Debug("Checking if report meets language requirements. {0}", subject.ParsedEpisodeInfo.Language);
|
||||
if (subject.ParsedEpisodeInfo.Language != Language.English)
|
||||
|
||||
if (subject.ParsedEpisodeInfo.Language != wantedLanguage)
|
||||
{
|
||||
_logger.Debug("Report Language: {0} rejected because it is not English", subject.ParsedEpisodeInfo.Language);
|
||||
_logger.Debug("Report Language: {0} rejected because it is not wanted, wanted {1}", subject.ParsedEpisodeInfo.Language, wantedLanguage);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var queue = _downloadTrackingService.GetQueuedDownloads()
|
||||
@ -46,7 +48,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
private bool IsInQueue(RemoteEpisode newEpisode, IEnumerable<RemoteEpisode> queue)
|
||||
{
|
||||
var matchingSeries = queue.Where(q => q.Series.Id == newEpisode.Series.Id);
|
||||
var matchingSeriesAndQuality = matchingSeries.Where(q => new QualityModelComparer(q.Series.QualityProfile).Compare(q.ParsedEpisodeInfo.Quality, newEpisode.ParsedEpisodeInfo.Quality) >= 0);
|
||||
var matchingSeriesAndQuality = matchingSeries.Where(q => new QualityModelComparer(q.Series.Profile).Compare(q.ParsedEpisodeInfo.Quality, newEpisode.ParsedEpisodeInfo.Quality) >= 0);
|
||||
|
||||
return matchingSeriesAndQuality.Any(q => q.Episodes.Select(e => e.Id).Intersect(newEpisode.Episodes.Select(e => e.Id)).Any());
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
_logger.Debug("Checking if release contains any restricted terms: {0}", subject);
|
||||
|
@ -7,7 +7,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
public class NotSampleSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public string RejectionReason { get { return "Sample"; } }
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public NotSampleSpecification(Logger logger)
|
||||
{
|
||||
|
@ -21,10 +21,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality);
|
||||
if (!subject.Series.QualityProfile.Value.Items.Exists(v => v.Allowed && v.Quality == subject.ParsedEpisodeInfo.Quality.Quality))
|
||||
if (!subject.Series.Profile.Value.Items.Exists(v => v.Allowed && v.Quality == subject.ParsedEpisodeInfo.Quality.Quality))
|
||||
{
|
||||
_logger.Debug("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality);
|
||||
return false;
|
||||
|
@ -25,6 +25,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var age = subject.Release.Age;
|
||||
|
@ -30,6 +30,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (!_configService.EnableFailedDownloadHandling)
|
||||
|
@ -0,0 +1,116 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||
{
|
||||
public class DelaySpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DelaySpecification(IPendingReleaseService pendingReleaseService, Logger logger)
|
||||
{
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string RejectionReason
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Waiting for better quality release";
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Temporary; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
//How do we want to handle drone being off and the automatic search being triggered?
|
||||
//TODO: Add a flag to the search to state it is a "scheduled" search
|
||||
|
||||
if (searchCriteria != null)
|
||||
{
|
||||
_logger.Debug("Ignore delay for searches");
|
||||
return true;
|
||||
}
|
||||
|
||||
var profile = subject.Series.Profile.Value;
|
||||
|
||||
if (profile.GrabDelay == 0)
|
||||
{
|
||||
_logger.Debug("Profile does not delay before download");
|
||||
return true;
|
||||
}
|
||||
|
||||
var comparer = new QualityModelComparer(profile);
|
||||
|
||||
if (subject.ParsedEpisodeInfo.Quality.Proper)
|
||||
{
|
||||
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
|
||||
{
|
||||
if (comparer.Compare(subject.ParsedEpisodeInfo.Quality, file.Quality) > 0)
|
||||
{
|
||||
var properCompare = subject.ParsedEpisodeInfo.Quality.Proper.CompareTo(file.Quality.Proper);
|
||||
|
||||
if (subject.ParsedEpisodeInfo.Quality.Quality == file.Quality.Quality && properCompare > 0)
|
||||
{
|
||||
_logger.Debug("New quality is a proper for existing quality, skipping delay");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If quality meets or exceeds the best allowed quality in the profile accept it immediately
|
||||
var bestQualityInProfile = new QualityModel(profile.Items.Last(q => q.Allowed).Quality);
|
||||
var bestCompare = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile);
|
||||
|
||||
if (bestCompare >= 0)
|
||||
{
|
||||
_logger.Debug("Quality is highest in profile, will not delay");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (profile.GrabDelayMode == GrabDelayMode.Cutoff)
|
||||
{
|
||||
var cutoff = new QualityModel(profile.Cutoff);
|
||||
var cutoffCompare = comparer.Compare(subject.ParsedEpisodeInfo.Quality, cutoff);
|
||||
|
||||
if (cutoffCompare >= 0)
|
||||
{
|
||||
_logger.Debug("Quality meets or exceeds the cutoff, will not delay");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (profile.GrabDelayMode == GrabDelayMode.First)
|
||||
{
|
||||
var episodeIds = subject.Episodes.Select(e => e.Id);
|
||||
|
||||
var oldest = _pendingReleaseService.GetPendingRemoteEpisodes(subject.Series.Id)
|
||||
.Where(r => r.Episodes.Select(e => e.Id).Intersect(episodeIds).Any())
|
||||
.OrderByDescending(p => p.Release.AgeHours)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (oldest != null && oldest.Release.AgeHours > profile.GrabDelay)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (subject.Release.AgeHours < profile.GrabDelay)
|
||||
{
|
||||
_logger.Debug("Age ({0}) is less than delay {1}, delaying", subject.Release.AgeHours, profile.GrabDelay);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null)
|
||||
@ -63,11 +65,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||
|
||||
foreach (var episode in subject.Episodes)
|
||||
{
|
||||
var bestQualityInHistory = _historyService.GetBestQualityInHistory(subject.Series.QualityProfile, episode.Id);
|
||||
var bestQualityInHistory = _historyService.GetBestQualityInHistory(subject.Series.Profile, episode.Id);
|
||||
if (bestQualityInHistory != null)
|
||||
{
|
||||
_logger.Debug("Comparing history quality with report. History is {0}", bestQualityInHistory);
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, bestQualityInHistory, subject.ParsedEpisodeInfo.Quality))
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, bestQualityInHistory, subject.ParsedEpisodeInfo.Quality))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null)
|
||||
|
@ -28,6 +28,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria != null)
|
||||
|
@ -23,6 +23,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
return "Episode doesn't match";
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -23,6 +23,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -21,6 +21,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -21,6 +21,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -22,6 +22,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
|
@ -24,13 +24,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
}
|
||||
|
||||
public RejectionType Type { get { return RejectionType.Permanent; } }
|
||||
|
||||
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
|
||||
{
|
||||
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
|
||||
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, file.Quality, subject.ParsedEpisodeInfo.Quality))
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, file.Quality, subject.ParsedEpisodeInfo.Quality))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public interface IDownloadApprovedReports
|
||||
{
|
||||
List<DownloadDecision> DownloadApproved(List<DownloadDecision> decisions);
|
||||
}
|
||||
|
||||
public class DownloadApprovedReports : IDownloadApprovedReports
|
||||
{
|
||||
private readonly IDownloadService _downloadService;
|
||||
private readonly IPrioritizeDownloadDecision _prioritizeDownloadDecision;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadApprovedReports(IDownloadService downloadService, IPrioritizeDownloadDecision prioritizeDownloadDecision, Logger logger)
|
||||
{
|
||||
_downloadService = downloadService;
|
||||
_prioritizeDownloadDecision = prioritizeDownloadDecision;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<DownloadDecision> DownloadApproved(List<DownloadDecision> decisions)
|
||||
{
|
||||
var qualifiedReports = GetQualifiedReports(decisions);
|
||||
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(qualifiedReports);
|
||||
var downloadedReports = new List<DownloadDecision>();
|
||||
|
||||
foreach (var report in prioritizedDecisions)
|
||||
{
|
||||
var remoteEpisode = report.RemoteEpisode;
|
||||
|
||||
try
|
||||
{
|
||||
if (downloadedReports.SelectMany(r => r.RemoteEpisode.Episodes)
|
||||
.Select(e => e.Id)
|
||||
.ToList()
|
||||
.Intersect(remoteEpisode.Episodes.Select(e => e.Id))
|
||||
.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_downloadService.DownloadReport(remoteEpisode);
|
||||
downloadedReports.Add(report);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.WarnException("Couldn't add report to download queue. " + remoteEpisode, e);
|
||||
}
|
||||
}
|
||||
|
||||
return downloadedReports;
|
||||
}
|
||||
|
||||
public List<DownloadDecision> GetQualifiedReports(IEnumerable<DownloadDecision> decisions)
|
||||
{
|
||||
return decisions.Where(c => c.Approved && c.RemoteEpisode.Episodes.Any()).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,5 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
|
18
src/NzbDrone.Core/Download/Pending/PendingRelease.cs
Normal file
18
src/NzbDrone.Core/Download/Pending/PendingRelease.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download.Pending
|
||||
{
|
||||
public class PendingRelease : ModelBase
|
||||
{
|
||||
public Int32 SeriesId { get; set; }
|
||||
public String Title { get; set; }
|
||||
public DateTime Added { get; set; }
|
||||
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
|
||||
public ReleaseInfo Release { get; set; }
|
||||
|
||||
//Not persisted
|
||||
public RemoteEpisode RemoteEpisode { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Download.Pending
|
||||
{
|
||||
public interface IPendingReleaseRepository : IBasicRepository<PendingRelease>
|
||||
{
|
||||
void DeleteBySeriesId(Int32 seriesId);
|
||||
List<PendingRelease> AllBySeriesId(Int32 seriesId);
|
||||
}
|
||||
|
||||
public class PendingReleaseRepository : BasicRepository<PendingRelease>, IPendingReleaseRepository
|
||||
{
|
||||
public PendingReleaseRepository(IDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
|
||||
public void DeleteBySeriesId(Int32 seriesId)
|
||||
{
|
||||
Delete(r => r.SeriesId == seriesId);
|
||||
}
|
||||
|
||||
public List<PendingRelease> AllBySeriesId(Int32 seriesId)
|
||||
{
|
||||
return Query.Where(p => p.SeriesId == seriesId);
|
||||
}
|
||||
}
|
||||
}
|
219
src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs
Normal file
219
src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs
Normal file
@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Download.Pending
|
||||
{
|
||||
public interface IPendingReleaseService
|
||||
{
|
||||
void Add(DownloadDecision decision);
|
||||
void RemoveGrabbed(List<DownloadDecision> grabbed);
|
||||
void RemoveRejected(List<DownloadDecision> rejected);
|
||||
List<ReleaseInfo> GetPending();
|
||||
List<RemoteEpisode> GetPendingRemoteEpisodes(Int32 seriesId);
|
||||
List<Queue.Queue> GetPendingQueue();
|
||||
}
|
||||
|
||||
public class PendingReleaseService : IPendingReleaseService, IHandle<SeriesDeletedEvent>
|
||||
{
|
||||
private readonly IPendingReleaseRepository _repository;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IDownloadService _downloadService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public PendingReleaseService(IPendingReleaseRepository repository,
|
||||
ISeriesService seriesService,
|
||||
IParsingService parsingService,
|
||||
IDownloadService downloadService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_repository = repository;
|
||||
_seriesService = seriesService;
|
||||
_parsingService = parsingService;
|
||||
_downloadService = downloadService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Add(DownloadDecision decision)
|
||||
{
|
||||
var alreadyPending = GetPendingReleases();
|
||||
|
||||
var episodeIds = decision.RemoteEpisode.Episodes.Select(e => e.Id);
|
||||
|
||||
var existingReports = alreadyPending.Where(r => r.RemoteEpisode.Episodes.Select(e => e.Id)
|
||||
.Intersect(episodeIds)
|
||||
.Any());
|
||||
|
||||
if (existingReports.Any(MatchingReleasePredicate(decision)))
|
||||
{
|
||||
_logger.Debug("This release is already pending, not adding again");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Debug("Adding release to pending releases");
|
||||
Insert(decision);
|
||||
}
|
||||
|
||||
public void RemoveGrabbed(List<DownloadDecision> grabbed)
|
||||
{
|
||||
_logger.Debug("Removing grabbed releases from pending");
|
||||
var alreadyPending = GetPendingReleases();
|
||||
|
||||
foreach (var decision in grabbed)
|
||||
{
|
||||
var decisionLocal = decision;
|
||||
var episodeIds = decisionLocal.RemoteEpisode.Episodes.Select(e => e.Id);
|
||||
|
||||
|
||||
var existingReports = alreadyPending.Where(r => r.RemoteEpisode.Episodes.Select(e => e.Id)
|
||||
.Intersect(episodeIds)
|
||||
.Any());
|
||||
|
||||
foreach (var existingReport in existingReports)
|
||||
{
|
||||
_logger.Debug("Removing previously pending release, as it was grabbed.");
|
||||
Delete(existingReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveRejected(List<DownloadDecision> rejected)
|
||||
{
|
||||
_logger.Debug("Removing failed releases from pending");
|
||||
var pending = GetPendingReleases();
|
||||
|
||||
foreach (var rejectedRelease in rejected)
|
||||
{
|
||||
var matching = pending.SingleOrDefault(MatchingReleasePredicate(rejectedRelease));
|
||||
|
||||
if (matching != null)
|
||||
{
|
||||
_logger.Debug("Removing previously pending release, as it has now been rejected.");
|
||||
Delete(matching);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<ReleaseInfo> GetPending()
|
||||
{
|
||||
return _repository.All().Select(p => p.Release).ToList();
|
||||
}
|
||||
|
||||
public List<RemoteEpisode> GetPendingRemoteEpisodes(int seriesId)
|
||||
{
|
||||
return _repository.AllBySeriesId(seriesId).Select(GetRemoteEpisode).ToList();
|
||||
}
|
||||
|
||||
public List<Queue.Queue> GetPendingQueue()
|
||||
{
|
||||
var queued = new List<Queue.Queue>();
|
||||
|
||||
foreach (var pendingRelease in GetPendingReleases())
|
||||
{
|
||||
foreach (var episode in pendingRelease.RemoteEpisode.Episodes)
|
||||
{
|
||||
var queue = new Queue.Queue
|
||||
{
|
||||
Id = episode.Id ^ (pendingRelease.Id << 16),
|
||||
Series = pendingRelease.RemoteEpisode.Series,
|
||||
Episode = episode,
|
||||
Quality = pendingRelease.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||
Title = pendingRelease.Title,
|
||||
Size = pendingRelease.RemoteEpisode.Release.Size,
|
||||
Sizeleft = pendingRelease.RemoteEpisode.Release.Size,
|
||||
Timeleft =
|
||||
pendingRelease.Release.PublishDate.AddHours(
|
||||
pendingRelease.RemoteEpisode.Series.Profile.Value.GrabDelay)
|
||||
.Subtract(DateTime.UtcNow),
|
||||
Status = "Pending",
|
||||
RemoteEpisode = pendingRelease.RemoteEpisode
|
||||
};
|
||||
queued.Add(queue);
|
||||
}
|
||||
}
|
||||
|
||||
return queued;
|
||||
}
|
||||
|
||||
private List<PendingRelease> GetPendingReleases()
|
||||
{
|
||||
var result = new List<PendingRelease>();
|
||||
|
||||
foreach (var release in _repository.All())
|
||||
{
|
||||
var remoteEpisode = GetRemoteEpisode(release);
|
||||
|
||||
if (remoteEpisode == null) continue;
|
||||
|
||||
release.RemoteEpisode = remoteEpisode;
|
||||
|
||||
result.Add(release);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private RemoteEpisode GetRemoteEpisode(PendingRelease release)
|
||||
{
|
||||
var series = _seriesService.GetSeries(release.SeriesId);
|
||||
|
||||
//Just in case the series was removed, but wasn't cleaned up yet (housekeeper will clean it up)
|
||||
if (series == null) return null;
|
||||
|
||||
var episodes = _parsingService.GetEpisodes(release.ParsedEpisodeInfo, series, true);
|
||||
|
||||
return new RemoteEpisode
|
||||
{
|
||||
Series = series,
|
||||
Episodes = episodes,
|
||||
ParsedEpisodeInfo = release.ParsedEpisodeInfo,
|
||||
Release = release.Release
|
||||
};
|
||||
}
|
||||
|
||||
private void Insert(DownloadDecision decision)
|
||||
{
|
||||
_repository.Insert(new PendingRelease
|
||||
{
|
||||
SeriesId = decision.RemoteEpisode.Series.Id,
|
||||
ParsedEpisodeInfo = decision.RemoteEpisode.ParsedEpisodeInfo,
|
||||
Release = decision.RemoteEpisode.Release,
|
||||
Title = decision.RemoteEpisode.Release.Title,
|
||||
Added = DateTime.UtcNow
|
||||
});
|
||||
|
||||
_eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent());
|
||||
}
|
||||
|
||||
private void Delete(PendingRelease pendingRelease)
|
||||
{
|
||||
_repository.Delete(pendingRelease);
|
||||
_eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent());
|
||||
}
|
||||
|
||||
private Func<PendingRelease, bool> MatchingReleasePredicate(DownloadDecision decision)
|
||||
{
|
||||
return p => p.Title == decision.RemoteEpisode.Release.Title &&
|
||||
p.Release.PublishDate == decision.RemoteEpisode.Release.PublishDate &&
|
||||
p.Release.Indexer == decision.RemoteEpisode.Release.Indexer;
|
||||
}
|
||||
|
||||
public void Handle(SeriesDeletedEvent message)
|
||||
{
|
||||
_repository.DeleteBySeriesId(message.Series.Id);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.Download.Pending
|
||||
{
|
||||
public class PendingReleasesUpdatedEvent : IEvent
|
||||
{
|
||||
}
|
||||
}
|
103
src/NzbDrone.Core/Download/ProcessDownloadDecisions.cs
Normal file
103
src/NzbDrone.Core/Download/ProcessDownloadDecisions.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public interface IProcessDownloadDecisions
|
||||
{
|
||||
ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions);
|
||||
}
|
||||
|
||||
public class ProcessDownloadDecisions : IProcessDownloadDecisions
|
||||
{
|
||||
private readonly IDownloadService _downloadService;
|
||||
private readonly IPrioritizeDownloadDecision _prioritizeDownloadDecision;
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ProcessDownloadDecisions(IDownloadService downloadService,
|
||||
IPrioritizeDownloadDecision prioritizeDownloadDecision,
|
||||
IPendingReleaseService pendingReleaseService,
|
||||
Logger logger)
|
||||
{
|
||||
_downloadService = downloadService;
|
||||
_prioritizeDownloadDecision = prioritizeDownloadDecision;
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions)
|
||||
{
|
||||
var qualifiedReports = GetQualifiedReports(decisions);
|
||||
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(qualifiedReports);
|
||||
var downloadedReports = new List<DownloadDecision>();
|
||||
var pendingReports = new List<DownloadDecision>();
|
||||
|
||||
foreach (var report in prioritizedDecisions)
|
||||
{
|
||||
var remoteEpisode = report.RemoteEpisode;
|
||||
|
||||
if (DownloadingOrPending(downloadedReports, pendingReports, remoteEpisode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (report.TemporarilyRejected)
|
||||
{
|
||||
_pendingReleaseService.Add(report);
|
||||
pendingReports.Add(report);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_downloadService.DownloadReport(remoteEpisode);
|
||||
downloadedReports.Add(report);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//TODO: support for store & forward
|
||||
_logger.WarnException("Couldn't add report to download queue. " + remoteEpisode, e);
|
||||
}
|
||||
}
|
||||
|
||||
return new ProcessedDecisions(downloadedReports, pendingReports);
|
||||
}
|
||||
|
||||
internal List<DownloadDecision> GetQualifiedReports(IEnumerable<DownloadDecision> decisions)
|
||||
{
|
||||
//Process both approved and temporarily rejected
|
||||
return decisions.Where(c => (c.Approved || c.TemporarilyRejected) && c.RemoteEpisode.Episodes.Any()).ToList();
|
||||
}
|
||||
|
||||
private bool DownloadingOrPending(List<DownloadDecision> downloading, List<DownloadDecision> pending, RemoteEpisode remoteEpisode)
|
||||
{
|
||||
var episodeIds = remoteEpisode.Episodes.Select(e => e.Id).ToList();
|
||||
|
||||
if (downloading.SelectMany(r => r.RemoteEpisode.Episodes)
|
||||
.Select(e => e.Id)
|
||||
.ToList()
|
||||
.Intersect(episodeIds)
|
||||
.Any())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pending.SelectMany(r => r.RemoteEpisode.Episodes)
|
||||
.Select(e => e.Id)
|
||||
.ToList()
|
||||
.Intersect(episodeIds)
|
||||
.Any())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
17
src/NzbDrone.Core/Download/ProcessedDecisions.cs
Normal file
17
src/NzbDrone.Core/Download/ProcessedDecisions.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public class ProcessedDecisions
|
||||
{
|
||||
public List<DownloadDecision> Grabbed { get; set; }
|
||||
public List<DownloadDecision> Pending { get; set; }
|
||||
|
||||
public ProcessedDecisions(List<DownloadDecision> grabbed, List<DownloadDecision> pending)
|
||||
{
|
||||
Grabbed = grabbed;
|
||||
Pending = pending;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
@ -17,7 +18,7 @@ namespace NzbDrone.Core.History
|
||||
List<History> All();
|
||||
void Purge();
|
||||
void Trim();
|
||||
QualityModel GetBestQualityInHistory(QualityProfile qualityProfile, int episodeId);
|
||||
QualityModel GetBestQualityInHistory(Profile profile, int episodeId);
|
||||
PagingSpec<History> Paged(PagingSpec<History> pagingSpec);
|
||||
List<History> BetweenDates(DateTime startDate, DateTime endDate, HistoryEventType eventType);
|
||||
List<History> Failed();
|
||||
@ -95,9 +96,9 @@ namespace NzbDrone.Core.History
|
||||
_historyRepository.Trim();
|
||||
}
|
||||
|
||||
public QualityModel GetBestQualityInHistory(QualityProfile qualityProfile, int episodeId)
|
||||
public QualityModel GetBestQualityInHistory(Profile profile, int episodeId)
|
||||
{
|
||||
var comparer = new QualityModelComparer(qualityProfile);
|
||||
var comparer = new QualityModelComparer(profile);
|
||||
return _historyRepository.GetBestQualityInHistory(episodeId)
|
||||
.OrderByDescending(q => q, comparer)
|
||||
.FirstOrDefault();
|
||||
|
@ -0,0 +1,31 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Housekeeping.Housekeepers
|
||||
{
|
||||
public class CleanupOrphanedPendingReleases : IHousekeepingTask
|
||||
{
|
||||
private readonly IDatabase _database;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CleanupOrphanedPendingReleases(IDatabase database, Logger logger)
|
||||
{
|
||||
_database = database;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Clean()
|
||||
{
|
||||
_logger.Debug("Running orphaned pending releases cleanup");
|
||||
|
||||
var mapper = _database.GetDataMapper();
|
||||
|
||||
mapper.ExecuteNonQuery(@"DELETE FROM PendingReleases
|
||||
WHERE Id IN (
|
||||
SELECT PendingReleases.Id FROM PendingReleases
|
||||
LEFT OUTER JOIN Series
|
||||
ON PendingReleases.SeriesId = Series.Id
|
||||
WHERE Series.Id IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
@ -21,19 +21,19 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
public class MissingEpisodeSearchService : IEpisodeSearchService, IExecute<EpisodeSearchCommand>, IExecute<MissingEpisodeSearchCommand>
|
||||
{
|
||||
private readonly ISearchForNzb _nzbSearchService;
|
||||
private readonly IDownloadApprovedReports _downloadApprovedReports;
|
||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly IQueueService _queueService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MissingEpisodeSearchService(ISearchForNzb nzbSearchService,
|
||||
IDownloadApprovedReports downloadApprovedReports,
|
||||
IProcessDownloadDecisions processDownloadDecisions,
|
||||
IEpisodeService episodeService,
|
||||
IQueueService queueService,
|
||||
Logger logger)
|
||||
{
|
||||
_nzbSearchService = nzbSearchService;
|
||||
_downloadApprovedReports = downloadApprovedReports;
|
||||
_processDownloadDecisions = processDownloadDecisions;
|
||||
_episodeService = episodeService;
|
||||
_queueService = queueService;
|
||||
_logger = logger;
|
||||
@ -52,9 +52,10 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
|
||||
foreach (var episode in missing)
|
||||
{
|
||||
//TODO: Add a flag to the search to state it is a "scheduled" search
|
||||
var decisions = _nzbSearchService.EpisodeSearch(episode);
|
||||
var downloaded = _downloadApprovedReports.DownloadApproved(decisions);
|
||||
downloadedCount += downloaded.Count;
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
downloadedCount += processed.Grabbed.Count;
|
||||
}
|
||||
|
||||
_logger.ProgressInfo("Completed search for {0} episodes. {1} reports downloaded.", missing.Count, downloadedCount);
|
||||
@ -65,9 +66,9 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
foreach (var episodeId in message.EpisodeIds)
|
||||
{
|
||||
var decisions = _nzbSearchService.EpisodeSearch(episodeId);
|
||||
var downloaded = _downloadApprovedReports.DownloadApproved(decisions);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
|
||||
_logger.ProgressInfo("Episode search completed. {0} reports downloaded.", downloaded.Count);
|
||||
_logger.ProgressInfo("Episode search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,8 +98,8 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
rateGate.WaitToProceed();
|
||||
var decisions = _nzbSearchService.EpisodeSearch(episode);
|
||||
var downloaded = _downloadApprovedReports.DownloadApproved(decisions);
|
||||
downloadedCount += downloaded.Count;
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
downloadedCount += processed.Grabbed.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,24 +8,24 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
public class SeasonSearchService : IExecute<SeasonSearchCommand>
|
||||
{
|
||||
private readonly ISearchForNzb _nzbSearchService;
|
||||
private readonly IDownloadApprovedReports _downloadApprovedReports;
|
||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SeasonSearchService(ISearchForNzb nzbSearchService,
|
||||
IDownloadApprovedReports downloadApprovedReports,
|
||||
IProcessDownloadDecisions processDownloadDecisions,
|
||||
Logger logger)
|
||||
{
|
||||
_nzbSearchService = nzbSearchService;
|
||||
_downloadApprovedReports = downloadApprovedReports;
|
||||
_processDownloadDecisions = processDownloadDecisions;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Execute(SeasonSearchCommand message)
|
||||
{
|
||||
var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, message.SeasonNumber);
|
||||
var downloaded = _downloadApprovedReports.DownloadApproved(decisions);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
|
||||
_logger.ProgressInfo("Season search completed. {0} reports downloaded.", downloaded.Count);
|
||||
_logger.ProgressInfo("Season search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,17 +10,17 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly ISearchForNzb _nzbSearchService;
|
||||
private readonly IDownloadApprovedReports _downloadApprovedReports;
|
||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SeriesSearchService(ISeriesService seriesService,
|
||||
ISearchForNzb nzbSearchService,
|
||||
IDownloadApprovedReports downloadApprovedReports,
|
||||
IProcessDownloadDecisions processDownloadDecisions,
|
||||
Logger logger)
|
||||
{
|
||||
_seriesService = seriesService;
|
||||
_nzbSearchService = nzbSearchService;
|
||||
_downloadApprovedReports = downloadApprovedReports;
|
||||
_processDownloadDecisions = processDownloadDecisions;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
}
|
||||
|
||||
var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, season.SeasonNumber);
|
||||
downloadedCount += _downloadApprovedReports.DownloadApproved(decisions).Count;
|
||||
downloadedCount += _processDownloadDecisions.ProcessDecisions(decisions).Grabbed.Count;
|
||||
}
|
||||
|
||||
_logger.ProgressInfo("Series search completed. {0} reports downloaded.", downloadedCount);
|
||||
|
8
src/NzbDrone.Core/Indexers/RssSyncCompleteEvent.cs
Normal file
8
src/NzbDrone.Core/Indexers/RssSyncCompleteEvent.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public class RssSyncCompleteEvent : IEvent
|
||||
{
|
||||
}
|
||||
}
|
@ -3,11 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.IndexerSearch;
|
||||
using NzbDrone.Core.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
@ -20,20 +21,26 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
private readonly IFetchAndParseRss _rssFetcherAndParser;
|
||||
private readonly IMakeDownloadDecision _downloadDecisionMaker;
|
||||
private readonly IDownloadApprovedReports _downloadApprovedReports;
|
||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||
private readonly IEpisodeSearchService _episodeSearchService;
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public RssSyncService(IFetchAndParseRss rssFetcherAndParser,
|
||||
IMakeDownloadDecision downloadDecisionMaker,
|
||||
IDownloadApprovedReports downloadApprovedReports,
|
||||
IProcessDownloadDecisions processDownloadDecisions,
|
||||
IEpisodeSearchService episodeSearchService,
|
||||
IPendingReleaseService pendingReleaseService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_rssFetcherAndParser = rssFetcherAndParser;
|
||||
_downloadDecisionMaker = downloadDecisionMaker;
|
||||
_downloadApprovedReports = downloadApprovedReports;
|
||||
_processDownloadDecisions = processDownloadDecisions;
|
||||
_episodeSearchService = episodeSearchService;
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -42,24 +49,35 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
_logger.ProgressInfo("Starting RSS Sync");
|
||||
|
||||
var reports = _rssFetcherAndParser.Fetch();
|
||||
var reports = _rssFetcherAndParser.Fetch().Concat(_pendingReleaseService.GetPending()).ToList();
|
||||
var decisions = _downloadDecisionMaker.GetRssDecision(reports);
|
||||
var downloaded = _downloadApprovedReports.DownloadApproved(decisions);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
_pendingReleaseService.RemoveGrabbed(processed.Grabbed);
|
||||
_pendingReleaseService.RemoveRejected(decisions.Where(d => d.Rejected).ToList());
|
||||
|
||||
_logger.ProgressInfo("RSS Sync Completed. Reports found: {0}, Reports downloaded: {1}", reports.Count, downloaded.Count());
|
||||
var message = String.Format("RSS Sync Completed. Reports found: {0}, Reports grabbed: {1}", reports.Count, processed.Grabbed.Count);
|
||||
|
||||
return downloaded;
|
||||
if (processed.Pending.Any())
|
||||
{
|
||||
message += ", Reports pending: " + processed.Pending.Count;
|
||||
}
|
||||
|
||||
_logger.ProgressInfo(message);
|
||||
|
||||
return processed.Grabbed.Concat(processed.Pending).ToList();
|
||||
}
|
||||
|
||||
public void Execute(RssSyncCommand message)
|
||||
{
|
||||
var downloaded = Sync();
|
||||
var processed = Sync();
|
||||
|
||||
if (message.LastExecutionTime.HasValue && DateTime.UtcNow.Subtract(message.LastExecutionTime.Value).TotalHours > 3)
|
||||
{
|
||||
_logger.Info("RSS Sync hasn't run since: {0}. Searching for any missing episodes since then.", message.LastExecutionTime.Value);
|
||||
_episodeSearchService.MissingEpisodesAiredAfter(message.LastExecutionTime.Value.AddDays(-1), downloaded.SelectMany(d => d.RemoteEpisode.Episodes).Select(e => e.Id));
|
||||
_episodeSearchService.MissingEpisodesAiredAfter(message.LastExecutionTime.Value.AddDays(-1), processed.SelectMany(d => d.RemoteEpisode.Episodes).Select(e => e.Id));
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new RssSyncCompleteEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
var qualifiedImports = decisions.Where(c => c.Approved)
|
||||
.GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s
|
||||
.OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.QualityProfile))
|
||||
.OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.Profile))
|
||||
.ThenByDescending(c => c.LocalEpisode.Size))
|
||||
.SelectMany(c => c)
|
||||
.ToList();
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Parser;
|
||||
@ -61,7 +60,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
|
||||
if (parsedEpisode != null)
|
||||
{
|
||||
if (quality != null && new QualityModelComparer(parsedEpisode.Series.QualityProfile).Compare(quality, parsedEpisode.Quality) > 0)
|
||||
if (quality != null && new QualityModelComparer(parsedEpisode.Series.Profile).Compare(quality, parsedEpisode.Quality) > 0)
|
||||
{
|
||||
_logger.Debug("Using quality from folder: {0}", quality);
|
||||
parsedEpisode.Quality = quality;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
@ -19,7 +18,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
|
||||
public bool IsSatisfiedBy(LocalEpisode localEpisode)
|
||||
{
|
||||
var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile);
|
||||
var qualityComparer = new QualityModelComparer(localEpisode.Series.Profile);
|
||||
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);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user