mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-24 22:35:39 -04:00
Profiles
Indexes are created with the same uniqueness when copying a table New: Non-English episode support New: Renamed Quality Profiles to Profiles and made them more powerful New: Configurable wait time before grabbing a release to wait for a better quality
This commit is contained in:
@@ -25,7 +25,7 @@ define(
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.qualityProfileCollection = options.qualityProfiles;
|
||||
this.profileCollection = options.profiles;
|
||||
this.filesize = fileSize;
|
||||
},
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'handlebars',
|
||||
'underscore'
|
||||
], function (Handlebars, _) {
|
||||
Handlebars.registerHelper('allowedLabeler', function () {
|
||||
var ret = '';
|
||||
var cutoff = this.cutoff;
|
||||
_.each(this.items, function (item) {
|
||||
if (item.allowed) {
|
||||
if (item.quality.id === cutoff.id) {
|
||||
ret += '<li><span class="label label-info" title="Cutoff">' + item.quality.name + '</span></li>';
|
||||
}
|
||||
else {
|
||||
ret += '<li><span class="label label-default">' + item.quality.name + '</span></li>';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return new Handlebars.SafeString(ret);
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'vent',
|
||||
'marionette'
|
||||
], function (vent, Marionette) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Settings/Quality/Profile/DeleteQualityProfileViewTemplate',
|
||||
|
||||
events: {
|
||||
'click .x-confirm-delete': '_removeProfile'
|
||||
},
|
||||
|
||||
_removeProfile: function () {
|
||||
|
||||
this.model.destroy({
|
||||
wait: true
|
||||
}).done(function () {
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Delete: {{name}}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete '{{name}}'?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
<button class="btn btn-danger x-confirm-delete">delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette'
|
||||
], function (Marionette) {
|
||||
return Marionette.ItemView.extend({
|
||||
template : 'Settings/Quality/Profile/Edit/EditQualityProfileItemViewTemplate'
|
||||
});
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
<i class="select-handle pull-left x-select" />
|
||||
<span class="quality-label">{{quality.name}}</span>
|
||||
<i class="drag-handle pull-right icon-reorder advanced-setting x-drag-handle" />
|
||||
@@ -1,150 +0,0 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'underscore',
|
||||
'vent',
|
||||
'AppLayout',
|
||||
'marionette',
|
||||
'backbone',
|
||||
'Settings/Quality/Profile/Edit/EditQualityProfileItemView',
|
||||
'Settings/Quality/Profile/Edit/QualitySortableCollectionView',
|
||||
'Settings/Quality/Profile/Edit/EditQualityProfileView',
|
||||
'Settings/Quality/Profile/DeleteQualityProfileView',
|
||||
'Series/SeriesCollection',
|
||||
'Config'
|
||||
], function (_,
|
||||
vent,
|
||||
AppLayout,
|
||||
Marionette,
|
||||
Backbone,
|
||||
EditQualityProfileItemView,
|
||||
QualitySortableCollectionView,
|
||||
EditQualityProfileView,
|
||||
DeleteView,
|
||||
SeriesCollection,
|
||||
Config) {
|
||||
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Settings/Quality/Profile/Edit/EditQualityProfileLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
fields : '#x-fields',
|
||||
qualities: '#x-qualities'
|
||||
},
|
||||
|
||||
ui: {
|
||||
deleteButton: '.x-delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-save' : '_saveQualityProfile',
|
||||
'click .x-cancel' : '_cancelQualityProfile',
|
||||
'click .x-delete' : '_delete'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.profileCollection = options.profileCollection;
|
||||
this.itemsCollection = new Backbone.Collection(_.toArray(this.model.get('items')).reverse());
|
||||
this.listenTo(SeriesCollection, 'all', this._updateDisableStatus);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this._updateDisableStatus();
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.fieldsView = new EditQualityProfileView({ model: this.model });
|
||||
this._showFieldsView();
|
||||
|
||||
this.sortableListView = new QualitySortableCollectionView({
|
||||
selectable : true,
|
||||
selectMultiple : true,
|
||||
clickToSelect : true,
|
||||
clickToToggle : true,
|
||||
sortable : Config.getValueBoolean(Config.Keys.AdvancedSettings, false),
|
||||
|
||||
sortableOptions : {
|
||||
handle: '.x-drag-handle'
|
||||
},
|
||||
|
||||
collection: this.itemsCollection,
|
||||
model : this.model
|
||||
});
|
||||
|
||||
this.sortableListView.setSelectedModels(this.itemsCollection.filter(function(item) { return item.get('allowed') === true; }));
|
||||
this.qualities.show(this.sortableListView);
|
||||
|
||||
this.listenTo(this.sortableListView, 'selectionChanged', this._selectionChanged);
|
||||
this.listenTo(this.sortableListView, 'sortStop', this._updateModel);
|
||||
},
|
||||
|
||||
_selectionChanged: function(newSelectedModels, oldSelectedModels) {
|
||||
var addedModels = _.difference(newSelectedModels, oldSelectedModels);
|
||||
var removeModels = _.difference(oldSelectedModels, newSelectedModels);
|
||||
|
||||
_.each(removeModels, function(item) { item.set('allowed', false); });
|
||||
_.each(addedModels, function(item) { item.set('allowed', true); });
|
||||
|
||||
this._updateModel();
|
||||
},
|
||||
|
||||
_updateModel: function() {
|
||||
this.model.set('items', this.itemsCollection.toJSON().reverse());
|
||||
|
||||
this._showFieldsView();
|
||||
},
|
||||
|
||||
_saveQualityProfile: function () {
|
||||
var self = this;
|
||||
var cutoff = this.fieldsView.getCutoff();
|
||||
this.model.set('cutoff', cutoff);
|
||||
|
||||
var promise = this.model.save();
|
||||
|
||||
if (promise) {
|
||||
promise.done(function () {
|
||||
self.profileCollection.add(self.model, { merge: true });
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_cancelQualityProfile: function () {
|
||||
if (!this.model.has('id')) {
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
return;
|
||||
}
|
||||
|
||||
var promise = this.model.fetch();
|
||||
|
||||
if (promise) {
|
||||
promise.done(function () {
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_showFieldsView: function () {
|
||||
this.fields.show(this.fieldsView);
|
||||
},
|
||||
|
||||
_delete: function () {
|
||||
var view = new DeleteView({ model: this.model });
|
||||
AppLayout.modalRegion.show(view);
|
||||
},
|
||||
|
||||
_updateDisableStatus: function () {
|
||||
if (this._isQualityInUse()) {
|
||||
this.ui.deleteButton.addClass('disabled');
|
||||
this.ui.deleteButton.attr('title', 'Can\'t delete quality profiles attached to a series.');
|
||||
}
|
||||
else {
|
||||
this.ui.deleteButton.removeClass('disabled');
|
||||
}
|
||||
},
|
||||
|
||||
_isQualityInUse: function () {
|
||||
return SeriesCollection.where({'qualityProfileId': this.model.id}).length !== 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close x-cancel" aria-hidden="true">×</button>
|
||||
{{#if id}}
|
||||
<h3>Edit</h3>
|
||||
{{else}}
|
||||
<h3>Add</h3>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div id="x-fields"></div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Qualities</label>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="controls qualities-controls">
|
||||
<span id="x-qualities"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1 help-inline">
|
||||
<i class="icon-nd-form-info" title="Qualities higher in the list are more preferred. Only checked qualities will be wanted."/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{{#if id}}
|
||||
<button class="btn btn-danger pull-left x-delete">delete</button>
|
||||
{{/if}}
|
||||
<button class="btn x-cancel">cancel</button>
|
||||
<button class="btn btn-primary x-save">save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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,16 +0,0 @@
|
||||
<fieldset>
|
||||
<legend>Quality Profiles</legend>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<ul class="quality-profiles thingies">
|
||||
<li>
|
||||
<div class="quality-profile-item thingy add-card x-add-card">
|
||||
<span class="center well">
|
||||
<i class="icon-plus" title="Add Profile"/>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -1,44 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
define(['AppLayout',
|
||||
'marionette',
|
||||
'Settings/Quality/Profile/QualityProfileView',
|
||||
'Settings/Quality/Profile/Edit/EditQualityProfileLayout',
|
||||
'Settings/Quality/Profile/QualityProfileSchemaCollection',
|
||||
'underscore'
|
||||
], function (AppLayout, Marionette, QualityProfileView, EditProfileView, ProfileCollection, _) {
|
||||
|
||||
return Marionette.CompositeView.extend({
|
||||
itemView : QualityProfileView,
|
||||
itemViewContainer: '.quality-profiles',
|
||||
template : 'Settings/Quality/Profile/QualityProfileCollectionTemplate',
|
||||
|
||||
ui: {
|
||||
'addCard': '.x-add-card'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-add-card': '_addProfile'
|
||||
},
|
||||
|
||||
appendHtml: function(collectionView, itemView, index){
|
||||
collectionView.ui.addCard.parent('li').before(itemView.el);
|
||||
},
|
||||
|
||||
_addProfile: function () {
|
||||
var self = this;
|
||||
var schemaCollection = new ProfileCollection();
|
||||
schemaCollection.fetch({
|
||||
success: function (collection) {
|
||||
var model = _.first(collection.models);
|
||||
model.set('id', undefined);
|
||||
model.set('name', '');
|
||||
model.collection = self.collection;
|
||||
|
||||
var view = new EditProfileView({ model: model, profileCollection: self.collection});
|
||||
AppLayout.modalRegion.show(view);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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,37 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'AppLayout',
|
||||
'marionette',
|
||||
'Settings/Quality/Profile/Edit/EditQualityProfileLayout',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Settings/Quality/Profile/AllowedLabeler',
|
||||
'bootstrap'
|
||||
], function (AppLayout, Marionette, EditProfileView, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/Quality/Profile/QualityProfileViewTemplate',
|
||||
tagName : 'li',
|
||||
|
||||
ui: {
|
||||
'progressbar' : '.progress .bar',
|
||||
'deleteButton': '.x-delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click' : '_editProfile'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'sync', this.render);
|
||||
},
|
||||
|
||||
_editProfile: function () {
|
||||
var view = new EditProfileView({ model: this.model, profileCollection: this.model.collection });
|
||||
AppLayout.modalRegion.show(view);
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user