1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-27 22:57:09 -04:00
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
@@ -0,0 +1,9 @@
'use strict';
define(
[
'marionette'
], function (Marionette) {
return Marionette.ItemView.extend({
template : 'Settings/Profile/Edit/EditProfileItemViewTemplate'
});
});
@@ -0,0 +1,3 @@
<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" />
@@ -0,0 +1,150 @@
'use strict';
define(
[
'underscore',
'vent',
'AppLayout',
'marionette',
'backbone',
'Settings/Profile/Edit/EditProfileItemView',
'Settings/Profile/Edit/QualitySortableCollectionView',
'Settings/Profile/Edit/EditProfileView',
'Settings/Profile/DeleteProfileView',
'Series/SeriesCollection',
'Config'
], function (_,
vent,
AppLayout,
Marionette,
Backbone,
EditProfileItemView,
QualitySortableCollectionView,
EditProfileView,
DeleteView,
SeriesCollection,
Config) {
return Marionette.Layout.extend({
template: 'Settings/Profile/Edit/EditProfileLayoutTemplate',
regions: {
fields : '#x-fields',
qualities: '#x-qualities'
},
ui: {
deleteButton: '.x-delete'
},
events: {
'click .x-save' : '_saveProfile',
'click .x-cancel' : '_cancelProfile',
'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 EditProfileView({ 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();
},
_saveProfile: 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);
});
}
},
_cancelProfile: 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 a profile that is attached to a series.');
}
else {
this.ui.deleteButton.removeClass('disabled');
}
},
_isQualityInUse: function () {
return SeriesCollection.where({'profileId': this.model.id}).length !== 0;
}
});
});
@@ -0,0 +1,37 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close x-cancel" aria-hidden="true">&times;</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>
@@ -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'
}
});
});