Indexes are created with the same uniqueness when copying a table

New: Non-English episode support
New: Renamed Quality Profiles to Profiles and made them more powerful
New: Configurable wait time before grabbing a release to wait for a better quality
This commit is contained in:
Mark McDowall
2014-06-08 01:22:55 -07:00
parent b72678a9ad
commit 74a38415cf
182 changed files with 2493 additions and 2433 deletions
@@ -7,6 +7,7 @@ define(
Handlebars.registerHelper('allowedLabeler', function () {
var ret = '';
var cutoff = this.cutoff;
_.each(this.items, function (item) {
if (item.allowed) {
if (item.quality.id === cutoff.id) {
@@ -6,7 +6,7 @@ define(
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Settings/Quality/Profile/DeleteQualityProfileViewTemplate',
template: 'Settings/Profile/DeleteProfileViewTemplate',
events: {
'click .x-confirm-delete': '_removeProfile'
@@ -4,6 +4,6 @@ define(
'marionette'
], function (Marionette) {
return Marionette.ItemView.extend({
template : 'Settings/Quality/Profile/Edit/EditQualityProfileItemViewTemplate'
template : 'Settings/Profile/Edit/EditProfileItemViewTemplate'
});
});
@@ -6,10 +6,10 @@ define(
'AppLayout',
'marionette',
'backbone',
'Settings/Quality/Profile/Edit/EditQualityProfileItemView',
'Settings/Quality/Profile/Edit/QualitySortableCollectionView',
'Settings/Quality/Profile/Edit/EditQualityProfileView',
'Settings/Quality/Profile/DeleteQualityProfileView',
'Settings/Profile/Edit/EditProfileItemView',
'Settings/Profile/Edit/QualitySortableCollectionView',
'Settings/Profile/Edit/EditProfileView',
'Settings/Profile/DeleteProfileView',
'Series/SeriesCollection',
'Config'
], function (_,
@@ -17,15 +17,15 @@ define(
AppLayout,
Marionette,
Backbone,
EditQualityProfileItemView,
EditProfileItemView,
QualitySortableCollectionView,
EditQualityProfileView,
EditProfileView,
DeleteView,
SeriesCollection,
Config) {
return Marionette.Layout.extend({
template: 'Settings/Quality/Profile/Edit/EditQualityProfileLayoutTemplate',
template: 'Settings/Profile/Edit/EditProfileLayoutTemplate',
regions: {
fields : '#x-fields',
@@ -37,8 +37,8 @@ define(
},
events: {
'click .x-save' : '_saveQualityProfile',
'click .x-cancel' : '_cancelQualityProfile',
'click .x-save' : '_saveProfile',
'click .x-cancel' : '_cancelProfile',
'click .x-delete' : '_delete'
},
@@ -53,7 +53,7 @@ define(
},
onShow: function () {
this.fieldsView = new EditQualityProfileView({ model: this.model });
this.fieldsView = new EditProfileView({ model: this.model });
this._showFieldsView();
this.sortableListView = new QualitySortableCollectionView({
@@ -94,7 +94,7 @@ define(
this._showFieldsView();
},
_saveQualityProfile: function () {
_saveProfile: function () {
var self = this;
var cutoff = this.fieldsView.getCutoff();
this.model.set('cutoff', cutoff);
@@ -109,7 +109,7 @@ define(
}
},
_cancelQualityProfile: function () {
_cancelProfile: function () {
if (!this.model.has('id')) {
vent.trigger(vent.Commands.CloseModalCommand);
return;
@@ -136,7 +136,7 @@ define(
_updateDisableStatus: function () {
if (this._isQualityInUse()) {
this.ui.deleteButton.addClass('disabled');
this.ui.deleteButton.attr('title', 'Can\'t delete quality profiles attached to a series.');
this.ui.deleteButton.attr('title', 'Can\'t delete a profile that is attached to a series.');
}
else {
this.ui.deleteButton.removeClass('disabled');
@@ -144,7 +144,7 @@ define(
},
_isQualityInUse: function () {
return SeriesCollection.where({'qualityProfileId': this.model.id}).length !== 0;
return SeriesCollection.where({'profileId': this.model.id}).length !== 0;
}
});
});
@@ -0,0 +1,61 @@
'use strict';
define(
[
'underscore',
'marionette',
'Settings/Profile/Language/LanguageCollection',
'Config',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView'
], function (_, Marionette, LanguageCollection, Config, AsModelBoundView, AsValidatedView) {
var view = Marionette.ItemView.extend({
template: 'Settings/Profile/Edit/EditProfileViewTemplate',
ui: {
cutoff : '.x-cutoff',
delay : '.x-delay',
delayMode : '.x-delay-mode'
},
events: {
'change .x-delay': 'toggleDelayMode',
'keyup .x-delay': 'toggleDelayMode'
},
templateHelpers: function () {
return {
languages : LanguageCollection.toJSON()
};
},
onShow: function () {
this.toggleDelayMode();
},
getCutoff: function () {
var self = this;
return _.findWhere(_.pluck(this.model.get('items'), 'quality'), { id: parseInt(self.ui.cutoff.val(), 10)});
},
toggleDelayMode: function () {
var delay = parseInt(this.ui.delay.val(), 10);
if (isNaN(delay)) {
return;
}
if (delay > 0 && Config.getValueBoolean(Config.Keys.AdvancedSettings)) {
this.ui.delayMode.show();
}
else {
this.ui.delayMode.hide();
}
}
});
AsValidatedView.call(view);
return AsModelBoundView.call(view);
});
@@ -0,0 +1,71 @@
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
<div class="col-sm-5">
<input type="text" name="name" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Language</label>
<div class="col-sm-5">
<select class="form-control" name="language">
{{#each languages}}
{{#unless_eq nameLower compare="unknown"}}
<option value="{{nameLower}}">{{name}}</option>
{{/unless_eq}}
{{/each}}
</select>
</div>
<div class="col-sm-1 help-inline">
<i class="icon-nd-form-info" title="Series assigned this profile will be look for episodes with the selected language"/>
</div>
</div>
<div class="form-group advanced-setting">
<label class="col-sm-3 control-label">Delay</label>
<div class="col-sm-5">
<input type="number" min="0" max="72" name="grabDelay" class="form-control x-delay">
</div>
<div class="col-sm-1 help-inline">
<i class="icon-nd-form-info" title="Wait time in hours before grabbing a release automatically, set to 0 to disable. The highest allowed quality in the profile will be grabbed immediately when available."/>
</div>
</div>
<div class="form-group advanced-setting x-delay-mode">
<label class="col-sm-3 control-label">Delay Mode</label>
<div class="col-sm-5">
<select class="form-control" name="grabDelayMode">
<option value="first">First</option>
<option value="cutoff">Cutoff</option>
<option value="always">Always</option>
</select>
</div>
<div class="col-sm-1 help-inline">
<i class="icon-nd-form-info" data-html="true" title="First: Delay until first wanted release passes delay, grabbing best quality release at that time. Cutoff: Delay for all qualities below the cutoff. Always: Delay before grabbing all qualities"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Cutoff</label>
<div class="col-sm-5">
<select class="form-control x-cutoff" name="cutoff.id" validation-name="cutoff">
{{#eachReverse items}}
{{#if allowed}}
<option value="{{quality.id}}">{{quality.name}}</option>
{{/if}}
{{/eachReverse}}
</select>
</div>
<div class="col-sm-1 help-inline">
<i class="icon-nd-form-info" title="Once this quality is reached NzbDrone will no longer download episodes"/>
</div>
</div>
@@ -0,0 +1,22 @@
'use strict';
define(
[
'backbone.collectionview',
'Settings/Profile/Edit/EditProfileItemView'
], function (BackboneSortableCollectionView, EditProfileItemView) {
return BackboneSortableCollectionView.extend({
className: 'qualities',
modelView: EditProfileItemView,
attributes: {
'validation-name': 'items'
},
events: {
'click li, td' : '_listItem_onMousedown',
'dblclick li, td' : '_listItem_onDoubleClick',
'keydown' : '_onKeydown'
}
});
});
@@ -0,0 +1,18 @@
'use strict';
define(
[
'backbone',
'Settings/Profile/Language/LanguageModel'
], function (Backbone, LanguageModel) {
var LanuageCollection = Backbone.Collection.extend({
model: LanguageModel,
url : window.NzbDrone.ApiRoot + '/language'
});
var languages = new LanuageCollection();
languages.fetch();
return languages;
});
@@ -0,0 +1,10 @@
'use strict';
define(
[
'backbone'
], function (Backbone) {
return Backbone.Model.extend({
});
});
+20
View File
@@ -0,0 +1,20 @@
'use strict';
define(
[
'underscore',
'handlebars',
'Settings/Profile/Language/LanguageCollection'
], function (_, Handlebars, LanguageCollection) {
Handlebars.registerHelper('languageLabel', function () {
var wantedLanguage = this.language;
var language = LanguageCollection.find(function (lang) {
return lang.get('nameLower') === wantedLanguage;
});
var result = '<span class="label label-primary">' + language.get('name') + '</span>';
return new Handlebars.SafeString(result);
});
});
@@ -1,10 +1,10 @@
<fieldset>
<legend>Quality Profiles</legend>
<legend>Profiles</legend>
<div class="row">
<div class="col-md-12">
<ul class="quality-profiles thingies">
<ul class="profiles thingies">
<li>
<div class="quality-profile-item thingy add-card x-add-card">
<div class="profile-item thingy add-card x-add-card">
<span class="center well">
<i class="icon-plus" title="Add Profile"/>
</span>
@@ -2,16 +2,16 @@
define(['AppLayout',
'marionette',
'Settings/Quality/Profile/QualityProfileView',
'Settings/Quality/Profile/Edit/EditQualityProfileLayout',
'Settings/Quality/Profile/QualityProfileSchemaCollection',
'Settings/Profile/ProfileView',
'Settings/Profile/Edit/EditProfileLayout',
'Settings/Profile/ProfileSchemaCollection',
'underscore'
], function (AppLayout, Marionette, QualityProfileView, EditProfileView, ProfileCollection, _) {
], function (AppLayout, Marionette, ProfileView, EditProfileView, ProfileCollection, _) {
return Marionette.CompositeView.extend({
itemView : QualityProfileView,
itemViewContainer: '.quality-profiles',
template : 'Settings/Quality/Profile/QualityProfileCollectionTemplate',
itemView : ProfileView,
itemViewContainer: '.profiles',
template : 'Settings/Profile/ProfileCollectionTemplate',
ui: {
'addCard': '.x-add-card'
+27
View File
@@ -0,0 +1,27 @@
'use strict';
define(
[
'marionette',
'Profile/ProfileCollection',
'Settings/Profile/ProfileCollectionView',
'Settings/Profile/Language/LanguageCollection'
], function (Marionette, ProfileCollection, ProfileCollectionView, LanguageCollection) {
return Marionette.Layout.extend({
template: 'Settings/Profile/ProfileLayoutTemplate',
regions: {
profile : '#profile'
},
initialize: function (options) {
this.settings = options.settings;
ProfileCollection.fetch();
},
onShow: function () {
this.profile.show(new ProfileCollectionView({collection: ProfileCollection}));
}
});
});
@@ -0,0 +1,3 @@
<div class="row">
<div class="col-md-12" id="profile"/>
</div>
@@ -0,0 +1,13 @@
'use strict';
define(
[
'backbone',
'Profile/ProfileModel'
], function (Backbone, ProfileModel) {
return Backbone.Collection.extend({
model: ProfileModel,
url : window.NzbDrone.ApiRoot + '/profile/schema'
});
});
@@ -4,14 +4,15 @@ define(
[
'AppLayout',
'marionette',
'Settings/Quality/Profile/Edit/EditQualityProfileLayout',
'Settings/Profile/Edit/EditProfileLayout',
'Mixins/AsModelBoundView',
'Settings/Quality/Profile/AllowedLabeler',
'Settings/Profile/AllowedLabeler',
'Settings/Profile/LanguageLabel',
'bootstrap'
], function (AppLayout, Marionette, EditProfileView, AsModelBoundView) {
var view = Marionette.ItemView.extend({
template: 'Settings/Quality/Profile/QualityProfileViewTemplate',
template: 'Settings/Profile/ProfileViewTemplate',
tagName : 'li',
ui: {
@@ -0,0 +1,16 @@
<div class="profile-item thingy" title="Click to edit">
<div>
<h3 name="name"></h3>
</div>
<div class="language">
{{languageLabel}}
{{#if_gt grabDelay compare="0"}}
<i class="icon-time" title="{{grabDelay}} hour, Mode: {{TitleCase grabDelayMode}}"></i>
{{/if_gt}}
</div>
<ul class="allowed-qualities">
{{allowedLabeler}}
</ul>
</div>
+31
View File
@@ -0,0 +1,31 @@
@import "../../Content/Bootstrap/mixins";
@import "../../Content/FontAwesome/font-awesome";
@import "../../Shared/Styles/clickable.less";
.profile-item {
.clickable;
width: 300px;
height: 158px;
padding: 10px 15px;
&.add-card {
.center {
margin-top: 10px;
}
}
.allowed-qualities {
padding-left: 0px;
li {
list-style-type : none;
margin: 1px;
}
}
.language {
margin-bottom: 3px;
}
}
@@ -25,7 +25,7 @@ define(
},
initialize: function (options) {
this.qualityProfileCollection = options.qualityProfiles;
this.profileCollection = options.profiles;
this.filesize = fileSize;
},
@@ -1,26 +0,0 @@
'use strict';
define(
[
'underscore',
'marionette',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView'
], function (_, Marionette, AsModelBoundView, AsValidatedView) {
var view = Marionette.ItemView.extend({
template: 'Settings/Quality/Profile/Edit/EditQualityProfileViewTemplate',
ui: {
cutoff : '.x-cutoff'
},
getCutoff: function () {
var self = this;
return _.findWhere(_.pluck(this.model.get('items'), 'quality'), { id: parseInt(self.ui.cutoff.val(), 10)});
}
});
AsValidatedView.call(view);
return AsModelBoundView.call(view);
});
@@ -1,23 +0,0 @@
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
<div class="col-sm-5">
<input type="text" name="name" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Cutoff</label>
<div class="col-sm-5">
<select class="form-control x-cutoff" name="cutoff.id" validation-name="cutoff">
{{#eachReverse items}}
{{#if allowed}}
<option value="{{quality.id}}">{{quality.name}}</option>
{{/if}}
{{/eachReverse}}
</select>
</div>
<div class="col-sm-1 help-inline">
<i class="icon-nd-form-info" title="Once this quality is reached NzbDrone will no longer download episodes"/>
</div>
</div>
@@ -1,22 +0,0 @@
'use strict';
define(
[
'backbone.collectionview',
'Settings/Quality/Profile/Edit/EditQualityProfileItemView'
], function (BackboneSortableCollectionView, EditQualityProfileItemView) {
return BackboneSortableCollectionView.extend({
className: 'qualities',
modelView: EditQualityProfileItemView,
attributes: {
'validation-name': 'items'
},
events: {
'click li, td' : '_listItem_onMousedown',
'dblclick li, td' : '_listItem_onDoubleClick',
'keydown' : '_onKeydown'
}
});
});
@@ -1,13 +0,0 @@
'use strict';
define(
[
'backbone',
'Quality/QualityProfileModel'
], function (Backbone, QualityProfileModel) {
return Backbone.Collection.extend({
model: QualityProfileModel,
url : window.NzbDrone.ApiRoot + '/qualityprofile/schema'
});
});
@@ -1,9 +0,0 @@
<div class="quality-profile-item thingy" title="Click to edit">
<div>
<h3 name="name"></h3>
</div>
<ul class="allowed-qualities">
{{allowedLabeler}}
</ul>
</div>
+1 -6
View File
@@ -3,28 +3,23 @@
define(
[
'marionette',
'Quality/QualityProfileCollection',
'Settings/Quality/Profile/QualityProfileCollectionView',
'Quality/QualityDefinitionCollection',
'Settings/Quality/Definition/QualityDefinitionCollectionView'
], function (Marionette, QualityProfileCollection, QualityProfileCollectionView, QualityDefinitionCollection, QualityDefinitionCollectionView) {
], function (Marionette, QualityDefinitionCollection, QualityDefinitionCollectionView) {
return Marionette.Layout.extend({
template: 'Settings/Quality/QualityLayoutTemplate',
regions: {
qualityProfile : '#quality-profile',
qualityDefinition : '#quality-definition'
},
initialize: function (options) {
this.settings = options.settings;
QualityProfileCollection.fetch();
this.qualityDefinitionCollection = new QualityDefinitionCollection();
this.qualityDefinitionCollection.fetch();
},
onShow: function () {
this.qualityProfile.show(new QualityProfileCollectionView({collection: QualityProfileCollection}));
this.qualityDefinition.show(new QualityDefinitionCollectionView({collection: this.qualityDefinitionCollection}));
}
});
@@ -1,9 +1,3 @@
<div class="row">
<div class="col-md-12" id="quality-profile"/>
</div>
<br/>
<div class="row advanced-setting">
<div class="col-md-12" id="quality-definition"/>
</div>
-24
View File
@@ -2,30 +2,6 @@
@import "../../Content/FontAwesome/font-awesome";
@import "../../Shared/Styles/clickable.less";
.quality-profile-item {
.clickable;
width: 300px;
height: 120px;
padding: 10px 15px;
&.add-card {
.center {
margin-top: 10px;
}
}
.allowed-qualities {
padding-left: 0px;
li {
list-style-type : none;
margin: 1px;
}
}
}
ul.qualities {
.user-select(none);
+18
View File
@@ -10,6 +10,7 @@ define(
'Settings/MediaManagement/Naming/NamingModel',
'Settings/MediaManagement/MediaManagementLayout',
'Settings/MediaManagement/MediaManagementSettingsModel',
'Settings/Profile/ProfileLayout',
'Settings/Quality/QualityLayout',
'Settings/Indexers/IndexerLayout',
'Settings/Indexers/IndexerCollection',
@@ -31,6 +32,7 @@ define(
NamingModel,
MediaManagementLayout,
MediaManagementSettingsModel,
ProfileLayout,
QualityLayout,
IndexerLayout,
IndexerCollection,
@@ -48,6 +50,7 @@ define(
regions: {
mediaManagement : '#media-management',
profiles : '#profiles',
quality : '#quality',
indexers : '#indexers',
downloadClient : '#download-client',
@@ -59,6 +62,7 @@ define(
ui: {
mediaManagementTab : '.x-media-management-tab',
profilesTab : '.x-profiles-tab',
qualityTab : '.x-quality-tab',
indexersTab : '.x-indexers-tab',
downloadClientTab : '.x-download-client-tab',
@@ -70,6 +74,7 @@ define(
events: {
'click .x-media-management-tab' : '_showMediaManagement',
'click .x-profiles-tab' : '_showProfiles',
'click .x-quality-tab' : '_showQuality',
'click .x-indexers-tab' : '_showIndexers',
'click .x-download-client-tab' : '_showDownloadClient',
@@ -109,6 +114,7 @@ define(
{
self.loading.$el.hide();
self.mediaManagement.show(new MediaManagementLayout({ settings: self.mediaManagementSettings, namingSettings: self.namingSettings }));
self.profiles.show(new ProfileLayout());
self.quality.show(new QualityLayout());
self.indexers.show(new IndexerLayout({ model: self.indexerSettings }));
self.downloadClient.show(new DownloadClientLayout({ model: self.downloadClientSettings }));
@@ -123,6 +129,9 @@ define(
onShow: function () {
switch (this.action) {
case 'profiles':
this._showProfiles();
break;
case 'quality':
this._showQuality();
break;
@@ -158,6 +167,15 @@ define(
this._navigate('settings/mediamanagement');
},
_showProfiles: function (e) {
if (e) {
e.preventDefault();
}
this.ui.profilesTab.tab('show');
this._navigate('settings/profiles');
},
_showQuality: function (e) {
if (e) {
e.preventDefault();
@@ -1,5 +1,6 @@
<ul class="nav nav-tabs nav-justified settings-tabs">
<li><a href="#media-management" class="x-media-management-tab no-router">Media Management</a></li>
<li><a href="#profiles" class="x-profiles-tab no-router">Profiles</a></li>
<li><a href="#quality" class="x-quality-tab no-router">Quality</a></li>
<li><a href="#indexers" class="x-indexers-tab no-router">Indexers</a></li>
<li><a href="#download-client" class="x-download-client-tab no-router">Download Client</a></li>
@@ -34,6 +35,7 @@
<div class="tab-content">
<div class="tab-pane" id="media-management"></div>
<div class="tab-pane" id="profiles"></div>
<div class="tab-pane" id="quality"></div>
<div class="tab-pane" id="indexers"></div>
<div class="tab-pane" id="download-client"></div>
+1
View File
@@ -2,6 +2,7 @@
@import "../Shared/Styles/clickable.less";
@import "Indexers/indexers";
@import "Quality/quality";
@import "Profile/profile";
@import "Notifications/notifications";
@import "Metadata/metadata";
@import "DownloadClient/downloadclient";