Merge branch 'quality-definitions' into develop

Conflicts:
	src/UI/Settings/Quality/Profile/EditQualityProfileTemplate.html
	src/UI/app.js
pull/3113/head
Mark McDowall 11 years ago
commit 958c2f1fe1

@ -41,7 +41,8 @@ namespace NzbDrone.Api.Test.MappingTests
[TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))] [TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))]
[TestCase(typeof(DownloadDecision), typeof(ReleaseResource))] [TestCase(typeof(DownloadDecision), typeof(ReleaseResource))]
[TestCase(typeof(Core.History.History), typeof(HistoryResource))] [TestCase(typeof(Core.History.History), typeof(HistoryResource))]
[TestCase(typeof(Quality), typeof(QualityResource))] [TestCase(typeof(QualityProfile), typeof(QualityProfileResource))]
[TestCase(typeof(QualityProfileItem), typeof(QualityProfileItemResource))]
[TestCase(typeof(Log), typeof(LogResource))] [TestCase(typeof(Log), typeof(LogResource))]
[TestCase(typeof(Command), typeof(CommandResource))] [TestCase(typeof(Command), typeof(CommandResource))]
public void matching_fields(Type modelType, Type resourceType) public void matching_fields(Type modelType, Type resourceType)
@ -107,10 +108,10 @@ namespace NzbDrone.Api.Test.MappingTests
[Test] [Test]
public void should_map_qualityprofile() public void should_map_qualityprofile()
{ {
var profileResource = new QualityProfileResource var profileResource = new QualityProfileResource
{ {
Allowed = Builder<QualityResource>.CreateListOfSize(1).Build().ToList(), Cutoff = Quality.WEBDL1080p,
Items = new List<QualityProfileItemResource> { new QualityProfileItemResource { Quality = Quality.WEBDL1080p, Allowed = true } }
}; };

@ -1,5 +1,6 @@
using System; using System;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Api.EpisodeFiles namespace NzbDrone.Api.EpisodeFiles

@ -4,6 +4,7 @@ using NzbDrone.Api.Episodes;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Api.Series; using NzbDrone.Api.Series;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Api.History namespace NzbDrone.Api.History

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Api.Indexers namespace NzbDrone.Api.Indexers

@ -139,6 +139,7 @@
<Compile Include="Missing\MissingModule.cs" /> <Compile Include="Missing\MissingModule.cs" />
<Compile Include="Config\NamingSampleResource.cs" /> <Compile Include="Config\NamingSampleResource.cs" />
<Compile Include="NzbDroneRestModuleWithSignalR.cs" /> <Compile Include="NzbDroneRestModuleWithSignalR.cs" />
<Compile Include="Qualities\QualityProfileValidation.cs" />
<Compile Include="Queue\QueueModule.cs" /> <Compile Include="Queue\QueueModule.cs" />
<Compile Include="Queue\QueueResource.cs" /> <Compile Include="Queue\QueueResource.cs" />
<Compile Include="ResourceChangeMessage.cs" /> <Compile Include="ResourceChangeMessage.cs" />
@ -168,8 +169,8 @@
<Compile Include="NzbDroneApiModule.cs" /> <Compile Include="NzbDroneApiModule.cs" />
<Compile Include="Qualities\QualityProfileResource.cs" /> <Compile Include="Qualities\QualityProfileResource.cs" />
<Compile Include="Qualities\QualityProfileModule.cs" /> <Compile Include="Qualities\QualityProfileModule.cs" />
<Compile Include="Qualities\QualitySizeResource.cs" /> <Compile Include="Qualities\QualityDefinitionResource.cs" />
<Compile Include="Qualities\QualitySizeModule.cs" /> <Compile Include="Qualities\QualityDefinitionModule.cs" />
<Compile Include="Extensions\ReqResExtensions.cs" /> <Compile Include="Extensions\ReqResExtensions.cs" />
<Compile Include="Config\SettingsModule.cs" /> <Compile Include="Config\SettingsModule.cs" />
<Compile Include="System\SystemModule.cs" /> <Compile Include="System\SystemModule.cs" />

@ -0,0 +1,38 @@
using System.Collections.Generic;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Mapping;
namespace NzbDrone.Api.Qualities
{
public class QualityDefinitionModule : NzbDroneRestModule<QualityDefinitionResource>
{
private readonly IQualityDefinitionService _qualityDefinitionService;
public QualityDefinitionModule(IQualityDefinitionService qualityDefinitionService)
{
_qualityDefinitionService = qualityDefinitionService;
GetResourceAll = GetAll;
GetResourceById = GetById;
UpdateResource = Update;
}
private void Update(QualityDefinitionResource resource)
{
var model = resource.InjectTo<QualityDefinition>();
_qualityDefinitionService.Update(model);
}
private QualityDefinitionResource GetById(int id)
{
return _qualityDefinitionService.Get((Quality)id).InjectTo<QualityDefinitionResource>();
}
private List<QualityDefinitionResource> GetAll()
{
return ToListResource(_qualityDefinitionService.All);
}
}
}

@ -0,0 +1,18 @@
using System;
using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Api.Qualities
{
public class QualityDefinitionResource : RestResource
{
public Quality Quality { get; set; }
public String Title { get; set; }
public Int32 Weight { get; set; }
public Int32 MinSize { get; set; }
public Int32 MaxSize { get; set; }
}
}

@ -1,7 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
using System.Linq;
using FluentValidation; using FluentValidation;
namespace NzbDrone.Api.Qualities namespace NzbDrone.Api.Qualities
@ -14,19 +13,14 @@ namespace NzbDrone.Api.Qualities
: base("/qualityprofiles") : base("/qualityprofiles")
{ {
_qualityProfileService = qualityProfileService; _qualityProfileService = qualityProfileService;
SharedValidator.RuleFor(c => c.Name).NotEmpty(); SharedValidator.RuleFor(c => c.Name).NotEmpty();
SharedValidator.RuleFor(c => c.Cutoff).NotNull(); SharedValidator.RuleFor(c => c.Cutoff).NotNull();
SharedValidator.RuleFor(c => c.Allowed).NotEmpty(); SharedValidator.RuleFor(c => c.Items).MustHaveAllowedQuality();//.SetValidator(new AllowedValidator<QualityProfileItemResource>());
GetResourceAll = GetAll; GetResourceAll = GetAll;
GetResourceById = GetById; GetResourceById = GetById;
UpdateResource = Update; UpdateResource = Update;
CreateResource = Create; CreateResource = Create;
DeleteResource = DeleteProfile; DeleteResource = DeleteProfile;
} }
@ -44,38 +38,24 @@ namespace NzbDrone.Api.Qualities
private void Update(QualityProfileResource resource) private void Update(QualityProfileResource resource)
{ {
var model = resource.InjectTo<QualityProfile>(); 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); _qualityProfileService.Update(model);
} }
private QualityProfileResource GetById(int id) private QualityProfileResource GetById(int id)
{ {
return QualityToResource(_qualityProfileService.Get(id)); return _qualityProfileService.Get(id).InjectTo<QualityProfileResource>();
} }
private List<QualityProfileResource> GetAll() private List<QualityProfileResource> GetAll()
{ {
var allProfiles = _qualityProfileService.All(); var profiles = _qualityProfileService.All().InjectTo<List<QualityProfileResource>>();
var profiles = allProfiles.Select(QualityToResource).ToList();
return profiles; return profiles;
} }
private static QualityProfileResource QualityToResource(QualityProfile profile)
{
return new QualityProfileResource
{
Cutoff = profile.Cutoff.InjectTo<QualityResource>(),
Available = Quality.All()
.Where(c => !profile.Allowed.Any(q => c.Id == q.Id))
.InjectTo<List<QualityResource>>(),
Allowed = profile.Allowed.InjectTo<List<QualityResource>>(),
Name = profile.Name,
Id = profile.Id
};
}
} }
} }

@ -1,20 +1,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Api.Qualities namespace NzbDrone.Api.Qualities
{ {
public class QualityProfileResource : RestResource public class QualityProfileResource : RestResource
{ {
public String Name { get; set; } public String Name { get; set; }
public QualityResource Cutoff { get; set; } public Quality Cutoff { get; set; }
public List<QualityResource> Available { get; set; } public List<QualityProfileItemResource> Items { get; set; }
public List<QualityResource> Allowed { get; set; }
} }
public class QualityResource : RestResource public class QualityProfileItemResource : RestResource
{ {
public Int32 Weight { get; set; } public Quality Quality { get; set; }
public String Name { get; set; } public bool Allowed { get; set; }
} }
} }

@ -7,33 +7,28 @@ namespace NzbDrone.Api.Qualities
{ {
public class QualityProfileSchemaModule : NzbDroneRestModule<QualityProfileResource> public class QualityProfileSchemaModule : NzbDroneRestModule<QualityProfileResource>
{ {
public QualityProfileSchemaModule() private readonly IQualityDefinitionService _qualityDefinitionService;
public QualityProfileSchemaModule(IQualityDefinitionService qualityDefinitionService)
: base("/qualityprofiles/schema") : base("/qualityprofiles/schema")
{ {
_qualityDefinitionService = qualityDefinitionService;
GetResourceAll = GetAll; GetResourceAll = GetAll;
} }
private List<QualityProfileResource> GetAll() private List<QualityProfileResource> GetAll()
{ {
var items = _qualityDefinitionService.All()
.OrderBy(v => v.Weight)
.Select(v => new QualityProfileItem { Quality = v.Quality, Allowed = false })
.ToList();
var profile = new QualityProfile(); var profile = new QualityProfile();
profile.Cutoff = Quality.Unknown; profile.Cutoff = Quality.Unknown;
profile.Allowed = new List<Quality>(); profile.Items = items;
return new List<QualityProfileResource>{ QualityToResource(profile)};
}
private static QualityProfileResource QualityToResource(QualityProfile profile)
{
return new QualityProfileResource
{
Available = Quality.All()
.Where(c => !profile.Allowed.Any(q => c.Id == q.Id))
.InjectTo<List<QualityResource>>(),
Allowed = profile.Allowed.InjectTo<List<QualityResource>>(), return new List<QualityProfileResource> { profile.InjectTo<QualityProfileResource>() };
Name = profile.Name,
Id = profile.Id
};
} }
} }
} }

@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Validators;
namespace NzbDrone.Api.Qualities
{
public static class QualityProfileValidation
{
public static IRuleBuilderOptions<T, IList<QualityProfileItemResource>> MustHaveAllowedQuality<T>(this IRuleBuilder<T, IList<QualityProfileItemResource>> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
return ruleBuilder.SetValidator(new AllowedValidator<T>());
}
}
public class AllowedValidator<T> : PropertyValidator
{
public AllowedValidator()
: base("Must contain at least one allowed quality")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var list = context.PropertyValue as IList<QualityProfileItemResource>;
if (list == null)
{
return false;
}
if (!list.Any(c => c.Allowed))
{
return false;
}
return true;
}
}
}

@ -1,38 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Mapping;
namespace NzbDrone.Api.Qualities
{
public class QualitySizeModule : NzbDroneRestModule<QualitySizeResource>
{
private readonly IQualitySizeService _qualityTypeProvider;
public QualitySizeModule(IQualitySizeService qualityTypeProvider)
{
_qualityTypeProvider = qualityTypeProvider;
GetResourceAll = GetAll;
GetResourceById = GetById;
UpdateResource = Update;
}
private void Update(QualitySizeResource resource)
{
var model = resource.InjectTo<QualitySize>();
_qualityTypeProvider.Update(model);
}
private QualitySizeResource GetById(int id)
{
return _qualityTypeProvider.Get(id).InjectTo<QualitySizeResource>();
}
private List<QualitySizeResource> GetAll()
{
return ToListResource(_qualityTypeProvider.All);
}
}
}

@ -1,13 +0,0 @@
using System;
using NzbDrone.Api.REST;
namespace NzbDrone.Api.Qualities
{
public class QualitySizeResource : RestResource
{
public Int32 QualityId { get; set; }
public String Name { get; set; }
public Int32 MinSize { get; set; }
public Int32 MaxSize { get; set; }
}
}

@ -1,5 +1,6 @@
using System; using System;
using NzbDrone.Api.REST; using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Api.Queue namespace NzbDrone.Api.Queue

@ -75,6 +75,7 @@ namespace NzbDrone.Core.Test.Datastore
public void one_to_one() public void one_to_one()
{ {
var episodeFile = Builder<EpisodeFile>.CreateNew() var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(c => c.Quality = new QualityModel())
.BuildNew(); .BuildNew();
Db.Insert(episodeFile); Db.Insert(episodeFile);

@ -81,25 +81,25 @@ namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests
[Test] [Test]
public void should_read_existing_indexes() public void should_read_existing_indexes()
{ {
var indexes = _subject.GetIndexes("QualitySizes"); var indexes = _subject.GetIndexes("QualityDefinitions");
indexes.Should().NotBeEmpty(); indexes.Should().NotBeEmpty();
indexes.Should().OnlyContain(c => c != null); indexes.Should().OnlyContain(c => c != null);
indexes.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Column)); indexes.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Column));
indexes.Should().OnlyContain(c => c.Table == "QualitySizes"); indexes.Should().OnlyContain(c => c.Table == "QualityDefinitions");
indexes.Should().OnlyContain(c => c.Unique); indexes.Should().OnlyContain(c => c.Unique);
} }
[Test] [Test]
public void should_add_indexes_when_creating_new_table() public void should_add_indexes_when_creating_new_table()
{ {
var columns = _subject.GetColumns("QualitySizes"); var columns = _subject.GetColumns("QualityDefinitions");
var indexes = _subject.GetIndexes("QualitySizes"); var indexes = _subject.GetIndexes("QualityDefinitions");
_subject.CreateTable("QualityB", columns.Values, indexes); _subject.CreateTable("QualityDefinitionsB", columns.Values, indexes);
var newIndexes = _subject.GetIndexes("QualityB"); var newIndexes = _subject.GetIndexes("QualityDefinitionsB");
newIndexes.Should().HaveSameCount(indexes); newIndexes.Should().HaveSameCount(indexes);
newIndexes.Select(c=>c.Column).Should().BeEquivalentTo(indexes.Select(c=>c.Column)); newIndexes.Select(c=>c.Column).Should().BeEquivalentTo(indexes.Select(c=>c.Column));

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private RemoteEpisode parseResultSingle; private RemoteEpisode parseResultSingle;
private Series series30minutes; private Series series30minutes;
private Series series60minutes; private Series series60minutes;
private QualitySize qualityType; private QualityDefinition qualityType;
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -47,10 +47,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(c => c.Runtime = 60) .With(c => c.Runtime = 60)
.Build(); .Build();
qualityType = Builder<QualitySize>.CreateNew() qualityType = Builder<QualityDefinition>.CreateNew()
.With(q => q.MinSize = 0) .With(q => q.MinSize = 0)
.With(q => q.MaxSize = 10) .With(q => q.MaxSize = 10)
.With(q => q.QualityId = 1) .With(q => q.Quality = Quality.SDTV)
.Build(); .Build();
} }
@ -61,7 +61,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Release.Size = 184572800; parseResultSingle.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -80,7 +80,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Release.Size = 368572800; parseResultSingle.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -99,7 +99,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Release.Size = 1.Gigabytes(); parseResultSingle.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -118,7 +118,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Release.Size = 1.Gigabytes(); parseResultSingle.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -135,7 +135,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMulti.Series = series30minutes; parseResultMulti.Series = series30minutes;
parseResultMulti.Release.Size = 184572800; parseResultMulti.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -154,7 +154,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMulti.Series = series60minutes; parseResultMulti.Series = series60minutes;
parseResultMulti.Release.Size = 368572800; parseResultMulti.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -173,7 +173,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMulti.Series = series30minutes; parseResultMulti.Series = series30minutes;
parseResultMulti.Release.Size = 1.Gigabytes(); parseResultMulti.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -192,7 +192,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMulti.Series = series60minutes; parseResultMulti.Series = series60minutes;
parseResultMulti.Release.Size = 10.Gigabytes(); parseResultMulti.Release.Size = 10.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -211,7 +211,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Release.Size = 184572800; parseResultSingle.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -230,7 +230,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Release.Size = 368572800; parseResultSingle.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -249,7 +249,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Release.Size = 1.Gigabytes(); parseResultSingle.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -270,7 +270,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Release.Size = 10.Gigabytes(); parseResultSingle.Release.Size = 10.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -292,7 +292,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Release.Size = 18457280000; parseResultSingle.Release.Size = 18457280000;
qualityType.MaxSize = 0; qualityType.MaxSize = 0;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -314,7 +314,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultSingle.Release.Size = 36857280000; parseResultSingle.Release.Size = 36857280000;
qualityType.MaxSize = 0; qualityType.MaxSize = 0;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -338,7 +338,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
qualityType.MaxSize = (int)600.Megabytes(); qualityType.MaxSize = (int)600.Megabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup( Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>())) s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
@ -374,7 +374,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.IsSatisfiedBy(parseResult, null).Should().BeFalse(); Subject.IsSatisfiedBy(parseResult, null).Should().BeFalse();
Mocker.GetMock<IQualitySizeService>().Verify(c=>c.Get(It.IsAny<int>()),Times.Never()); Mocker.GetMock<IQualityDefinitionService>().Verify(c => c.Get(It.IsAny<Quality>()), Times.Never());
} }
} }
} }

@ -13,35 +13,35 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_true_if_current_episode_is_less_than_cutoff() public void should_return_true_if_current_episode_is_less_than_cutoff()
{ {
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.Bluray1080p }, Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.DVD, true)).Should().BeTrue(); new QualityModel(Quality.DVD, true)).Should().BeTrue();
} }
[Test] [Test]
public void should_return_false_if_current_episode_is_equal_to_cutoff() public void should_return_false_if_current_episode_is_equal_to_cutoff()
{ {
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.HDTV720p, true)).Should().BeFalse(); new QualityModel(Quality.HDTV720p, true)).Should().BeFalse();
} }
[Test] [Test]
public void should_return_false_if_current_episode_is_greater_than_cutoff() public void should_return_false_if_current_episode_is_greater_than_cutoff()
{ {
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
} }
[Test] [Test]
public void should_return_true_when_new_episode_is_proper_but_existing_is_not() public void should_return_true_when_new_episode_is_proper_but_existing_is_not()
{ {
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue(); new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue();
} }
[Test] [Test]
public void should_return_false_if_cutoff_is_met_and_quality_is_higher() public void should_return_false_if_cutoff_is_met_and_quality_is_higher()
{ {
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() },
new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
} }
} }

@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
}; };
_fakeSeries = Builder<Series>.CreateNew() _fakeSeries = Builder<Series>.CreateNew()
.With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p }) .With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build(); .Build();
_parseResultMulti = new RemoteEpisode _parseResultMulti = new RemoteEpisode
@ -62,11 +62,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_upgradableQuality = new QualityModel(Quality.SDTV, false); _upgradableQuality = new QualityModel(Quality.SDTV, false);
_notupgradableQuality = new QualityModel(Quality.HDTV1080p, true); _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(1)).Returns(_notupgradableQuality); Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 3)).Returns<QualityModel>(null);
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(2)).Returns(_notupgradableQuality);
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(3)).Returns<QualityModel>(null);
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClient()).Returns(Mocker.GetMock<IDownloadClient>().Object); .Setup(c => c.GetDownloadClient()).Returns(Mocker.GetMock<IDownloadClient>().Object);
@ -74,12 +72,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private void WithFirstReportUpgradable() private void WithFirstReportUpgradable()
{ {
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(1)).Returns(_upgradableQuality); Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 1)).Returns(_upgradableQuality);
} }
private void WithSecondReportUpgradable() private void WithSecondReportUpgradable()
{ {
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(2)).Returns(_upgradableQuality); Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 2)).Returns(_upgradableQuality);
} }
private void GivenSabnzbdDownloadClient() private void GivenSabnzbdDownloadClient()
@ -132,11 +130,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing() public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing()
{ {
_fakeSeries.QualityProfile = new QualityProfile { Cutoff = Quality.WEBDL1080p }; _fakeSeries.QualityProfile = new QualityProfile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false);
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, false); _upgradableQuality = new QualityModel(Quality.WEBDL1080p, false);
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(1)).Returns(_upgradableQuality); Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(It.IsAny<QualityProfile>(), 1)).Returns(_upgradableQuality);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse();
} }

@ -26,7 +26,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_series = Builder<Series>.CreateNew().Build(); _series = Builder<Series>.CreateNew()
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build();
_episode = Builder<Episode>.CreateNew() _episode = Builder<Episode>.CreateNew()
.With(e => e.SeriesId = _series.Id) .With(e => e.SeriesId = _series.Id)

@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_quality_is_defined_in_profile(Quality qualityType) public void should_allow_if_quality_is_defined_in_profile(Quality qualityType)
{ {
remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType; remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType;
remoteEpisode.Series.QualityProfile.Value.Allowed = new List<Quality> { Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p }; remoteEpisode.Series.QualityProfile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
Subject.IsSatisfiedBy(remoteEpisode, null).Should().BeTrue(); Subject.IsSatisfiedBy(remoteEpisode, null).Should().BeTrue();
} }
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType) public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType)
{ {
remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType; remoteEpisode.ParsedEpisodeInfo.Quality.Quality = qualityType;
remoteEpisode.Series.QualityProfile.Value.Allowed = new List<Quality> { Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p }; remoteEpisode.Series.QualityProfile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.DVD, Quality.HDTV720p, Quality.Bluray1080p);
Subject.IsSatisfiedBy(remoteEpisode, null).Should().BeFalse(); Subject.IsSatisfiedBy(remoteEpisode, null).Should().BeFalse();
} }

@ -1,4 +1,5 @@
using FluentAssertions; using System.Linq;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -23,6 +24,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true }, new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true },
new object[] { Quality.WEBDL1080p, false, Quality.WEBDL1080p, false, Quality.WEBDL1080p, false } new object[] { Quality.WEBDL1080p, false, Quality.WEBDL1080p, false, Quality.WEBDL1080p, false }
}; };
[SetUp]
public void Setup()
{
}
private void GivenAutoDownloadPropers(bool autoDownloadPropers) private void GivenAutoDownloadPropers(bool autoDownloadPropers)
{ {
@ -36,7 +43,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenAutoDownloadPropers(true); GivenAutoDownloadPropers(true);
Subject.IsUpgradable(new QualityModel(current, currentProper), new QualityModel(newQuality, newProper)) var qualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() };
Subject.IsUpgradable(qualityProfile, new QualityModel(current, currentProper), new QualityModel(newQuality, newProper))
.Should().Be(expected); .Should().Be(expected);
} }
@ -45,8 +54,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
GivenAutoDownloadPropers(false); GivenAutoDownloadPropers(false);
Subject.IsUpgradable(new QualityModel(Quality.DVD, true), var qualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() };
new QualityModel(Quality.DVD, false)).Should().BeFalse();
Subject.IsUpgradable(qualityProfile, new QualityModel(Quality.DVD, true), new QualityModel(Quality.DVD, false))
.Should().BeFalse();
} }
} }
} }

@ -38,7 +38,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 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() var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p }) .With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build(); .Build();
_parseResultMulti = new RemoteEpisode _parseResultMulti = new RemoteEpisode

@ -37,6 +37,10 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
remoteEpisode.Release = new ReleaseInfo(); remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Release.PublishDate = DateTime.UtcNow; remoteEpisode.Release.PublishDate = DateTime.UtcNow;
remoteEpisode.Series = Builder<Series>.CreateNew()
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build();
return remoteEpisode; return remoteEpisode;
} }

@ -37,6 +37,10 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
remoteEpisode.Release.PublishDate = DateTime.Now.AddDays(-Age); remoteEpisode.Release.PublishDate = DateTime.Now.AddDays(-Age);
remoteEpisode.Release.Size = size; remoteEpisode.Release.Size = size;
remoteEpisode.Series = Builder<Series>.CreateNew()
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build();
return remoteEpisode; return remoteEpisode;
} }

@ -87,7 +87,6 @@ namespace NzbDrone.Core.Test.Download
{ {
Mocker.GetMock<IDownloadClient>().Setup(c => c.IsConfigured).Returns(false); Mocker.GetMock<IDownloadClient>().Setup(c => c.IsConfigured).Returns(false);
Subject.DownloadReport(_parseResult); Subject.DownloadReport(_parseResult);
Mocker.GetMock<IDownloadClient>().Verify(c => c.DownloadNzb(It.IsAny<RemoteEpisode>()), Times.Never()); Mocker.GetMock<IDownloadClient>().Verify(c => c.DownloadNzb(It.IsAny<RemoteEpisode>()), Times.Never());

@ -0,0 +1,59 @@
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.History;
using NzbDrone.Core.Qualities;
using System.Collections.Generic;
using NzbDrone.Core.Test.Qualities;
using FluentAssertions;
namespace NzbDrone.Core.Test.HistoryTests
{
public class HistoryServiceFixture : CoreTest<HistoryService>
{
private QualityProfile _profile;
private QualityProfile _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) };
}
[Test]
public void should_return_null_if_no_history()
{
Mocker.GetMock<IHistoryRepository>()
.Setup(v => v.GetBestQualityInHistory(2))
.Returns(new List<QualityModel>());
var quality = Subject.GetBestQualityInHistory(_profile, 2);
quality.Should().BeNull();
}
[Test]
public void should_return_best_quality()
{
Mocker.GetMock<IHistoryRepository>()
.Setup(v => v.GetBestQualityInHistory(2))
.Returns(new List<QualityModel> { new QualityModel(Quality.DVD), new QualityModel(Quality.Bluray1080p) });
var quality = Subject.GetBestQualityInHistory(_profile, 2);
quality.Should().Be(new QualityModel(Quality.Bluray1080p));
}
[Test]
public void should_return_best_quality_with_custom_order()
{
Mocker.GetMock<IHistoryRepository>()
.Setup(v => v.GetBestQualityInHistory(2))
.Returns(new List<QualityModel> { new QualityModel(Quality.DVD), new QualityModel(Quality.Bluray1080p) });
var quality = Subject.GetBestQualityInHistory(_profileCustom, 2);
quality.Should().Be(new QualityModel(Quality.DVD));
}
}
}

@ -80,6 +80,20 @@ namespace NzbDrone.Core.Test.MediaFiles
.Returns(true); .Returns(true);
Subject.Execute(new DownloadedEpisodesScanCommand()); Subject.Execute(new DownloadedEpisodesScanCommand());
VerifyNoImport();
}
[Test]
public void should_skip_if_no_series_found()
{
Mocker.GetMock<IParsingService>().Setup(c => c.GetSeries("foldername")).Returns((Series)null);
Subject.Execute(new DownloadedEpisodesScanCommand());
Mocker.GetMock<IMakeImportDecision>()
.Verify(c => c.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<bool>(), It.IsAny<Core.Qualities.QualityModel>()),
Times.Never());
VerifyNoImport(); VerifyNoImport();
} }

@ -13,6 +13,7 @@ using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using FizzWare.NBuilder;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{ {
@ -63,7 +64,10 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_fail3.Setup(c => c.RejectionReason).Returns("_fail3"); _fail3.Setup(c => c.RejectionReason).Returns("_fail3");
_videoFiles = new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" }; _videoFiles = new List<string> { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" };
_series = new Series(); _series = Builder<Series>.CreateNew()
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build();
_quality = new QualityModel(Quality.DVD); _quality = new QualityModel(Quality.DVD);
_localEpisode = new LocalEpisode _localEpisode = new LocalEpisode
{ {
@ -80,7 +84,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
Mocker.GetMock<IMediaFileService>() Mocker.GetMock<IMediaFileService>()
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<int>())) .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<int>()))
.Returns(_videoFiles); .Returns(_videoFiles);
} }
private void GivenSpecifications(params Mock<IImportDecisionEngineSpecification>[] mocks) private void GivenSpecifications(params Mock<IImportDecisionEngineSpecification>[] mocks)
@ -162,7 +165,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<int>())) .Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<int>()))
.Returns(_videoFiles); .Returns(_videoFiles);
Subject.GetImportDecisions(_videoFiles, new Series(), false); Subject.GetImportDecisions(_videoFiles, _series, false);
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Verify(c => c.GetEpisodes(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()), Times.Exactly(_videoFiles.Count)); .Verify(c => c.GetEpisodes(It.IsAny<String>(), It.IsAny<Series>(), It.IsAny<Boolean>()), Times.Exactly(_videoFiles.Count));
@ -176,7 +179,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single()); var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false, null); var result = Subject.GetImportDecisions(_videoFiles, _series, false, null);
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality); result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
} }
@ -187,7 +190,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single()); var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false, new QualityModel(Quality.SDTV)); var result = Subject.GetImportDecisions(_videoFiles, _series, false, new QualityModel(Quality.SDTV));
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality); result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
} }
@ -198,7 +201,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenSpecifications(_pass1, _pass2, _pass3); GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = new QualityModel(Quality.Bluray1080p); var expectedQuality = new QualityModel(Quality.Bluray1080p);
var result = Subject.GetImportDecisions(_videoFiles, new Series(), false, expectedQuality); var result = Subject.GetImportDecisions(_videoFiles, _series, false, expectedQuality);
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality); result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
} }

@ -22,13 +22,15 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
public void Setup() public void Setup()
{ {
_series = Builder<Series>.CreateNew() _series = Builder<Series>.CreateNew()
.With(s => s.SeriesType = SeriesTypes.Standard) .With(s => s.SeriesType = SeriesTypes.Standard)
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build(); .Build();
_localEpisode = new LocalEpisode _localEpisode = new LocalEpisode
{ {
Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", Path = @"C:\Test\30 Rock\30.rock.s01e01.avi",
Quality = new QualityModel(Quality.HDTV720p, false) Quality = new QualityModel(Quality.HDTV720p, false),
Series = _series
}; };
} }

@ -29,6 +29,7 @@ namespace NzbDrone.Core.Test.MediaFiles
_approvedDecisions = new List<ImportDecision>(); _approvedDecisions = new List<ImportDecision>();
var series = Builder<Series>.CreateNew() var series = Builder<Series>.CreateNew()
.With(e => e.QualityProfile = new QualityProfile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.Build(); .Build();
var episodes = Builder<Episode>.CreateListOfSize(5) var episodes = Builder<Episode>.CreateListOfSize(5)

@ -131,6 +131,7 @@
<Compile Include="Framework\CoreTest.cs" /> <Compile Include="Framework\CoreTest.cs" />
<Compile Include="Framework\DbTest.cs" /> <Compile Include="Framework\DbTest.cs" />
<Compile Include="Framework\NBuilderExtensions.cs" /> <Compile Include="Framework\NBuilderExtensions.cs" />
<Compile Include="HistoryTests\HistoryServiceFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItemsFixture.cs" /> <Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItemsFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodeFilesFixture.cs" /> <Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodeFilesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupAdditionalNamingSpecsFixture.cs" /> <Compile Include="Housekeeping\Housekeepers\CleanupAdditionalNamingSpecsFixture.cs" />
@ -183,7 +184,7 @@
<Compile Include="ParserTests\ParsingServiceTests\MapFixture.cs" /> <Compile Include="ParserTests\ParsingServiceTests\MapFixture.cs" />
<Compile Include="ParserTests\SeriesTitleInfoFixture.cs" /> <Compile Include="ParserTests\SeriesTitleInfoFixture.cs" />
<Compile Include="Providers\XemProxyFixture.cs" /> <Compile Include="Providers\XemProxyFixture.cs" />
<Compile Include="Qualities\QualitySizeRepositoryFixture.cs" /> <Compile Include="Qualities\QualityDefinitionRepositoryFixture.cs" />
<Compile Include="Qualities\QualityProfileRepositoryFixture.cs" /> <Compile Include="Qualities\QualityProfileRepositoryFixture.cs" />
<Compile Include="RootFolderTests\FreeSpaceOnDrivesFixture.cs" /> <Compile Include="RootFolderTests\FreeSpaceOnDrivesFixture.cs" />
<Compile Include="Qualities\QualityFixture.cs" /> <Compile Include="Qualities\QualityFixture.cs" />
@ -218,14 +219,14 @@
<Compile Include="TvTests\SeriesServiceTests\UpdateSeriesFixture.cs" /> <Compile Include="TvTests\SeriesServiceTests\UpdateSeriesFixture.cs" />
<Compile Include="UpdateTests\UpdateServiceFixture.cs" /> <Compile Include="UpdateTests\UpdateServiceFixture.cs" />
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
<Compile Include="Qualities\QualitySizeServiceFixture.cs" /> <Compile Include="Qualities\QualityDefinitionServiceFixture.cs" />
<Compile Include="TvTests\EpisodeProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" /> <Compile Include="TvTests\EpisodeProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" />
<Compile Include="FluentTest.cs" /> <Compile Include="FluentTest.cs" />
<Compile Include="InstrumentationTests\DatabaseTargetFixture.cs" /> <Compile Include="InstrumentationTests\DatabaseTargetFixture.cs" />
<Compile Include="OrganizerTests\GetNewFilenameFixture.cs" /> <Compile Include="OrganizerTests\GetNewFilenameFixture.cs" />
<Compile Include="DecisionEngineTests\MonitoredEpisodeSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\MonitoredEpisodeSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" /> <Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" />
<Compile Include="TvTests\QualityModelFixture.cs" /> <Compile Include="Qualities\QualityModelComparerFixture.cs" />
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" /> <Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
<Compile Include="HistoryTests\HistoryRepositoryFixture.cs" /> <Compile Include="HistoryTests\HistoryRepositoryFixture.cs" />
<Compile Include="MediaFiles\MediaFileServiceTest.cs" /> <Compile Include="MediaFiles\MediaFileServiceTest.cs" />

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@ -50,6 +51,10 @@ namespace NzbDrone.Core.Test.OrganizerTests
.Build(); .Build();
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "DRONE" }; _episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "DRONE" };
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
} }
private void GivenProper() private void GivenProper()

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -150,9 +151,9 @@ namespace NzbDrone.Core.Test.ParserTests
} }
[Test, TestCaseSource("SelfQualityParserCases")] [Test, TestCaseSource("SelfQualityParserCases")]
public void parsing_our_own_quality_enum(Quality quality) public void parsing_our_own_quality_enum_name(Quality quality)
{ {
var fileName = String.Format("My series S01E01 [{0}]", quality); var fileName = String.Format("My series S01E01 [{0}]", quality.Name);
var result = Parser.QualityParser.ParseQuality(fileName); var result = Parser.QualityParser.ParseQuality(fileName);
result.Quality.Should().Be(quality); result.Quality.Should().Be(quality);
} }

@ -4,25 +4,27 @@ using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using FluentAssertions; using FluentAssertions;
using System.Collections.Generic;
namespace NzbDrone.Core.Test.Qualities namespace NzbDrone.Core.Test.Qualities
{ {
[TestFixture] [TestFixture]
public class QualityDefinitionRepositoryFixture : DbTest<QualityDefinitionRepository, QualityDefinition>
public class QualitySizeRepositoryFixture : DbTest<QualitySizeRepository, QualitySize>
{ {
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
Mocker.SetConstant<IQualitySizeRepository>(Subject); foreach (var qualityDefault in Quality.DefaultQualityDefinitions)
Mocker.Resolve<QualitySizeService>().Handle(new ApplicationStartedEvent()); {
qualityDefault.Id = 0;
Storage.Insert(qualityDefault);
}
} }
[Test] [Test]
public void should_get_quality_size_by_id() public void should_get_qualitydefinition_by_id()
{ {
var size = Subject.GetByQualityId(Quality.Bluray1080p.Id); var size = Subject.GetByQualityId((int)Quality.Bluray1080p);
size.Should().NotBeNull(); size.Should().NotBeNull();
} }

@ -0,0 +1,79 @@
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Qualities
{
[TestFixture]
public class QualityDefinitionServiceFixture : CoreTest<QualityDefinitionService>
{
[Test]
public void init_should_add_all_definitions()
{
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.IsAny<QualityDefinition>()), Times.Exactly(Quality.All.Count));
}
[Test]
public void init_should_insert_any_missing_definitions()
{
Mocker.GetMock<IQualityDefinitionRepository>()
.Setup(s => s.All())
.Returns(new List<QualityDefinition>
{
new QualityDefinition(Quality.SDTV) { Weight = 1, MinSize = 0, MaxSize = 100, Id = 20 }
});
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.IsAny<QualityDefinition>()), Times.Exactly(Quality.All.Count - 1));
}
[Test]
public void init_should_insert_missing_definitions_preserving_weight()
{
// User moved HDTV1080p to a higher weight.
var currentQualities = new List<QualityDefinition>
{
new QualityDefinition(Quality.SDTV) { Id = 5, Title = "SDTV", Weight = 1, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.WEBDL720p) { Id = 2, Title = "720p WEB-DL", Weight = 2, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.HDTV1080p) { Id = 4, Title = "1080p HDTV", Weight = 3, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.WEBDL1080p) { Id = 8, Title = "1080p WEB-DL", Weight = 4, MinSize=0, MaxSize=100 },
};
// Expected to insert Bluray720p above HDTV1080p.
// Expected to insert Bluray1080p above WEBDL1080p.
var addBluray1080p = new List<QualityDefinition>
{
new QualityDefinition(Quality.SDTV) { Title = "SDTV", Weight = 1, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.HDTV1080p) { Title = "1080p HDTV", Weight = 2, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.WEBDL720p) { Title = "720p WEB-DL", Weight = 3, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.Bluray720p) { Title = "720p BluRay", Weight = 4, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.WEBDL1080p) { Title = "1080p WEB-DL", Weight = 5, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.Bluray1080p) { Title = "1080p BluRay", Weight = 6, MinSize=0, MaxSize=100 }
};
Mocker.GetMock<IQualityDefinitionRepository>()
.Setup(v => v.All())
.Returns(currentQualities);
Subject.InsertMissingDefinitions(addBluray1080p);
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.Is<QualityDefinition>(p => p.Quality == Quality.Bluray720p && p.Weight == 4)), Times.Once());
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Update(It.Is<QualityDefinition>(p => p.Quality == Quality.WEBDL1080p && p.Weight == 5)), Times.Once());
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.Is<QualityDefinition>(p => p.Quality == Quality.Bluray1080p && p.Weight == 6)), Times.Once());
}
}
}

@ -1,4 +1,6 @@
using FluentAssertions; using System.Linq;
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -42,81 +44,31 @@ namespace NzbDrone.Core.Test.Qualities
i.Should().Be(expected); i.Should().Be(expected);
} }
public static List<QualityProfileItem> GetDefaultQualities(params Quality[] allowed)
[Test]
public void Icomparer_greater_test()
{
var first = Quality.DVD;
var second = Quality.Bluray1080p;
second.Should().BeGreaterThan(first);
}
[Test]
public void Icomparer_lesser()
{
var first = Quality.DVD;
var second = Quality.Bluray1080p;
first.Should().BeLessThan(second);
}
[Test]
public void equal_operand()
{
var first = Quality.Bluray1080p;
var second = Quality.Bluray1080p;
(first == second).Should().BeTrue();
(first >= second).Should().BeTrue();
(first <= second).Should().BeTrue();
}
[Test]
public void equal_operand_false()
{
var first = Quality.Bluray1080p;
var second = Quality.Unknown;
(first == second).Should().BeFalse();
}
[Test]
public void not_equal_operand()
{
var first = Quality.Bluray1080p;
var second = Quality.Bluray1080p;
(first != second).Should().BeFalse();
}
[Test]
public void not_equal_operand_false()
{ {
var first = Quality.Bluray1080p; var qualities = new List<Quality>
var second = Quality.Unknown; {
Quality.SDTV,
(first != second).Should().BeTrue(); Quality.WEBDL480p,
} Quality.DVD,
Quality.HDTV720p,
[Test] Quality.HDTV1080p,
public void greater_operand() Quality.RAWHD,
{ Quality.WEBDL720p,
var first = Quality.DVD; Quality.Bluray720p,
var second = Quality.Bluray1080p; Quality.WEBDL1080p,
Quality.Bluray1080p
(first < second).Should().BeTrue(); };
(first <= second).Should().BeTrue();
} if (allowed.Length == 0)
allowed = qualities.ToArray();
[Test]
public void lesser_operand() var items = qualities
{ .Except(allowed)
var first = Quality.DVD; .Concat(allowed)
var second = Quality.Bluray1080p; .Select(v => new QualityProfileItem { Quality = v, Allowed = allowed.Contains(v) }).ToList();
(second > first).Should().BeTrue(); return items;
(second >= first).Should().BeTrue();
} }
} }
} }

@ -0,0 +1,91 @@
using System.Linq;
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Qualities
{
[TestFixture]
public class QualityModelComparerFixture : CoreTest
{
public QualityModelComparer Subject { get; set; }
private void GivenDefaultQualityProfile()
{
Subject = new QualityModelComparer(new QualityProfile { Items = QualityFixture.GetDefaultQualities() });
}
private void GivenCustomQualityProfile()
{
Subject = new QualityModelComparer(new QualityProfile { Items = QualityFixture.GetDefaultQualities(Quality.Bluray720p, Quality.DVD) });
}
[Test]
public void Icomparer_greater_test()
{
GivenDefaultQualityProfile();
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
var compare = Subject.Compare(second, first);
compare.Should().BeGreaterThan(0);
}
[Test]
public void Icomparer_greater_proper()
{
GivenDefaultQualityProfile();
var first = new QualityModel(Quality.Bluray1080p, false);
var second = new QualityModel(Quality.Bluray1080p, true);
var compare = Subject.Compare(second, first);
compare.Should().BeGreaterThan(0);
}
[Test]
public void Icomparer_lesser()
{
GivenDefaultQualityProfile();
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
var compare = Subject.Compare(first, second);
compare.Should().BeLessThan(0);
}
[Test]
public void Icomparer_lesser_proper()
{
GivenDefaultQualityProfile();
var first = new QualityModel(Quality.DVD, false);
var second = new QualityModel(Quality.DVD, true);
var compare = Subject.Compare(first, second);
compare.Should().BeLessThan(0);
}
[Test]
public void Icomparer_greater_custom_order()
{
GivenCustomQualityProfile();
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray720p, true);
var compare = Subject.Compare(first, second);
compare.Should().BeGreaterThan(0);
}
}
}

@ -7,7 +7,6 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Qualities namespace NzbDrone.Core.Test.Qualities
{ {
[TestFixture] [TestFixture]
public class QualityProfileRepositoryFixture : DbTest<QualityProfileRepository, QualityProfile> public class QualityProfileRepositoryFixture : DbTest<QualityProfileRepository, QualityProfile>
{ {
[Test] [Test]
@ -15,13 +14,7 @@ namespace NzbDrone.Core.Test.Qualities
{ {
var profile = new QualityProfile var profile = new QualityProfile
{ {
Allowed = new List<Quality> Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
{
Quality.Bluray1080p,
Quality.DVD,
Quality.HDTV720p
},
Cutoff = Quality.Bluray1080p, Cutoff = Quality.Bluray1080p,
Name = "TestProfile" Name = "TestProfile"
}; };
@ -30,8 +23,8 @@ namespace NzbDrone.Core.Test.Qualities
StoredModel.Name.Should().Be(profile.Name); StoredModel.Name.Should().Be(profile.Name);
StoredModel.Cutoff.Should().Be(profile.Cutoff); StoredModel.Cutoff.Should().Be(profile.Cutoff);
StoredModel.Allowed.Should().BeEquivalentTo(profile.Allowed); StoredModel.Items.Should().Equal(profile.Items, (a,b) => a.Quality == b.Quality && a.Allowed == b.Allowed);
} }

@ -1,39 +0,0 @@
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Qualities
{
[TestFixture]
public class QualitySizeServiceFixture : CoreTest<QualitySizeService>
{
[Test]
public void Init_should_add_all_sizes()
{
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualitySizeRepository>()
.Verify(v => v.Insert(It.IsAny<QualitySize>()), Times.Exactly(Quality.All().Count));
}
[Test]
public void Init_should_insert_any_missing_sizes()
{
Mocker.GetMock<IQualitySizeRepository>()
.Setup(s => s.All())
.Returns(new List<QualitySize>
{
new QualitySize { QualityId = 1, Name = "SDTV", MinSize = 0, MaxSize = 100 }
});
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualitySizeRepository>()
.Verify(v => v.Insert(It.IsAny<QualitySize>()), Times.Exactly(Quality.All().Count - 1));
}
}
}

@ -1,125 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.TvTests
{
[TestFixture]
public class QualityModelFixture : CoreTest
{
[Test]
public void Icomparer_greater_test()
{
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
second.Should().BeGreaterThan(first);
}
[Test]
public void Icomparer_greater_proper()
{
var first = new QualityModel(Quality.Bluray1080p, false);
var second = new QualityModel(Quality.Bluray1080p, true);
second.Should().BeGreaterThan(first);
}
[Test]
public void Icomparer_lesser()
{
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
first.Should().BeLessThan(second);
}
[Test]
public void Icomparer_lesser_proper()
{
var first = new QualityModel(Quality.DVD, false);
var second = new QualityModel(Quality.DVD, true);
first.Should().BeLessThan(second);
}
[Test]
public void equal_operand()
{
var first = new QualityModel(Quality.Bluray1080p, true);
var second = new QualityModel(Quality.Bluray1080p, true);
(first == second).Should().BeTrue();
(first >= second).Should().BeTrue();
(first <= second).Should().BeTrue();
}
[Test]
public void equal_operand_false()
{
var first = new QualityModel(Quality.Bluray1080p, true);
var second = new QualityModel(Quality.Unknown, true);
(first == second).Should().BeFalse();
}
[Test]
public void equal_operand_false_proper()
{
var first = new QualityModel(Quality.Bluray1080p, true);
var second = new QualityModel(Quality.Bluray1080p, false);
(first == second).Should().BeFalse();
}
[Test]
public void not_equal_operand()
{
var first = new QualityModel(Quality.Bluray1080p, true);
var second = new QualityModel(Quality.Bluray1080p, true);
(first != second).Should().BeFalse();
}
[Test]
public void not_equal_operand_false()
{
var first = new QualityModel(Quality.Bluray1080p, true);
var second = new QualityModel(Quality.Unknown, true);
(first != second).Should().BeTrue();
}
[Test]
public void not_equal_operand_false_proper()
{
var first = new QualityModel(Quality.Bluray1080p, true);
var second = new QualityModel(Quality.Bluray1080p, false);
(first != second).Should().BeTrue();
}
[Test]
public void greater_operand()
{
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
(first < second).Should().BeTrue();
(first <= second).Should().BeTrue();
}
[Test]
public void lesser_operand()
{
var first = new QualityModel(Quality.DVD, true);
var second = new QualityModel(Quality.Bluray1080p, true);
(second > first).Should().BeTrue();
(second >= first).Should().BeTrue();
}
}
}

@ -17,12 +17,7 @@ namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests
{ {
var profile = new QualityProfile var profile = new QualityProfile
{ {
Allowed = new List<Quality> Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
{
Quality.Bluray1080p,
Quality.DVD,
Quality.HDTV720p
},
Cutoff = Quality.Bluray1080p, Cutoff = Quality.Bluray1080p,
Name = "TestProfile" Name = "TestProfile"

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Blacklisting namespace NzbDrone.Core.Blacklisting

@ -1,12 +1,36 @@
using System; using System;
using Marr.Data.Converters; using Marr.Data.Converters;
using Marr.Data.Mapping; using Marr.Data.Mapping;
using NzbDrone.Common.Serializer; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Converters;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class EmbeddedDocumentConverter : IConverter public class EmbeddedDocumentConverter : IConverter
{ {
private readonly JsonSerializerSettings SerializerSetting;
public EmbeddedDocumentConverter(params JsonConverter[] converters)
{
SerializerSetting = new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.Indented,
DefaultValueHandling = DefaultValueHandling.Include,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
SerializerSetting.Converters.Add(new StringEnumConverter { CamelCaseText = true });
SerializerSetting.Converters.Add(new VersionConverter());
foreach (var converter in converters)
{
SerializerSetting.Converters.Add(converter);
}
}
public virtual object FromDB(ConverterContext context) public virtual object FromDB(ConverterContext context)
{ {
if (context.DbValue == DBNull.Value) if (context.DbValue == DBNull.Value)
@ -20,8 +44,7 @@ namespace NzbDrone.Core.Datastore.Converters
{ {
return null; return null;
} }
return JsonConvert.DeserializeObject(stringValue, context.ColumnMap.FieldType, SerializerSetting);
return Json.Deserialize(stringValue, context.ColumnMap.FieldType);
} }
public object FromDB(ColumnMap map, object dbValue) public object FromDB(ColumnMap map, object dbValue)
@ -33,7 +56,7 @@ namespace NzbDrone.Core.Datastore.Converters
{ {
if (clrValue == null) return null; if (clrValue == null) return null;
return clrValue.ToJson(); return JsonConvert.SerializeObject(clrValue, SerializerSetting);
} }
public Type DbType public Type DbType

@ -2,10 +2,13 @@
using Marr.Data.Converters; using Marr.Data.Converters;
using Marr.Data.Mapping; using Marr.Data.Mapping;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using System.Collections.Generic;
using NzbDrone.Common.Serializer;
using Newtonsoft.Json;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
public class QualityIntConverter : IConverter public class QualityIntConverter : JsonConverter, IConverter
{ {
public object FromDB(ConverterContext context) public object FromDB(ConverterContext context)
{ {
@ -26,7 +29,7 @@ namespace NzbDrone.Core.Datastore.Converters
public object ToDB(object clrValue) public object ToDB(object clrValue)
{ {
if(clrValue == null) return 0; if(clrValue == DBNull.Value) return 0;
if(clrValue as Quality == null) if(clrValue as Quality == null)
{ {
@ -44,5 +47,21 @@ namespace NzbDrone.Core.Datastore.Converters
return typeof(int); return typeof(int);
} }
} }
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Quality);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var item = reader.Value;
return (Quality)Convert.ToInt32(item);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(ToDB(value));
}
} }
} }

@ -0,0 +1,101 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using System.Data;
using System.Linq;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Qualities;
using System.Collections.Generic;
using NzbDrone.Core.Datastore.Converters;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(36)]
public class update_with_quality_converters : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("QualityProfiles").AddColumn("Items").AsString().Nullable();
Execute.WithConnection(ConvertQualityProfiles);
Execute.WithConnection(ConvertQualityModels);
}
private void ConvertQualityProfiles(IDbConnection conn, IDbTransaction tran)
{
var qualityProfileItemConverter = new EmbeddedDocumentConverter(new QualityIntConverter());
// Convert 'Allowed' column in QualityProfiles from Json List<object> to Json List<int> (int = Quality)
using (IDbCommand qualityProfileCmd = conn.CreateCommand())
{
qualityProfileCmd.Transaction = tran;
qualityProfileCmd.CommandText = @"SELECT Id, Allowed FROM QualityProfiles";
using (IDataReader qualityProfileReader = qualityProfileCmd.ExecuteReader())
{
while (qualityProfileReader.Read())
{
var id = qualityProfileReader.GetInt32(0);
var allowedJson = qualityProfileReader.GetString(1);
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 allowedNewJson = qualityProfileItemConverter.ToDB(items);
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE QualityProfiles SET Items = ? WHERE Id = ?";
updateCmd.AddParameter(allowedNewJson);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
private void ConvertQualityModels(IDbConnection conn, IDbTransaction tran)
{
// Converts the QualityModel JSON objects to their new format (only storing the QualityId instead of the entire object)
ConvertQualityModel(conn, tran, "Blacklist");
ConvertQualityModel(conn, tran, "EpisodeFiles");
ConvertQualityModel(conn, tran, "History");
}
private void ConvertQualityModel(IDbConnection conn, IDbTransaction tran, string tableName)
{
var qualityModelConverter = new EmbeddedDocumentConverter(new QualityIntConverter());
using (IDbCommand qualityModelCmd = conn.CreateCommand())
{
qualityModelCmd.Transaction = tran;
qualityModelCmd.CommandText = @"SELECT Id, Quality FROM " + tableName;
using (IDataReader qualityModelReader = qualityModelCmd.ExecuteReader())
{
while (qualityModelReader.Read())
{
var id = qualityModelReader.GetInt32(0);
var qualityJson = qualityModelReader.GetString(1);
var quality = Json.Deserialize<QualityModel>(qualityJson);
var qualityNewJson = qualityModelConverter.ToDB(quality);
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE " + tableName + " SET Quality = ? WHERE Id = ?";
updateCmd.AddParameter(qualityNewJson);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

@ -0,0 +1,63 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using System.Data;
using System.Linq;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(37)]
public class add_configurable_qualities : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
SqLiteAlter.DropColumns("QualityProfiles", new[] { "Allowed" });
Alter.Column("Items").OnTable("QualityProfiles").AsString().NotNullable();
Create.TableForModel("QualityDefinitions")
.WithColumn("Quality").AsInt32().Unique()
.WithColumn("Title").AsString().Unique()
.WithColumn("Weight").AsInt32().Unique()
.WithColumn("MinSize").AsInt32()
.WithColumn("MaxSize").AsInt32();
Execute.WithConnection(ConvertQualities);
Delete.Table("QualitySizes");
}
private void ConvertQualities(IDbConnection conn, IDbTransaction tran)
{
// Convert QualitySizes to a more generic QualityDefinitions table.
using (IDbCommand qualitySizeCmd = conn.CreateCommand())
{
qualitySizeCmd.Transaction = tran;
qualitySizeCmd.CommandText = @"SELECT QualityId, MinSize, MaxSize FROM QualitySizes";
using (IDataReader qualitySizeReader = qualitySizeCmd.ExecuteReader())
{
while (qualitySizeReader.Read())
{
var qualityId = qualitySizeReader.GetInt32(0);
var minSize = qualitySizeReader.GetInt32(1);
var maxSize = qualitySizeReader.GetInt32(2);
var defaultConfig = Quality.DefaultQualityDefinitions.Single(p => (int)p.Quality == qualityId);
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "INSERT INTO QualityDefinitions (Quality, Title, Weight, MinSize, MaxSize) VALUES (?, ?, ?, ?, ?)";
updateCmd.AddParameter(qualityId);
updateCmd.AddParameter(defaultConfig.Title);
updateCmd.AddParameter(defaultConfig.Weight);
updateCmd.AddParameter(minSize);
updateCmd.AddParameter(maxSize);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

@ -10,5 +10,11 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
return expressionRoot.Table(name).WithColumn("Id").AsInt32().PrimaryKey().Identity(); return expressionRoot.Table(name).WithColumn("Id").AsInt32().PrimaryKey().Identity();
} }
public static void AddParameter(this System.Data.IDbCommand command, object value)
{
var parameter = command.CreateParameter();
parameter.Value = value;
command.Parameters.Add(parameter);
}
} }
} }

@ -45,7 +45,6 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
var newIndexes = originalIndexes.Union(indexes); var newIndexes = originalIndexes.Union(indexes);
CreateTable(tableName, columns, newIndexes); CreateTable(tableName, columns, newIndexes);
transaction.Commit(); transaction.Commit();
@ -57,7 +56,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
using (var transaction = _sqLiteMigrationHelper.BeginTransaction()) using (var transaction = _sqLiteMigrationHelper.BeginTransaction())
{ {
var originalColumns = _sqLiteMigrationHelper.GetColumns(tableName); var originalColumns = _sqLiteMigrationHelper.GetColumns(tableName);
var originalIndexes = _sqLiteMigrationHelper.GetIndexes(tableName); var indexes = _sqLiteMigrationHelper.GetIndexes(tableName);
var newColumns = originalColumns.Select(c => var newColumns = originalColumns.Select(c =>
{ {
@ -82,9 +81,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
return c.Value; return c.Value;
}).ToList(); }).ToList();
var newIndexes = originalIndexes; CreateTable(tableName, newColumns, indexes);
CreateTable(tableName, newColumns, newIndexes);
transaction.Commit(); transaction.Commit();
} }

@ -60,7 +60,7 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<QualityProfile>().RegisterModel("QualityProfiles"); Mapper.Entity<QualityProfile>().RegisterModel("QualityProfiles");
Mapper.Entity<QualitySize>().RegisterModel("QualitySizes"); Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions");
Mapper.Entity<Log>().RegisterModel("Logs"); Mapper.Entity<Log>().RegisterModel("Logs");
@ -81,6 +81,8 @@ namespace NzbDrone.Core.Datastore
MapRepository.Instance.RegisterTypeConverter(typeof(Boolean), new BooleanIntConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(Boolean), new BooleanIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(Enum), new EnumIntConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(Enum), new EnumIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(Quality), new QualityIntConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(Quality), new QualityIntConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<QualityProfileItem>), new EmbeddedDocumentConverter(new QualityIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(QualityModel), new EmbeddedDocumentConverter(new QualityIntConverter()));
MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<string, string>), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<string, string>), new EmbeddedDocumentConverter());
MapRepository.Instance.RegisterTypeConverter(typeof(List<int>), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(List<int>), new EmbeddedDocumentConverter());
} }

@ -6,7 +6,7 @@ namespace NzbDrone.Core.DecisionEngine
{ {
public interface IQualityUpgradableSpecification public interface IQualityUpgradableSpecification
{ {
bool IsUpgradable(QualityModel currentQuality, QualityModel newQuality = null); bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null);
bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null); bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null);
bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality); bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality);
} }
@ -20,11 +20,12 @@ namespace NzbDrone.Core.DecisionEngine
_logger = logger; _logger = logger;
} }
public bool IsUpgradable(QualityModel currentQuality, QualityModel newQuality = null) public bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null)
{ {
if (newQuality != null) if (newQuality != null)
{ {
if (currentQuality >= newQuality) int compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality);
if (compare <= 0)
{ {
_logger.Trace("existing item has better or equal quality. skipping"); _logger.Trace("existing item has better or equal quality. skipping");
return false; return false;
@ -41,7 +42,9 @@ namespace NzbDrone.Core.DecisionEngine
public bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) public bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null)
{ {
if (currentQuality.Quality >= profile.Cutoff) int compare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff);
if (compare >= 0)
{ {
if (newQuality != null && IsProperUpgrade(currentQuality, newQuality)) if (newQuality != null && IsProperUpgrade(currentQuality, newQuality))
{ {
@ -57,7 +60,9 @@ namespace NzbDrone.Core.DecisionEngine
public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality) public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality)
{ {
if (currentQuality.Quality == newQuality.Quality && newQuality > currentQuality) int compare = newQuality.Proper.CompareTo(currentQuality.Proper);
if (currentQuality.Quality == newQuality.Quality && compare > 0)
{ {
_logger.Trace("New quality is a proper for existing quality"); _logger.Trace("New quality is a proper for existing quality");
return true; return true;

@ -9,13 +9,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
public class AcceptableSizeSpecification : IDecisionEngineSpecification public class AcceptableSizeSpecification : IDecisionEngineSpecification
{ {
private readonly IQualitySizeService _qualityTypeProvider; private readonly IQualityDefinitionService _qualityDefinitionService;
private readonly IEpisodeService _episodeService; private readonly IEpisodeService _episodeService;
private readonly Logger _logger; private readonly Logger _logger;
public AcceptableSizeSpecification(IQualitySizeService qualityTypeProvider, IEpisodeService episodeService, Logger logger) public AcceptableSizeSpecification(IQualityDefinitionService qualityDefinitionService, IEpisodeService episodeService, Logger logger)
{ {
_qualityTypeProvider = qualityTypeProvider; _qualityDefinitionService = qualityDefinitionService;
_episodeService = episodeService; _episodeService = episodeService;
_logger = logger; _logger = logger;
} }
@ -44,15 +44,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
return false; return false;
} }
var qualityType = _qualityTypeProvider.Get(quality.Id); var qualityDefinition = _qualityDefinitionService.Get(quality);
if (qualityType.MaxSize == 0) if (qualityDefinition.MaxSize == 0)
{ {
_logger.Trace("Max size is 0 (unlimited) - skipping check."); _logger.Trace("Max size is 0 (unlimited) - skipping check.");
return true; return true;
} }
var maxSize = qualityType.MaxSize.Megabytes(); var maxSize = qualityDefinition.MaxSize.Megabytes();
//Multiply maxSize by Series.Runtime //Multiply maxSize by Series.Runtime
maxSize = maxSize * subject.Series.Runtime * subject.Episodes.Count; maxSize = maxSize * subject.Series.Runtime * subject.Episodes.Count;

@ -4,6 +4,8 @@ using NLog;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.DecisionEngine.Specifications namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
@ -44,7 +46,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
private bool IsInQueue(RemoteEpisode newEpisode, IEnumerable<RemoteEpisode> queue) private bool IsInQueue(RemoteEpisode newEpisode, IEnumerable<RemoteEpisode> queue)
{ {
var matchingSeries = queue.Where(q => q.Series.Id == newEpisode.Series.Id); var matchingSeries = queue.Where(q => q.Series.Id == newEpisode.Series.Id);
var matchingSeriesAndQuality = matchingSeries.Where(q => q.ParsedEpisodeInfo.Quality >= newEpisode.ParsedEpisodeInfo.Quality); var matchingSeriesAndQuality = matchingSeries.Where(q => new QualityModelComparer(q.Series.QualityProfile).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()); return matchingSeriesAndQuality.Any(q => q.Episodes.Select(e => e.Id).Intersect(newEpisode.Episodes.Select(e => e.Id)).Any());
} }

@ -24,7 +24,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
_logger.Trace("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality); _logger.Trace("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality);
if (!subject.Series.QualityProfile.Value.Allowed.Contains(subject.ParsedEpisodeInfo.Quality.Quality)) if (!subject.Series.QualityProfile.Value.Items.Exists(v => v.Allowed && v.Quality == subject.ParsedEpisodeInfo.Quality.Quality))
{ {
_logger.Trace("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality); _logger.Trace("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality);
return false; return false;

@ -59,11 +59,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
foreach (var episode in subject.Episodes) foreach (var episode in subject.Episodes)
{ {
var bestQualityInHistory = _historyService.GetBestQualityInHistory(episode.Id); var bestQualityInHistory = _historyService.GetBestQualityInHistory(subject.Series.QualityProfile, episode.Id);
if (bestQualityInHistory != null) if (bestQualityInHistory != null)
{ {
_logger.Trace("Comparing history quality with report. History is {0}", bestQualityInHistory); _logger.Trace("Comparing history quality with report. History is {0}", bestQualityInHistory);
if (!_qualityUpgradableSpecification.IsUpgradable(bestQualityInHistory, subject.ParsedEpisodeInfo.Quality)) if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, bestQualityInHistory, subject.ParsedEpisodeInfo.Quality))
return false; return false;
} }
} }

@ -30,7 +30,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{ {
_logger.Trace("Comparing file quality with report. Existing file is {0}", file.Quality); _logger.Trace("Comparing file quality with report. Existing file is {0}", file.Quality);
if (!_qualityUpgradableSpecification.IsUpgradable(file.Quality, subject.ParsedEpisodeInfo.Quality)) if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, file.Quality, subject.ParsedEpisodeInfo.Quality))
{ {
return false; return false;
} }

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Download
{ {
@ -16,7 +18,7 @@ namespace NzbDrone.Core.Download
private readonly IDownloadService _downloadService; private readonly IDownloadService _downloadService;
private readonly Logger _logger; private readonly Logger _logger;
public DownloadApprovedReports(IDownloadService downloadService, Logger logger) public DownloadApprovedReports(IDownloadService downloadService, Logger logger)
{ {
_downloadService = downloadService; _downloadService = downloadService;
_logger = logger; _logger = logger;
@ -57,11 +59,13 @@ namespace NzbDrone.Core.Download
public List<DownloadDecision> GetQualifiedReports(IEnumerable<DownloadDecision> decisions) public List<DownloadDecision> GetQualifiedReports(IEnumerable<DownloadDecision> decisions)
{ {
return decisions.Where(c => c.Approved && c.RemoteEpisode.Episodes.Any()) return decisions.Where(c => c.Approved && c.RemoteEpisode.Episodes.Any())
.OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality) .GroupBy(c => c.RemoteEpisode.Series.Id, (i,s) => s
.ThenBy(c => c.RemoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault()) .OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality, new QualityModelComparer(s.First().RemoteEpisode.Series.QualityProfile))
.ThenBy(c => c.RemoteEpisode.Release.Size.Round(200.Megabytes()) / c.RemoteEpisode.Episodes.Count) .ThenBy(c => c.RemoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault())
.ThenBy(c => c.RemoteEpisode.Release.Age) .ThenBy(c => c.RemoteEpisode.Release.Size.Round(200.Megabytes()) / c.RemoteEpisode.Episodes.Count)
.ToList(); .ThenBy(c => c.RemoteEpisode.Release.Age))
.SelectMany(c => c)
.ToList();
} }
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Download

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History

@ -4,6 +4,7 @@ using System.Linq;
using Marr.Data.QGen; using Marr.Data.QGen;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History

@ -6,6 +6,7 @@ using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History
@ -15,7 +16,7 @@ namespace NzbDrone.Core.History
List<History> All(); List<History> All();
void Purge(); void Purge();
void Trim(); void Trim();
QualityModel GetBestQualityInHistory(int episodeId); QualityModel GetBestQualityInHistory(QualityProfile qualityProfile, int episodeId);
PagingSpec<History> Paged(PagingSpec<History> pagingSpec); PagingSpec<History> Paged(PagingSpec<History> pagingSpec);
List<History> BetweenDates(DateTime startDate, DateTime endDate, HistoryEventType eventType); List<History> BetweenDates(DateTime startDate, DateTime endDate, HistoryEventType eventType);
List<History> Failed(); List<History> Failed();
@ -80,9 +81,12 @@ namespace NzbDrone.Core.History
_historyRepository.Trim(); _historyRepository.Trim();
} }
public QualityModel GetBestQualityInHistory(int episodeId) public QualityModel GetBestQualityInHistory(QualityProfile qualityProfile, int episodeId)
{ {
return _historyRepository.GetBestQualityInHistory(episodeId).OrderByDescending(q => q).FirstOrDefault(); var comparer = new QualityModelComparer(qualityProfile);
return _historyRepository.GetBestQualityInHistory(episodeId)
.OrderByDescending(q => q, comparer)
.FirstOrDefault();
} }
public void Handle(EpisodeGrabbedEvent message) public void Handle(EpisodeGrabbedEvent message)

@ -10,6 +10,7 @@ using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles namespace NzbDrone.Core.MediaFiles

@ -1,5 +1,6 @@
using System; using System;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles namespace NzbDrone.Core.MediaFiles

@ -7,6 +7,8 @@ using NzbDrone.Common;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles.EpisodeImport namespace NzbDrone.Core.MediaFiles.EpisodeImport
@ -40,8 +42,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
public List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownload = false) public List<ImportDecision> Import(List<ImportDecision> decisions, bool newDownload = false)
{ {
var qualifiedImports = decisions.Where(c => c.Approved) var qualifiedImports = decisions.Where(c => c.Approved)
.OrderByDescending(c => c.LocalEpisode.Quality) .GroupBy(c => c.LocalEpisode.Series.Id, (i, s) => s
.ThenByDescending(c => c.LocalEpisode.Size); .OrderByDescending(c => c.LocalEpisode.Quality, new QualityModelComparer(s.First().LocalEpisode.Series.QualityProfile))
.ThenByDescending(c => c.LocalEpisode.Size))
.SelectMany(c => c)
.ToList();
var imported = new List<ImportDecision>(); var imported = new List<ImportDecision>();

@ -7,6 +7,7 @@ using NzbDrone.Common.Disk;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -60,7 +61,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
if (parsedEpisode != null) if (parsedEpisode != null)
{ {
if (quality != null && quality > parsedEpisode.Quality) if (quality != null && new QualityModelComparer(parsedEpisode.Series.QualityProfile).Compare(quality, parsedEpisode.Quality) > 0)
{ {
_logger.Trace("Using quality from folder: {0}", quality); _logger.Trace("Using quality from folder: {0}", quality);
parsedEpisode.Quality = quality; parsedEpisode.Quality = quality;

@ -1,6 +1,8 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
{ {
@ -17,7 +19,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
public bool IsSatisfiedBy(LocalEpisode localEpisode) public bool IsSatisfiedBy(LocalEpisode localEpisode)
{ {
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && e.EpisodeFile.Value.Quality > localEpisode.Quality)) var qualityComparer = new QualityModelComparer(localEpisode.Series.QualityProfile);
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0))
{ {
_logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path); _logger.Trace("This file isn't an upgrade for all episodes. Skipping {0}", localEpisode.Path);
return false; return false;

@ -5,6 +5,7 @@ using NLog;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications namespace NzbDrone.Core.Notifications

@ -192,6 +192,8 @@
<Compile Include="Datastore\Migration\033_add_api_key_to_pushover.cs" /> <Compile Include="Datastore\Migration\033_add_api_key_to_pushover.cs" />
<Compile Include="Datastore\Migration\034_remove_series_contraints.cs" /> <Compile Include="Datastore\Migration\034_remove_series_contraints.cs" />
<Compile Include="Datastore\Migration\035_add_series_folder_format_to_naming_config.cs" /> <Compile Include="Datastore\Migration\035_add_series_folder_format_to_naming_config.cs" />
<Compile Include="Datastore\Migration\036_update_with_quality_converters.cs" />
<Compile Include="Datastore\Migration\037_add_configurable_qualities.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
@ -459,12 +461,14 @@
<Compile Include="Parser\Parser.cs" /> <Compile Include="Parser\Parser.cs" />
<Compile Include="Parser\ParsingService.cs" /> <Compile Include="Parser\ParsingService.cs" />
<Compile Include="Parser\QualityParser.cs" /> <Compile Include="Parser\QualityParser.cs" />
<Compile Include="Qualities\QualityModelComparer.cs" />
<Compile Include="Qualities\QualityProfileItem.cs" />
<Compile Include="Rest\JsonNetSerializer.cs" /> <Compile Include="Rest\JsonNetSerializer.cs" />
<Compile Include="RootFolders\RootFolderRepository.cs" /> <Compile Include="RootFolders\RootFolderRepository.cs" />
<Compile Include="ThingiProvider\ConfigContractNotFoundException.cs" /> <Compile Include="ThingiProvider\ConfigContractNotFoundException.cs" />
<Compile Include="ThingiProvider\IProvider.cs" /> <Compile Include="ThingiProvider\IProvider.cs" />
<Compile Include="Qualities\QualityProfileInUseException.cs" /> <Compile Include="Qualities\QualityProfileInUseException.cs" />
<Compile Include="Qualities\QualitySizeRepository.cs" /> <Compile Include="Qualities\QualityDefinitionRepository.cs" />
<Compile Include="Qualities\QualityProfileRepository.cs" /> <Compile Include="Qualities\QualityProfileRepository.cs" />
<Compile Include="Queue\Queue.cs" /> <Compile Include="Queue\Queue.cs" />
<Compile Include="Queue\UpdateQueueEvent.cs" /> <Compile Include="Queue\UpdateQueueEvent.cs" />
@ -492,7 +496,7 @@
<Compile Include="Tv\Commands\RefreshSeriesCommand.cs" /> <Compile Include="Tv\Commands\RefreshSeriesCommand.cs" />
<Compile Include="Tv\RefreshEpisodeService.cs" /> <Compile Include="Tv\RefreshEpisodeService.cs" />
<Compile Include="Tv\SeriesRepository.cs" /> <Compile Include="Tv\SeriesRepository.cs" />
<Compile Include="Tv\QualityModel.cs" /> <Compile Include="Qualities\QualityModel.cs" />
<Compile Include="Download\Clients\Sabnzbd\SabAddResponse.cs" /> <Compile Include="Download\Clients\Sabnzbd\SabAddResponse.cs" />
<Compile Include="Download\Clients\Sabnzbd\SabHistoryItem.cs" /> <Compile Include="Download\Clients\Sabnzbd\SabHistoryItem.cs" />
<Compile Include="Download\Clients\Sabnzbd\SabHistory.cs" /> <Compile Include="Download\Clients\Sabnzbd\SabHistory.cs" />
@ -578,7 +582,7 @@
<Compile Include="Qualities\QualityProfileService.cs"> <Compile Include="Qualities\QualityProfileService.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Qualities\QualitySizeService.cs"> <Compile Include="Qualities\QualityDefinitionService.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="RootFolders\UnmappedFolder.cs" /> <Compile Include="RootFolders\UnmappedFolder.cs" />
@ -600,7 +604,7 @@
<Compile Include="Tv\Episode.cs" /> <Compile Include="Tv\Episode.cs" />
<Compile Include="Instrumentation\Log.cs" /> <Compile Include="Instrumentation\Log.cs" />
<Compile Include="History\History.cs" /> <Compile Include="History\History.cs" />
<Compile Include="Qualities\QualitySize.cs" /> <Compile Include="Qualities\QualityDefinition.cs" />
<Compile Include="Qualities\QualityProfile.cs" /> <Compile Include="Qualities\QualityProfile.cs" />
<Compile Include="RootFolders\RootFolder.cs" /> <Compile Include="RootFolders\RootFolder.cs" />
<Compile Include="Tv\Series.cs" /> <Compile Include="Tv\Series.cs" />

@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
using NLog; using NLog;
using NzbDrone.Common.Cache; using NzbDrone.Common.Cache;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Organizer namespace NzbDrone.Core.Organizer
@ -22,6 +23,7 @@ namespace NzbDrone.Core.Organizer
public class FileNameBuilder : IBuildFileNames public class FileNameBuilder : IBuildFileNames
{ {
private readonly INamingConfigService _namingConfigService; private readonly INamingConfigService _namingConfigService;
private readonly IQualityDefinitionService _qualityDefinitionService;
private readonly ICached<EpisodeFormat> _patternCache; private readonly ICached<EpisodeFormat> _patternCache;
private readonly Logger _logger; private readonly Logger _logger;
@ -43,10 +45,12 @@ namespace NzbDrone.Core.Organizer
RegexOptions.Compiled | RegexOptions.IgnoreCase); RegexOptions.Compiled | RegexOptions.IgnoreCase);
public FileNameBuilder(INamingConfigService namingConfigService, public FileNameBuilder(INamingConfigService namingConfigService,
IQualityDefinitionService qualityDefinitionService,
ICacheManger cacheManger, ICacheManger cacheManger,
Logger logger) Logger logger)
{ {
_namingConfigService = namingConfigService; _namingConfigService = namingConfigService;
_qualityDefinitionService = qualityDefinitionService;
_patternCache = cacheManger.GetCache<EpisodeFormat>(GetType()); _patternCache = cacheManger.GetCache<EpisodeFormat>(GetType());
_logger = logger; _logger = logger;
} }
@ -87,12 +91,10 @@ namespace NzbDrone.Core.Organizer
sortedEpisodes.First().Title sortedEpisodes.First().Title
}; };
var tokenValues = new Dictionary<string, string>(FilenameBuilderTokenEqualityComparer.Instance) var tokenValues = new Dictionary<string, string>(FilenameBuilderTokenEqualityComparer.Instance);
{
{"{Series Title}", series.Title},
{"Original Title", episodeFile.SceneName}
};
tokenValues.Add("{Series Title}", series.Title);
tokenValues.Add("{Original Title}", episodeFile.SceneName);
tokenValues.Add("{Release Group}", episodeFile.ReleaseGroup); tokenValues.Add("{Release Group}", episodeFile.ReleaseGroup);
if (series.SeriesType == SeriesTypes.Daily) if (series.SeriesType == SeriesTypes.Daily)
@ -146,9 +148,8 @@ namespace NzbDrone.Core.Organizer
} }
tokenValues.Add("{Episode Title}", GetEpisodeTitle(episodeTitles)); tokenValues.Add("{Episode Title}", GetEpisodeTitle(episodeTitles));
tokenValues.Add("{Quality Title}", episodeFile.Quality.ToString()); tokenValues.Add("{Quality Title}", GetQualityTitle(episodeFile.Quality));
return CleanFilename(ReplaceTokens(pattern, tokenValues).Trim()); return CleanFilename(ReplaceTokens(pattern, tokenValues).Trim());
} }
@ -341,6 +342,14 @@ namespace NzbDrone.Core.Organizer
return String.Join(" + ", episodeTitles.Select(Parser.Parser.CleanupEpisodeTitle).Distinct()); return String.Join(" + ", episodeTitles.Select(Parser.Parser.CleanupEpisodeTitle).Distinct());
} }
private string GetQualityTitle(QualityModel quality)
{
if (quality.Proper)
return _qualityDefinitionService.Get(quality.Quality).Title + " Proper";
else
return _qualityDefinitionService.Get(quality.Quality).Title;
}
} }
public enum MultiEpisodeStyle public enum MultiEpisodeStyle

@ -1,7 +1,8 @@
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using System.Linq;
namespace NzbDrone.Core.Parser.Model namespace NzbDrone.Core.Parser.Model
{ {

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Parser.Model namespace NzbDrone.Core.Parser.Model

@ -2,65 +2,24 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Converters;
namespace NzbDrone.Core.Qualities namespace NzbDrone.Core.Qualities
{ {
public class Quality : IComparable<Quality>, IEmbeddedDocument public class Quality : IEmbeddedDocument, IEquatable<Quality>
{ {
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public int Weight { get; set; }
public int CompareTo(Quality other)
{
if (other.Weight > Weight)
return -1;
if (other.Weight < Weight)
return 1;
if (other.Weight == Weight)
return 0;
return 0;
}
public static bool operator !=(Quality x, Quality y) public Quality()
{ {
return !(x == y);
}
public static bool operator ==(Quality x, Quality y)
{
var xObj = (Object)x;
var yObj = (object)y;
if (xObj == null || yObj == null)
{
return xObj == yObj;
}
return x.CompareTo(y) == 0;
} }
public static bool operator >(Quality x, Quality y) private Quality(int id, string name)
{ {
return x.CompareTo(y) > 0; Id = id;
} Name = name;
public static bool operator <(Quality x, Quality y)
{
return x.CompareTo(y) < 0;
}
public static bool operator <=(Quality x, Quality y)
{
return x.CompareTo(y) <= 0;
}
public static bool operator >=(Quality x, Quality y)
{
return x.CompareTo(y) >= 0;
} }
public override string ToString() public override string ToString()
@ -70,110 +29,96 @@ namespace NzbDrone.Core.Qualities
public override int GetHashCode() public override int GetHashCode()
{ {
unchecked // Overflow is fine, just wrap return Id.GetHashCode();
{
int hash = 17;
hash = hash * 23 + Weight.GetHashCode();
return hash;
}
} }
public bool Equals(Quality other) public bool Equals(Quality other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;
return Equals(other.Weight, Weight); return Id.Equals(other.Id);
} }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true; if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(Quality)) return false;
return Equals((Quality)obj);
}
public static Quality Unknown return Equals(obj as Quality);
{
get { return new Quality { Id = 0, Name = "Unknown", Weight = 0 }; }
} }
public static Quality SDTV public static bool operator ==(Quality left, Quality right)
{ {
get { return new Quality { Id = 1, Name = "SDTV", Weight = 1 }; } return Equals(left, right);
} }
public static Quality WEBDL480p public static bool operator !=(Quality left, Quality right)
{ {
get { return new Quality { Id = 8, Name = "WEBDL-480p", Weight = 2 }; } return !Equals(left, right);
} }
public static Quality DVD public static Quality Unknown { get { return new Quality(0, "Unknown"); } }
public static Quality SDTV { get { return new Quality(1, "SDTV"); } }
public static Quality DVD { get { return new Quality(2, "DVD"); } }
public static Quality WEBDL1080p { get { return new Quality(3, "WEBDL-1080p"); } }
public static Quality HDTV720p { get { return new Quality(4, "HDTV-720p"); } }
public static Quality WEBDL720p { get { return new Quality(5, "WEBDL-720p"); } }
public static Quality Bluray720p { get { return new Quality(6, "Bluray-720p"); } }
public static Quality Bluray1080p { get { return new Quality(7, "Bluray-1080p"); } }
public static Quality WEBDL480p { get { return new Quality(8, "WEBDL-480p"); } }
public static Quality HDTV1080p { get { return new Quality(9, "HDTV-1080p"); } }
public static Quality RAWHD { get { return new Quality(10, "Raw-HD"); } }
//public static Quality HDTV480p { get { return new Quality(11, "HDTV-480p"); } }
public static List<Quality> All
{ {
get { return new Quality { Id = 2, Name = "DVD", Weight = 3 }; } get
} {
return new List<Quality>
public static Quality HDTV720p {
{ SDTV,
get { return new Quality { Id = 4, Name = "HDTV-720p", Weight = 4 }; } DVD,
} WEBDL1080p,
HDTV720p,
public static Quality HDTV1080p WEBDL720p,
{ Bluray720p,
get { return new Quality { Id = 9, Name = "HDTV-1080p", Weight = 5 }; } Bluray1080p,
} WEBDL480p,
HDTV1080p,
public static Quality RAWHD RAWHD
{ };
get { return new Quality { Id = 10, Name = "Raw-HD", Weight = 6 }; } }
}
public static Quality WEBDL720p
{
get { return new Quality { Id = 5, Name = "WEBDL-720p", Weight = 7 }; }
}
public static Quality Bluray720p
{
get { return new Quality { Id = 6, Name = "Bluray720p", Weight = 8 }; }
}
public static Quality WEBDL1080p
{
get { return new Quality { Id = 3, Name = "WEBDL-1080p", Weight = 9 }; }
}
public static Quality Bluray1080p
{
get { return new Quality { Id = 7, Name = "Bluray1080p", Weight = 10 }; }
} }
public static List<Quality> All() public static HashSet<QualityDefinition> DefaultQualityDefinitions
{ {
return new List<Quality> get
{ {
SDTV, return new HashSet<QualityDefinition>
WEBDL480p, {
DVD, new QualityDefinition(Quality.SDTV) { /*Title = "SDTV", */ Weight = 1, MinSize=0, MaxSize=100 },
HDTV720p, new QualityDefinition(Quality.WEBDL480p) { /*Title = "WEB-DL", */ Weight = 2, MinSize=0, MaxSize=100 },
HDTV1080p, new QualityDefinition(Quality.DVD) { /*Title = "DVD", */ Weight = 3, MinSize=0, MaxSize=100 },
RAWHD, new QualityDefinition(Quality.HDTV720p) { /*Title = "720p HDTV", */ Weight = 4, MinSize=0, MaxSize=100 },
WEBDL720p, new QualityDefinition(Quality.HDTV1080p) { /*Title = "1080p HDTV", */ Weight = 5, MinSize=0, MaxSize=100 },
WEBDL1080p, new QualityDefinition(Quality.RAWHD) { /*Title = "RawHD", */ Weight = 6, MinSize=0, MaxSize=100 },
Bluray720p, new QualityDefinition(Quality.WEBDL720p) { /*Title = "720p WEB-DL", */ Weight = 7, MinSize=0, MaxSize=100 },
Bluray1080p new QualityDefinition(Quality.Bluray720p) { /*Title = "720p BluRay", */ Weight = 8, MinSize=0, MaxSize=100 },
}; new QualityDefinition(Quality.WEBDL1080p) { /*Title = "1080p WEB-DL",*/ Weight = 9, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.Bluray1080p) { /*Title = "1080p BluRay",*/ Weight = 10, MinSize=0, MaxSize=100 }
};
}
} }
public static Quality FindById(int id) public static Quality FindById(int id)
{ {
if (id == 0) return Unknown; if (id == 0) return Unknown;
var quality = All().SingleOrDefault(q => q.Id == id); Quality quality = All.FirstOrDefault(v => v.Id == id);
if (quality == null) if (quality == null)
throw new ArgumentException("ID does not match a known quality", "id"); throw new ArgumentException("ID does not match a known quality", "id");
return quality; return quality;
} }

@ -0,0 +1,33 @@
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Qualities
{
public class QualityDefinition : ModelBase
{
public Quality Quality { get; set; }
public string Title { get; set; }
public int Weight { get; set; }
public int MinSize { get; set; }
public int MaxSize { get; set; }
public QualityDefinition()
{
}
public QualityDefinition(Quality quality)
{
Quality = quality;
Title = quality.Name;
}
public override string ToString()
{
return Quality.Name;
}
}
}

@ -0,0 +1,33 @@
using System;
using System.Linq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Qualities
{
public interface IQualityDefinitionRepository : IBasicRepository<QualityDefinition>
{
QualityDefinition GetByQualityId(int qualityId);
}
public class QualityDefinitionRepository : BasicRepository<QualityDefinition>, IQualityDefinitionRepository
{
public QualityDefinitionRepository(IDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
public QualityDefinition GetByQualityId(int qualityId)
{
try
{
return Query.Single(q => (int)q.Quality == qualityId);
}
catch (InvalidOperationException e)
{
throw new ModelNotFoundException(typeof(QualityDefinition), qualityId);
}
}
}
}

@ -0,0 +1,101 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events;
using System;
namespace NzbDrone.Core.Qualities
{
public interface IQualityDefinitionService
{
void Update(QualityDefinition qualityDefinition);
List<QualityDefinition> All();
QualityDefinition Get(Quality quality);
}
public class QualityDefinitionService : IQualityDefinitionService, IHandle<ApplicationStartedEvent>
{
private readonly IQualityDefinitionRepository _qualityDefinitionRepository;
private readonly Logger _logger;
public QualityDefinitionService(IQualityDefinitionRepository qualityDefinitionRepository, Logger logger)
{
_qualityDefinitionRepository = qualityDefinitionRepository;
_logger = logger;
}
public void Update(QualityDefinition qualityDefinition)
{
_qualityDefinitionRepository.Update(qualityDefinition);
}
public List<QualityDefinition> All()
{
return _qualityDefinitionRepository.All().ToList();
}
public QualityDefinition Get(Quality quality)
{
if (quality == Quality.Unknown)
return new QualityDefinition(Quality.Unknown);
return _qualityDefinitionRepository.GetByQualityId((int)quality);
}
public void InsertMissingDefinitions(List<QualityDefinition> allDefinitions)
{
allDefinitions.OrderBy(v => v.Weight).ToList();
var existingDefinitions = _qualityDefinitionRepository.All().OrderBy(v => v.Weight).ToList();
// Try insert each item intelligently to merge the lists preserving the Weight the user set.
for (int i = 0; i < allDefinitions.Count;i++)
{
// Skip if this definition isn't missing.
if (existingDefinitions.Any(v => v.Quality == allDefinitions[i].Quality))
continue;
int targetIndexMinimum = 0;
for (int j = 0; j < i; j++)
targetIndexMinimum = Math.Max(targetIndexMinimum, existingDefinitions.FindIndex(v => v.Quality == allDefinitions[j].Quality) + 1);
int targetIndexMaximum = existingDefinitions.Count;
for (int j = i + 1; j < allDefinitions.Count; j++)
{
var index = existingDefinitions.FindIndex(v => v.Quality == allDefinitions[j].Quality);
if (index != -1)
targetIndexMaximum = Math.Min(targetIndexMaximum, index);
}
// Rounded down average sounds reasonable.
int targetIndex = (targetIndexMinimum + targetIndexMaximum) / 2;
existingDefinitions.Insert(targetIndex, allDefinitions[i]);
}
// Update all Weights.
List<QualityDefinition> insertList = new List<QualityDefinition>();
List<QualityDefinition> updateList = new List<QualityDefinition>();
for (int i = 0; i < existingDefinitions.Count; i++)
{
if (existingDefinitions[i].Id == 0)
{
existingDefinitions[i].Weight = i + 1;
_qualityDefinitionRepository.Insert(existingDefinitions[i]);
}
else if (existingDefinitions[i].Weight != i + 1)
{
existingDefinitions[i].Weight = i + 1;
_qualityDefinitionRepository.Update(existingDefinitions[i]);
}
}
}
public void Handle(ApplicationStartedEvent message)
{
_logger.Debug("Setting up default quality config");
InsertMissingDefinitions(Quality.DefaultQualityDefinitions.ToList());
}
}
}

@ -0,0 +1,73 @@
using System;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Qualities
{
public class QualityModel : IEmbeddedDocument, IEquatable<QualityModel>
{
public Quality Quality { get; set; }
public Boolean Proper { get; set; }
public QualityModel()
: this(Quality.Unknown)
{
}
public QualityModel(Quality quality, Boolean proper = false)
{
Quality = quality;
Proper = proper;
}
public override string ToString()
{
string result = Quality.ToString();
if (Proper)
{
result += " Proper";
}
return result;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + Proper.GetHashCode();
hash = hash * 23 + Quality.GetHashCode();
return hash;
}
}
public bool Equals(QualityModel other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Quality.Equals(Quality) && other.Proper.Equals(Proper);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj as QualityModel);
}
public static bool operator ==(QualityModel left, QualityModel right)
{
return Equals(left, right);
}
public static bool operator !=(QualityModel left, QualityModel right)
{
return !Equals(left, right);
}
}
}

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using NzbDrone.Common.EnsureThat;
namespace NzbDrone.Core.Qualities
{
public class QualityModelComparer : IComparer<Quality>, IComparer<QualityModel>
{
private readonly QualityProfile _qualityProfile;
public QualityModelComparer(QualityProfile qualityProfile)
{
Ensure.That(qualityProfile, () => qualityProfile).IsNotNull();
Ensure.That(qualityProfile.Items, () => qualityProfile.Items).HasItems();
_qualityProfile = qualityProfile;
}
public int Compare(Quality left, Quality right)
{
int leftIndex = _qualityProfile.Items.FindIndex(v => v.Quality == left);
int rightIndex = _qualityProfile.Items.FindIndex(v => v.Quality == right);
return leftIndex.CompareTo(rightIndex);
}
public int Compare(QualityModel left, QualityModel right)
{
int result = Compare(left.Quality, right.Quality);
if (result == 0)
result = left.Proper.CompareTo(right.Proper);
return result;
}
}
}

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Qualities
public class QualityProfile : ModelBase public class QualityProfile : ModelBase
{ {
public string Name { get; set; } public string Name { get; set; }
public List<Quality> Allowed { get; set; }
public Quality Cutoff { get; set; } public Quality Cutoff { get; set; }
public List<QualityProfileItem> Items { get; set; }
} }
} }

@ -0,0 +1,11 @@
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Qualities
{
public class QualityProfileItem : IEmbeddedDocument
{
public Quality Quality { get; set; }
public bool Allowed { get; set; }
}
}

@ -60,68 +60,46 @@ namespace NzbDrone.Core.Qualities
return _qualityProfileRepository.Get(id); return _qualityProfileRepository.Get(id);
} }
private QualityProfile AddDefaultQualityProfile(string name, Quality cutoff, params Quality[] allowed)
{
var items = Quality.DefaultQualityDefinitions
.OrderBy(v => v.Weight)
.Select(v => new QualityProfileItem { Quality = v.Quality, Allowed = allowed.Contains(v.Quality) })
.ToList();
var qualityProfile = new QualityProfile { Name = name, Cutoff = cutoff, Items = items };
return Add(qualityProfile);
}
public void Handle(ApplicationStartedEvent message) public void Handle(ApplicationStartedEvent message)
{ {
if (All().Any()) return; if (All().Any()) return;
_logger.Info("Setting up default quality profiles"); _logger.Info("Setting up default quality profiles");
var sd = new QualityProfile AddDefaultQualityProfile("SD", Quality.SDTV,
{ Quality.SDTV,
Name = "SD", Quality.WEBDL480p,
Allowed = new List<Quality> Quality.DVD);
{
Quality.SDTV, AddDefaultQualityProfile("HD-720p", Quality.HDTV720p,
Quality.WEBDL480p, Quality.HDTV720p,
Quality.DVD Quality.WEBDL720p,
}, Quality.Bluray720p);
Cutoff = Quality.SDTV
}; AddDefaultQualityProfile("HD-1080p", Quality.HDTV1080p,
Quality.HDTV1080p,
var hd720p = new QualityProfile Quality.WEBDL1080p,
{ Quality.Bluray1080p);
Name = "HD 720p",
Allowed = new List<Quality> AddDefaultQualityProfile("HD - All", Quality.HDTV720p,
{ Quality.HDTV720p,
Quality.HDTV720p, Quality.HDTV1080p,
Quality.WEBDL720p, Quality.WEBDL720p,
Quality.Bluray720p Quality.WEBDL1080p,
}, Quality.Bluray720p,
Cutoff = Quality.HDTV720p Quality.Bluray1080p);
};
var hd1080p = new QualityProfile
{
Name = "HD 1080p",
Allowed = new List<Quality>
{
Quality.HDTV1080p,
Quality.WEBDL1080p,
Quality.Bluray1080p
},
Cutoff = Quality.HDTV1080p
};
var hdAll = new QualityProfile
{
Name = "HD - All",
Allowed = new List<Quality>
{
Quality.HDTV720p,
Quality.WEBDL720p,
Quality.Bluray720p,
Quality.HDTV1080p,
Quality.WEBDL1080p,
Quality.Bluray1080p
},
Cutoff = Quality.HDTV720p
};
Add(sd);
Add(hd720p);
Add(hd1080p);
Add(hdAll);
} }
} }
} }

@ -1,18 +0,0 @@
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Qualities
{
public class QualitySize : ModelBase
{
public int QualityId { get; set; }
public string Name { get; set; }
public int MinSize { get; set; }
public int MaxSize { get; set; }
public override string ToString()
{
return Name;
}
}
}

@ -1,33 +0,0 @@
using System;
using System.Linq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Qualities
{
public interface IQualitySizeRepository : IBasicRepository<QualitySize>
{
QualitySize GetByQualityId(int qualityId);
}
public class QualitySizeRepository : BasicRepository<QualitySize>, IQualitySizeRepository
{
public QualitySizeRepository(IDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
public QualitySize GetByQualityId(int qualityId)
{
try
{
return Query.Single(q => q.QualityId == qualityId);
}
catch (InvalidOperationException e)
{
throw new ModelNotFoundException(typeof(QualitySize), qualityId);
}
}
}
}

@ -1,64 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Qualities
{
public interface IQualitySizeService
{
void Update(QualitySize qualitySize);
List<QualitySize> All();
QualitySize Get(int qualityId);
}
public class QualitySizeService : IQualitySizeService, IHandle<ApplicationStartedEvent>
{
private readonly IQualitySizeRepository _qualitySizeRepository;
private readonly Logger _logger;
public QualitySizeService(IQualitySizeRepository qualitySizeRepository, Logger logger)
{
_qualitySizeRepository = qualitySizeRepository;
_logger = logger;
}
public virtual void Update(QualitySize qualitySize)
{
_qualitySizeRepository.Update(qualitySize);
}
public virtual List<QualitySize> All()
{
return _qualitySizeRepository.All().ToList();
}
public virtual QualitySize Get(int qualityId)
{
return _qualitySizeRepository.GetByQualityId(qualityId);
}
public void Handle(ApplicationStartedEvent message)
{
var existing = All();
_logger.Debug("Setting up default quality sizes");
foreach (var quality in Quality.All())
{
if (!existing.Any(s => s.QualityId == quality.Id))
{
_qualitySizeRepository.Insert(new QualitySize
{
QualityId = quality.Id,
Name = quality.Name,
MinSize = 0,
MaxSize = 100
});
}
}
}
}
}

@ -1,5 +1,6 @@
using System; using System;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Queue namespace NzbDrone.Core.Queue

@ -1,120 +0,0 @@
using System;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Tv
{
public class QualityModel : IComparable<QualityModel>, IEmbeddedDocument
{
public Quality Quality { get; set; }
public Boolean Proper { get; set; }
public QualityModel()
: this(Quality.Unknown)
{
}
public QualityModel(Quality quality, Boolean proper = false)
{
Quality = quality;
Proper = proper;
}
public int CompareTo(QualityModel other)
{
if (other.Quality > Quality)
return -1;
if (other.Quality < Quality)
return 1;
if (other.Quality == Quality && other.Proper == Proper)
return 0;
if (Proper && !other.Proper)
return 1;
if (!Proper && other.Proper)
return -1;
return 0;
}
public static bool operator !=(QualityModel x, QualityModel y)
{
return !(x == y);
}
public static bool operator ==(QualityModel x, QualityModel y)
{
var xObj = (Object)x;
var yObj = (object)y;
if (xObj == null || yObj == null)
{
return xObj == yObj;
}
return x.CompareTo(y) == 0;
}
public static bool operator >(QualityModel x, QualityModel y)
{
return x.CompareTo(y) > 0;
}
public static bool operator <(QualityModel x, QualityModel y)
{
return x.CompareTo(y) < 0;
}
public static bool operator <=(QualityModel x, QualityModel y)
{
return x.CompareTo(y) <= 0;
}
public static bool operator >=(QualityModel x, QualityModel y)
{
return x.CompareTo(y) >= 0;
}
public override string ToString()
{
string result = Quality.ToString();
if (Proper)
{
result += " Proper";
}
return result;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + Proper.GetHashCode();
hash = hash * 23 + Quality.GetHashCode();
return hash;
}
}
public bool Equals(QualityModel other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Quality, Quality) && other.Proper.Equals(Proper);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(QualityModel)) return false;
return Equals((QualityModel)obj);
}
}
}

@ -9,8 +9,9 @@ define(
}, },
Keys : { Keys : {
DefaultQualityProfileId: 'DefaultQualityProfileId', DefaultQualityProfileId: 'DefaultQualityProfileId',
DefaultRootFolderId: 'DefaultRootFolderId', DefaultRootFolderId : 'DefaultRootFolderId',
UseSeasonFolder: 'UseSeasonFolder' UseSeasonFolder : 'UseSeasonFolder',
AdvancedSettings : 'advancedSettings'
}, },
getValueBoolean: function (key, defaultValue) { getValueBoolean: function (key, defaultValue) {

@ -47,3 +47,12 @@ textarea.release-restrictions {
.clickable; .clickable;
} }
} }
h3 {
.help-inline {
font-size: 16px;
padding-left: 0px;
margin-top: -5px;
text-transform: none;
}
}

@ -0,0 +1,20 @@
'use strict';
define(
[
'handlebars'
], function (Handlebars) {
Handlebars.registerHelper('eachReverse', function (context) {
var options = arguments[arguments.length - 1];
var ret = '';
if (context && context.length > 0) {
for (var i = context.length - 1; i >= 0; i--) {
ret += options.fn(context[i]);
}
} else {
ret = options.inverse(this);
}
return ret;
});
});

@ -10,6 +10,7 @@ define(
'Handlebars/Helpers/Series', 'Handlebars/Helpers/Series',
'Handlebars/Helpers/Quality', 'Handlebars/Helpers/Quality',
'Handlebars/Helpers/System', 'Handlebars/Helpers/System',
'Handlebars/Helpers/EachReverse',
'Handlebars/Handlebars.Debug' 'Handlebars/Handlebars.Debug'
], function (Templates) { ], function (Templates) {
return function () { return function () {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,11 @@
'use strict';
define(
[
'backbone',
'Quality/QualityDefinitionModel'
], function (Backbone, QualityDefinitionModel) {
return Backbone.Collection.extend({
model: QualityDefinitionModel,
url : window.NzbDrone.ApiRoot + '/qualitydefinition'
});
});

@ -9,10 +9,10 @@ define(
baseInitialize: ModelBase.prototype.initialize, baseInitialize: ModelBase.prototype.initialize,
initialize: function () { initialize: function () {
var name = this.get('name'); var name = this.get('quality').name;
this.successMessage = 'Saved ' + name + ' size settings'; this.successMessage = 'Saved ' + name + ' quality settings';
this.errorMessage = 'Couldn\'t save ' + name + ' size settings'; this.errorMessage = 'Couldn\'t save ' + name + ' quality settings';
this.baseInitialize.call(this); this.baseInitialize.call(this);
} }

@ -1,11 +0,0 @@
'use strict';
define(
[
'backbone',
'Quality/QualitySizeModel'
], function (Backbone, QualitySizeModel) {
return Backbone.Collection.extend({
model: QualitySizeModel,
url : window.NzbDrone.ApiRoot + '/qualitysize'
});
});

@ -0,0 +1,16 @@
<fieldset>
<legend>Quality Definitions</legend>
<div class="span11">
<div id="quality-definition-list">
<div class="quality-header x-header">
<div class="row">
<span class="span2">Quality</span>
<span class="span2">Title</span>
<span class="offset1 span4">Size Limit</span>
</div>
</div>
<div class="rows x-rows">
</div>
</div>
</div>
</fieldset>

@ -0,0 +1,17 @@
'use strict';
define(
[
'marionette',
'backgrid',
'Settings/Quality/Definition/QualityDefinitionView'
], function (Marionette, Backgrid, QualityDefinitionView) {
return Marionette.CompositeView.extend({
template: 'Settings/Quality/Definition/QualityDefinitionCollectionTemplate',
itemViewContainer: ".x-rows",
itemView: QualityDefinitionView
});
});

@ -0,0 +1,31 @@
 <span class="span2">
{{quality.name}}
</span>
<span class="span2">
<input type="text" class="x-title input-block-level" value="{{title}}">
</span>
<span class="offset1 span4">
<div class="x-slider"></div>
<div class="size-label-wrapper">
<div class="pull-left">
<span class="label label-warning x-min-thirty"
name="thirtyMinuteMinSize"
title="Minimum size for a 30 minute episode">
</span>
<span class="label label-info x-min-sixty"
name="sixtyMinuteMinSize"
title="Minimum size for a 60 minute episode">
</span>
</div>
<div class="pull-right">
<span class="label label-warning x-max-thirty"
name="thirtyMinuteMaxSize"
title="Maximum size for a 30 minute episode">
</span>
<span class="label label-info x-max-sixty"
name="sixtyMinuteMaxSize"
title="Maximum size for a 60 minute episode">
</span>
</div>
</div>
</span>

@ -0,0 +1,86 @@
'use strict';
define(
[
'marionette',
'Mixins/AsModelBoundView',
'filesize',
'jquery-ui'
], function (Marionette, AsModelBoundView, fileSize) {
var view = Marionette.ItemView.extend({
template: 'Settings/Quality/Definition/QualityDefinitionTemplate',
className: 'row',
ui: {
title : '.x-title',
sizeSlider : '.x-slider',
thirtyMinuteMinSize: '.x-min-thirty',
sixtyMinuteMinSize : '.x-min-sixty',
thirtyMinuteMaxSize: '.x-max-thirty',
sixtyMinuteMaxSize : '.x-max-sixty'
},
events: {
'change .x-title': '_updateTitle',
'slide .x-slider': '_updateSize'
},
initialize: function (options) {
this.qualityProfileCollection = options.qualityProfiles;
this.filesize = fileSize;
},
onRender: function () {
this.ui.sizeSlider.slider({
range : true,
min : 0,
max : 200,
values : [ this.model.get('minSize'), this.model.get('maxSize') ],
});
this._changeSize();
},
_updateTitle: function() {
this.model.set('title', this.ui.title.val());
},
_updateSize: function (event, ui) {
this.model.set('minSize', ui.values[0]);
this.model.set('maxSize', ui.values[1]);
this._changeSize();
},
_changeSize: function () {
var minSize = this.model.get('minSize');
var maxSize = this.model.get('maxSize');
{
var minBytes = minSize * 1024 * 1024;
var minThirty = fileSize(minBytes * 30, 1, false);
var minSixty = fileSize(minBytes * 60, 1, false);
this.ui.thirtyMinuteMinSize.html(minThirty);
this.ui.sixtyMinuteMinSize.html(minSixty);
}
{
var maxBytes = maxSize * 1024 * 1024;
var maxThirty = fileSize(maxBytes * 30, 1, false);
var maxSixty = fileSize(maxBytes * 60, 1, false);
this.ui.thirtyMinuteMaxSize.html(maxThirty);
this.ui.sixtyMinuteMaxSize.html(maxSixty);
}
/*if (parseInt(maxSize, 10) === 0) {
thirty = 'No Limit';
sixty = 'No Limit';
}*/
}
});
return AsModelBoundView.call(view);
});

@ -7,13 +7,14 @@ define(
Handlebars.registerHelper('allowedLabeler', function () { Handlebars.registerHelper('allowedLabeler', function () {
var ret = ''; var ret = '';
var cutoff = this.cutoff; var cutoff = this.cutoff;
_.each(this.allowed, function (allowed) { _.each(this.items, function (item) {
if (allowed.id === cutoff.id) { if (item.allowed) {
ret += '<span class="label label-info" title="Cutoff">' + allowed.name + '</span> '; if (item.quality.id === cutoff.id) {
} ret += '<span class="label label-info" title="Cutoff">' + item.quality.name + '</span>&nbsp;';
}
else { else {
ret += '<span class="label">' + allowed.name + '</span> '; ret += '<span class="label">' + item.quality.name + '</span>&nbsp;';
}
} }
}); });

@ -0,0 +1,9 @@
'use strict';
define(
[
'marionette'
], function (Marionette) {
return Marionette.ItemView.extend({
template : 'Settings/Quality/Profile/Edit/EditQualityProfileItemViewTemplate'
});
});

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

Loading…
Cancel
Save