New: Add Unknown Quality to profile (advanced)

pull/4/head
Mark McDowall 10 years ago
parent 641fe6476c
commit 7ac6fc98a9

@ -22,8 +22,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{
GrabDelay = 1,
Name = "OneHour",
Cutoff = "{}",
Items = "{}"
Cutoff = 0,
Items = "[]"
});
c.Insert.IntoTable("Profiles").Row(new
@ -52,8 +52,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{
GrabDelay = 1,
Name = "OneHour",
Cutoff = "{}",
Items = "{}"
Cutoff = 0,
Items = "[]"
})
);
@ -72,8 +72,8 @@ namespace NzbDrone.Core.Test.Datastore.Migration
{
GrabDelay = 1,
Name = "OneHour",
Cutoff = "{}",
Items = "{}"
Cutoff = 0,
Items = "[]"
});
c.Insert.IntoTable("Series").Row(new

@ -0,0 +1,38 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Tags;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.Migration
{
[TestFixture]
public class unknown_quality_in_profileFixture : MigrationTest<unknown_quality_in_profile>
{
[Test]
public void should_add_unknown_to_old_profile()
{
WithTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Name = "SDTV",
Cutoff = 1,
Items = "[ { \"quality\": 1, \"allowed\": true } ]",
Language = 1
});
});
var allProfiles = Mocker.Resolve<ProfileRepository>().All().ToList();
allProfiles.Should().HaveCount(1);
allProfiles.First().Items.Should().HaveCount(2);
allProfiles.First().Items.Should().Contain(i => i.Quality.Id == 0 && i.Allowed == false);
}
}
}

@ -187,18 +187,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.IsSatisfiedBy(parseResult, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_unknown()
{
var parseResult = new RemoteEpisode
{
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.Unknown) },
};
Subject.IsSatisfiedBy(parseResult, null).Accepted.Should().BeTrue();
Mocker.GetMock<IQualityDefinitionService>().Verify(c => c.Get(It.IsAny<Quality>()), Times.Never());
}
}
}

@ -116,6 +116,7 @@
<Compile Include="Datastore\DatabaseRelationshipFixture.cs" />
<Compile Include="Datastore\MappingExtentionFixture.cs" />
<Compile Include="Datastore\MarrDataLazyLoadingFixture.cs" />
<Compile Include="Datastore\Migration\071_unknown_quality_in_profileFixture.cs" />
<Compile Include="Datastore\Migration\070_delay_profileFixture.cs" />
<Compile Include="Datastore\ObjectDatabaseFixture.cs" />
<Compile Include="Datastore\PagingSpecExtensionsTests\PagingOffsetFixture.cs" />

@ -16,7 +16,7 @@ namespace NzbDrone.Core.Test.Qualities
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.IsAny<QualityDefinition>()), Times.Exactly(Quality.All.Count));
.Verify(v => v.InsertMany(It.Is<List<QualityDefinition>>(d => d.Count == Quality.All.Count)), Times.Once());
}
[Test]
@ -32,48 +32,39 @@ namespace NzbDrone.Core.Test.Qualities
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.IsAny<QualityDefinition>()), Times.Exactly(Quality.All.Count - 1));
.Verify(v => v.InsertMany(It.Is<List<QualityDefinition>>(d => d.Count == Quality.All.Count -1 )), Times.Once());
}
[Test]
public void init_should_insert_missing_definitions_preserving_weight()
public void init_should_update_existing_definitions()
{
// User moved HDTV1080p to a higher weight.
var currentQualities = new List<QualityDefinition>
Mocker.GetMock<IQualityDefinitionRepository>()
.Setup(s => s.All())
.Returns(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 },
};
new QualityDefinition(Quality.SDTV) { Weight = 1, MinSize = 0, MaxSize = 100, Id = 20 }
});
// 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 }
};
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualityDefinitionRepository>()
.Setup(v => v.All())
.Returns(currentQualities);
Subject.InsertMissingDefinitions(addBluray1080p);
.Verify(v => v.UpdateMany(It.Is<List<QualityDefinition>>(d => d.Count == 1)), Times.Once());
}
[Test]
public void init_should_remove_old_definitions()
{
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.Is<QualityDefinition>(p => p.Quality == Quality.Bluray720p && p.Weight == 4)), Times.Once());
.Setup(s => s.All())
.Returns(new List<QualityDefinition>
{
new QualityDefinition(new Quality{ Id = 100, Name = "Test" }) { Weight = 1, MinSize = 0, MaxSize = 100, Id = 20 }
});
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Update(It.Is<QualityDefinition>(p => p.Quality == Quality.WEBDL1080p && p.Weight == 5)), Times.Once());
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IQualityDefinitionRepository>()
.Verify(v => v.Insert(It.Is<QualityDefinition>(p => p.Quality == Quality.Bluray1080p && p.Weight == 6)), Times.Once());
.Verify(v => v.DeleteMany(It.Is<List<QualityDefinition>>(d => d.Count == 1)), Times.Once());
}
}
}

@ -10,7 +10,6 @@ using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Datastore
{
public interface IBasicRepository<TModel> where TModel : ModelBase, new()
@ -36,7 +35,6 @@ namespace NzbDrone.Core.Datastore
PagingSpec<TModel> GetPaged(PagingSpec<TModel> pagingSpec);
}
public class BasicRepository<TModel> : IBasicRepository<TModel> where TModel : ModelBase, new()
{
private readonly IDatabase _database;

@ -0,0 +1,90 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using FluentMigrator;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(71)]
public class unknown_quality_in_profile : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Delete.Column("Weight").FromTable("QualityDefinitions");
Execute.WithConnection(ConvertProfile);
}
private void ConvertProfile(IDbConnection conn, IDbTransaction tran)
{
var profiles = GetProfiles(conn, tran);
foreach (var profile in profiles)
{
if (profile.Items.Any(p => p.Quality == 0)) continue;
profile.Items.Insert(0, new ProfileItem71
{
Quality = 0,
Allowed = false
});
var itemsJson = profile.Items.ToJson();
using (IDbCommand updateProfileCmd = conn.CreateCommand())
{
updateProfileCmd.Transaction = tran;
updateProfileCmd.CommandText = "UPDATE Profiles SET Items = ? WHERE Id = ?";
updateProfileCmd.AddParameter(itemsJson);
updateProfileCmd.AddParameter(profile.Id);
updateProfileCmd.ExecuteNonQuery();
}
}
}
private List<Profile71> GetProfiles(IDbConnection conn, IDbTransaction tran)
{
var profiles = new List<Profile71>();
using (IDbCommand getProfilesCmd = conn.CreateCommand())
{
getProfilesCmd.Transaction = tran;
getProfilesCmd.CommandText = @"SELECT Id, Items FROM Profiles";
using (IDataReader profileReader = getProfilesCmd.ExecuteReader())
{
while (profileReader.Read())
{
var id = profileReader.GetInt32(0);
var itemsJson = profileReader.GetString(1);
var items = Json.Deserialize<List<ProfileItem71>>(itemsJson);
profiles.Add(new Profile71
{
Id = id,
Items = items
});
}
}
}
return profiles;
}
private class Profile71
{
public int Id { get; set; }
public List<ProfileItem71> Items { get; set; }
}
private class ProfileItem71
{
public int Quality { get; set; }
public bool Allowed { get; set; }
}
}
}

@ -82,8 +82,11 @@ namespace NzbDrone.Core.Datastore
.Relationship()
.HasOne(episode => episode.EpisodeFile, episode => episode.EpisodeFileId);
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions")
.Ignore(d => d.Weight);
Mapper.Entity<Profile>().RegisterModel("Profiles");
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions");
Mapper.Entity<Log>().RegisterModel("Logs");
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
Mapper.Entity<SeriesStatistics>().MapResultSet();

@ -36,12 +36,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
return Decision.Accept();
}
if (quality == Quality.Unknown)
{
_logger.Debug("Unknown quality. skipping size check.");
return Decision.Accept();
}
var qualityDefinition = _qualityDefinitionService.Get(quality);
var minSize = qualityDefinition.MinSize.Megabytes();

@ -230,6 +230,7 @@
<Compile Include="Datastore\Migration\066_add_tags.cs" />
<Compile Include="Datastore\Migration\067_add_added_to_series.cs" />
<Compile Include="Datastore\Migration\069_quality_proper.cs" />
<Compile Include="Datastore\Migration\071_unknown_quality_in_profile.cs" />
<Compile Include="Datastore\Migration\070_delay_profile.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />

@ -74,6 +74,7 @@ namespace NzbDrone.Core.Qualities
{
return new List<Quality>
{
Unknown,
SDTV,
DVD,
WEBDL1080p,
@ -94,16 +95,17 @@ namespace NzbDrone.Core.Qualities
{
return new HashSet<QualityDefinition>
{
new QualityDefinition(Quality.SDTV) { /*Title = "SDTV", */ Weight = 1, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.WEBDL480p) { /*Title = "WEB-DL", */ Weight = 2, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.DVD) { /*Title = "DVD", */ Weight = 3, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.HDTV720p) { /*Title = "720p HDTV", */ Weight = 4, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.HDTV1080p) { /*Title = "1080p HDTV", */ Weight = 5, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.RAWHD) { /*Title = "RawHD", */ Weight = 6, MinSize=0, MaxSize=100 },
new QualityDefinition(Quality.WEBDL720p) { /*Title = "720p WEB-DL", */ Weight = 7, MinSize=0, MaxSize=100 },
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 }
new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.SDTV) { Weight = 2, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.WEBDL480p) { Weight = 3, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.DVD) { Weight = 4, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.HDTV720p) { Weight = 5, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.HDTV1080p) { Weight = 6, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.RAWHD) { Weight = 7, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.WEBDL720p) { Weight = 8, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.Bluray720p) { Weight = 9, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.WEBDL1080p) { Weight = 10, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.Bluray1080p) { Weight = 11, MinSize = 0, MaxSize = 100 }
};
}
}

@ -18,32 +18,32 @@ namespace NzbDrone.Core.Qualities
public class QualityDefinitionService : IQualityDefinitionService, IHandle<ApplicationStartedEvent>
{
private readonly IQualityDefinitionRepository _qualityDefinitionRepository;
private readonly IQualityDefinitionRepository _repo;
private readonly ICached<Dictionary<Quality, QualityDefinition>> _cache;
private readonly Logger _logger;
public QualityDefinitionService(IQualityDefinitionRepository qualityDefinitionRepository, ICacheManager cacheManager, Logger logger)
public QualityDefinitionService(IQualityDefinitionRepository repo, ICacheManager cacheManager, Logger logger)
{
_qualityDefinitionRepository = qualityDefinitionRepository;
_repo = repo;
_cache = cacheManager.GetCache<Dictionary<Quality, QualityDefinition>>(this.GetType());
_logger = logger;
}
private Dictionary<Quality, QualityDefinition> GetAll()
{
return _cache.Get("all", () => _qualityDefinitionRepository.All().ToDictionary(v => v.Quality), TimeSpan.FromSeconds(5.0));
return _cache.Get("all", () => _repo.All().Select(WithWeight).ToDictionary(v => v.Quality), TimeSpan.FromSeconds(5.0));
}
public void Update(QualityDefinition qualityDefinition)
{
_qualityDefinitionRepository.Update(qualityDefinition);
_repo.Update(qualityDefinition);
_cache.Clear();
}
public List<QualityDefinition> All()
{
return GetAll().Values.ToList();
return GetAll().Values.OrderBy(d => d.Weight).ToList();
}
public QualityDefinition GetById(Int32 id)
@ -53,67 +53,52 @@ namespace NzbDrone.Core.Qualities
public QualityDefinition Get(Quality quality)
{
if (quality == Quality.Unknown)
return new QualityDefinition(Quality.Unknown);
return GetAll()[quality];
}
public void InsertMissingDefinitions(List<QualityDefinition> allDefinitions)
private void InsertMissingDefinitions()
{
allDefinitions.OrderBy(v => v.Weight).ToList();
var existingDefinitions = _qualityDefinitionRepository.All().OrderBy(v => v.Weight).ToList();
List<QualityDefinition> insertList = new List<QualityDefinition>();
List<QualityDefinition> updateList = new List<QualityDefinition>();
var allDefinitions = Quality.DefaultQualityDefinitions.OrderBy(d => d.Weight).ToList();
var existingDefinitions = _repo.All().ToList();
// Try insert each item intelligently to merge the lists preserving the Weight the user set.
for (int i = 0; i < allDefinitions.Count;i++)
foreach (var definition in allDefinitions)
{
// Skip if this definition isn't missing.
if (existingDefinitions.Any(v => v.Quality == allDefinitions[i].Quality))
continue;
var existing = existingDefinitions.SingleOrDefault(d => d.Quality == definition.Quality);
int targetIndexMinimum = 0;
for (int j = 0; j < i; j++)
targetIndexMinimum = Math.Max(targetIndexMinimum, existingDefinitions.FindIndex(v => v.Quality == allDefinitions[j].Quality) + 1);
if (existing == null)
{
insertList.Add(definition);
}
int targetIndexMaximum = existingDefinitions.Count;
for (int j = i + 1; j < allDefinitions.Count; j++)
else
{
var index = existingDefinitions.FindIndex(v => v.Quality == allDefinitions[j].Quality);
if (index != -1)
targetIndexMaximum = Math.Min(targetIndexMaximum, index);
updateList.Add(existing);
existingDefinitions.Remove(existing);
}
}
// Rounded down average sounds reasonable.
int targetIndex = (targetIndexMinimum + targetIndexMaximum) / 2;
_repo.InsertMany(insertList);
_repo.UpdateMany(updateList);
_repo.DeleteMany(existingDefinitions);
existingDefinitions.Insert(targetIndex, allDefinitions[i]);
_cache.Clear();
}
// 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)
private static QualityDefinition WithWeight(QualityDefinition definition)
{
existingDefinitions[i].Weight = i + 1;
_qualityDefinitionRepository.Update(existingDefinitions[i]);
}
}
definition.Weight = Quality.DefaultQualityDefinitions.Single(d => d.Quality == definition.Quality).Weight;
_cache.Clear();
return definition;
}
public void Handle(ApplicationStartedEvent message)
{
_logger.Debug("Setting up default quality config");
InsertMissingDefinitions(Quality.DefaultQualityDefinitions.ToList());
InsertMissingDefinitions();
}
}
}

@ -53,18 +53,23 @@ define(
onShow: function () {
this.fieldsView = new EditProfileView({ model: this.model });
this._showFieldsView();
var advancedShown = Config.getValueBoolean(Config.Keys.AdvancedSettings, false);
this.sortableListView = new QualitySortableCollectionView({
selectable : true,
selectMultiple : true,
clickToSelect : true,
clickToToggle : true,
sortable : Config.getValueBoolean(Config.Keys.AdvancedSettings, false),
sortable : advancedShown,
sortableOptions : {
handle: '.x-drag-handle'
},
visibleModelsFilter : function (model) {
return model.get('quality').id !== 0 || advancedShown;
},
collection: this.itemsCollection,
model : this.model
});

@ -3,15 +3,14 @@
define(
[
'marionette',
'backgrid',
'Settings/Quality/Definition/QualityDefinitionView'
], function (Marionette, Backgrid, QualityDefinitionView) {
'Settings/Quality/Definition/QualityDefinitionItemView'
], function (Marionette, QualityDefinitionItemView) {
return Marionette.CompositeView.extend({
template: 'Settings/Quality/Definition/QualityDefinitionCollectionTemplate',
itemViewContainer: '.x-rows',
itemView: QualityDefinitionView
itemView: QualityDefinitionItemView
});
});

@ -9,7 +9,7 @@ define(
], function (Marionette, AsModelBoundView, fileSize) {
var view = Marionette.ItemView.extend({
template: 'Settings/Quality/Definition/QualityDefinitionViewTemplate',
template: 'Settings/Quality/Definition/QualityDefinitionItemViewTemplate',
className: 'row',
ui: {
@ -24,12 +24,15 @@ define(
'slide .x-slider': '_updateSize'
},
initialize: function (options) {
this.profileCollection = options.profiles;
initialize: function () {
this.filesize = fileSize;
},
onRender: function () {
if (this.model.get('quality').id === 0) {
this.$el.addClass('row advanced-setting');
}
this.ui.sizeSlider.slider({
range : true,
min : 0,
Loading…
Cancel
Save