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

View File

@@ -7,7 +7,7 @@ define(
'AddSeries/RootFolders/RootFolderLayout',
'AddSeries/Existing/AddExistingSeriesCollectionView',
'AddSeries/AddSeriesView',
'Quality/QualityProfileCollection',
'Profile/ProfileCollection',
'AddSeries/RootFolders/RootFolderCollection',
'Series/SeriesCollection'
], function (vent,
@@ -16,7 +16,7 @@ define(
RootFolderLayout,
ExistingSeriesCollectionView,
AddSeriesView,
QualityProfileCollection,
ProfileCollection,
RootFolderCollection) {
return Marionette.Layout.extend({
@@ -36,7 +36,7 @@ define(
},
initialize: function () {
QualityProfileCollection.fetch();
ProfileCollection.fetch();
RootFolderCollection.fetch()
.done(function () {
RootFolderCollection.synced = true;

View File

@@ -6,7 +6,7 @@ define(
'AppLayout',
'backbone',
'marionette',
'Quality/QualityProfileCollection',
'Profile/ProfileCollection',
'AddSeries/RootFolders/RootFolderCollection',
'AddSeries/RootFolders/RootFolderLayout',
'Series/SeriesCollection',
@@ -19,7 +19,7 @@ define(
AppLayout,
Backbone,
Marionette,
QualityProfiles,
Profiles,
RootFolders,
RootFolderLayout,
SeriesCollection,
@@ -32,7 +32,7 @@ define(
template: 'AddSeries/SearchResultViewTemplate',
ui: {
qualityProfile : '.x-quality-profile',
profile : '.x-profile',
rootFolder : '.x-root-folder',
seasonFolder : '.x-season-folder',
seriesType : '.x-series-type',
@@ -42,11 +42,11 @@ define(
},
events: {
'click .x-add' : '_addSeries',
'change .x-quality-profile' : '_qualityProfileChanged',
'change .x-root-folder' : '_rootFolderChanged',
'change .x-season-folder' : '_seasonFolderChanged',
'change .x-series-type' : '_seriesTypeChanged'
'click .x-add' : '_addSeries',
'change .x-profile' : '_profileChanged',
'change .x-root-folder' : '_rootFolderChanged',
'change .x-season-folder' : '_seasonFolderChanged',
'change .x-series-type' : '_seriesTypeChanged'
},
initialize: function () {
@@ -65,13 +65,13 @@ define(
onRender: function () {
var defaultQuality = Config.getValue(Config.Keys.DefaultQualityProfileId);
var defaultProfile = Config.getValue(Config.Keys.DefaultProfileId);
var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId);
var useSeasonFolder = Config.getValueBoolean(Config.Keys.UseSeasonFolder, true);
var defaultSeriesType = Config.getValueBoolean(Config.Keys.DefaultSeriesType, true);
if (QualityProfiles.get(defaultQuality)) {
this.ui.qualityProfile.val(defaultQuality);
if (Profiles.get(defaultProfile)) {
this.ui.profile.val(defaultProfile);
}
if (RootFolders.get(defaultRoot)) {
@@ -101,7 +101,7 @@ define(
this.templateHelpers.existing = existingSeries[0].toJSON();
}
this.templateHelpers.qualityProfiles = QualityProfiles.toJSON();
this.templateHelpers.profiles = Profiles.toJSON();
if (!this.model.get('isExisting')) {
this.templateHelpers.rootFolders = RootFolders.toJSON();
@@ -109,8 +109,8 @@ define(
},
_onConfigUpdated: function (options) {
if (options.key === Config.Keys.DefaultQualityProfileId) {
this.ui.qualityProfile.val(options.value);
if (options.key === Config.Keys.DefaultProfileId) {
this.ui.profile.val(options.value);
}
else if (options.key === Config.Keys.DefaultRootFolderId) {
@@ -126,8 +126,8 @@ define(
}
},
_qualityProfileChanged: function () {
Config.setValue(Config.Keys.DefaultQualityProfileId, this.ui.qualityProfile.val());
_profileChanged: function () {
Config.setValue(Config.Keys.DefaultProfileId, this.ui.profile.val());
},
_seasonFolderChanged: function () {
@@ -160,14 +160,14 @@ define(
var icon = this.ui.addButton.find('icon');
icon.removeClass('icon-plus').addClass('icon-spin icon-spinner disabled');
var quality = this.ui.qualityProfile.val();
var profile = this.ui.profile.val();
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
var startingSeason = this.ui.startingSeason.val();
var seriesType = this.ui.seriesType.val();
var seasonFolder = this.ui.seasonFolder.prop('checked');
this.model.set({
qualityProfileId: quality,
profileId: profile,
rootFolderPath: rootFolderPath,
seasonFolder: seasonFolder,
seriesType: seriesType

View File

@@ -40,8 +40,8 @@
</div>
<div class="form-group col-md-2">
<label>Quality Profile</label>
{{> QualityProfileSelectionPartial qualityProfiles}}
<label>Profile</label>
{{> ProfileSelectionPartial profiles}}
</div>
<div class="form-group col-md-2">

View File

@@ -4,8 +4,8 @@ define(
'backgrid',
'marionette',
'underscore',
'Settings/Quality/Profile/QualityProfileSchemaCollection',
], function (Backgrid, Marionette, _, QualityProfileSchemaCollection) {
'Settings/Profile/ProfileSchemaCollection'
], function (Backgrid, Marionette, _, ProfileSchemaCollection) {
return Backgrid.CellEditor.extend({
className: 'quality-cell-editor',
@@ -21,12 +21,12 @@ define(
render: function () {
var self = this;
var qualityProfileSchemaCollection = new QualityProfileSchemaCollection();
var promise = qualityProfileSchemaCollection.fetch();
var profileSchemaCollection = new ProfileSchemaCollection();
var promise = profileSchemaCollection.fetch();
promise.done(function () {
var templateName = self.template;
self.schema = qualityProfileSchemaCollection.first();
self.schema = profileSchemaCollection.first();
var selected = _.find(self.schema.get('items'), function (model) {
return model.quality.id === self.model.get(self.column.get('name')).quality.id;
@@ -50,13 +50,13 @@ define(
var column = this.column;
var selected = parseInt(this.$el.val(), 10);
var qualityProfileItem = _.find(this.schema.get('items'), function(model) {
var profileItem = _.find(this.schema.get('items'), function(model) {
return model.quality.id === selected;
});
var newQuality = {
proper : false,
quality: qualityProfileItem.quality
quality: profileItem.quality
};
model.set(column.get('name'), newQuality);

View File

@@ -2,18 +2,18 @@
define(
[
'backgrid',
'Quality/QualityProfileCollection',
'Profile/ProfileCollection',
'underscore'
], function (Backgrid, QualityProfileCollection,_) {
], function (Backgrid, ProfileCollection,_) {
return Backgrid.Cell.extend({
className: 'quality-profile-cell',
className: 'profile-cell',
render: function () {
this.$el.empty();
var qualityProfileId = this.model.get(this.column.get('name'));
var profileId = this.model.get(this.column.get('name'));
var profile = _.findWhere(QualityProfileCollection.models, { id: qualityProfileId });
var profile = _.findWhere(ProfileCollection.models, { id: profileId });
if (profile) {
this.$el.html(profile.get('name'));

View File

@@ -8,7 +8,7 @@ define(
ConfigUpdatedEvent: 'ConfigUpdatedEvent'
},
Keys : {
DefaultQualityProfileId : 'DefaultQualityProfileId',
DefaultProfileId : 'DefaultProfileId',
DefaultRootFolderId : 'DefaultRootFolderId',
UseSeasonFolder : 'UseSeasonFolder',
DefaultSeriesType : 'DefaultSeriesType',

View File

@@ -1,6 +1,6 @@
<div class="episode-info">
{{#with series}}
{{qualityProfile qualityProfileId}}
{{profile profileId}}
<span class="label label-info">{{network}}</span>
{{/with}}
<span class="label label-info">{{StartTime airDateUtc}}</span>

View File

@@ -2,15 +2,15 @@
define(
[
'handlebars',
'Quality/QualityProfileCollection'
], function (Handlebars, QualityProfileCollection) {
'Profile/ProfileCollection'
], function (Handlebars, ProfileCollection) {
Handlebars.registerHelper('qualityProfile', function (profileId) {
Handlebars.registerHelper('profile', function (profileId) {
var profile = QualityProfileCollection.get(profileId);
var profile = ProfileCollection.get(profileId);
if (profile) {
return new Handlebars.SafeString('<span class="label label-default quality-profile-label">' + profile.get('name') + '</span>');
return new Handlebars.SafeString('<span class="label label-default profile-label">' + profile.get("name") + '</span>');
}
return undefined;

View File

@@ -0,0 +1,9 @@
'use strict';
define(
[
'handlebars'
], function (Handlebars) {
Handlebars.registerHelper('TitleCase', function (input) {
return new Handlebars.SafeString(input.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}));
});
});

View File

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

View File

@@ -31,6 +31,11 @@ define(
icon = 'icon-inbox';
title = 'Downloaded';
}
if (status === 'pending') {
icon = 'icon-time';
title = 'Pending';
}
if (errorMessage !== '') {
if (status === 'completed') {

View File

@@ -14,12 +14,21 @@ define(
if (this.cellValue) {
//If the release is pending we want to use the timeleft as the time it will be processed at
if (this.cellValue.get('status').toLowerCase() === 'pending') {
this.$el.html('-');
this.$el.attr('title', 'Will be processed again in: {0}'.format(this.cellValue.get('timeleft')));
this.$el.attr('data-container', 'body');
return this;
}
var timeleft = this.cellValue.get('timeleft');
var totalSize = fileSize(this.cellValue.get('size'), 1, false);
var remainingSize = fileSize(this.cellValue.get('sizeleft'), 1, false);
if (timeleft === undefined) {
this.$el.html("-");
this.$el.html('-');
}
else {
this.$el.html('<span title="{1} / {2}">{0}</span>'.format(timeleft, remainingSize, totalSize));

View File

@@ -0,0 +1,18 @@
'use strict';
define(
[
'backbone',
'Profile/ProfileModel'
], function (Backbone, ProfileModel) {
var ProfileCollection = Backbone.Collection.extend({
model: ProfileModel,
url : window.NzbDrone.ApiRoot + '/profile'
});
var profiles = new ProfileCollection();
profiles.fetch();
return profiles;
});

View File

@@ -1,4 +1,4 @@
<select class="col-md-2 form-control x-quality-profile">
<select class="col-md-2 form-control x-profile">
{{#each this}}
<option value="{{id}}">{{name}}</option>
{{/each}}

View File

@@ -1,18 +0,0 @@
'use strict';
define(
[
'backbone',
'Quality/QualityProfileModel'
], function (Backbone, QualityProfileModel) {
var QualityProfileCollection = Backbone.Collection.extend({
model: QualityProfileModel,
url : window.NzbDrone.ApiRoot + '/qualityprofile'
});
var profiles = new QualityProfileCollection();
profiles.fetch();
return profiles;
});

View File

@@ -1,6 +1,6 @@
<div class="row">
<div class="col-md-9">
{{qualityProfile qualityProfileId}}
{{profile profileId}}
<span class="label label-info">{{network}}</span>
<span class="label label-info">{{runtime}} minutes</span>
<span class="label label-info">{{path}}</span>

View File

@@ -3,17 +3,17 @@ define(
[
'vent',
'marionette',
'Quality/QualityProfileCollection',
'Profile/ProfileCollection',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView',
'Mixins/AutoComplete'
], function (vent, Marionette, QualityProfiles, AsModelBoundView, AsValidatedView) {
], function (vent, Marionette, Profiles, AsModelBoundView, AsValidatedView) {
var view = Marionette.ItemView.extend({
template: 'Series/Edit/EditSeriesViewTemplate',
ui: {
qualityProfile: '.x-quality-profile',
profile: '.x-profile',
path : '.x-path'
},
@@ -24,14 +24,14 @@ define(
initialize: function () {
this.model.set('qualityProfiles', QualityProfiles);
this.model.set('profiles', Profiles);
},
_saveSeries: function () {
var self = this;
var qualityProfileId = this.ui.qualityProfile.val();
this.model.set({ qualityProfileId: qualityProfileId});
var profileId = this.ui.profile.val();
this.model.set({ profileId: profileId});
this.model.save().done(function () {
self.trigger('saved');

View File

@@ -58,11 +58,11 @@
</div>
<div class="form-group">
<label class="col-sm-4 control-label">Quality Profile</label>
<label class="col-sm-4 control-label">Profile</label>
<div class="col-sm-4">
<select class="form-control x-quality-profile" name="qualityProfileId">
{{#each qualityProfiles.models}}
<select class="form-control x-profile" id="inputProfile" name="profileId">
{{#each profiles.models}}
<option value="{{id}}">{{attributes.name}}</option>
{{/each}}
</select>

View File

@@ -5,7 +5,7 @@ define(
'marionette',
'backgrid',
'vent',
'Quality/QualityProfileCollection',
'Profile/ProfileCollection',
'AddSeries/RootFolders/RootFolderCollection',
'Shared/Toolbar/ToolbarLayout',
'AddSeries/RootFolders/RootFolderLayout',
@@ -15,7 +15,7 @@ define(
Marionette,
Backgrid,
vent,
QualityProfiles,
Profiles,
RootFolders,
ToolbarLayout,
RootFolderLayout,
@@ -26,7 +26,7 @@ define(
ui: {
monitored : '.x-monitored',
qualityProfile : '.x-quality-profiles',
profile : '.x-profiles',
seasonFolder : '.x-season-folder',
rootFolder : '.x-root-folder',
selectedCount : '.x-selected-count',
@@ -43,8 +43,8 @@ define(
templateHelpers: function () {
return {
qualityProfiles: QualityProfiles,
rootFolders : RootFolders.toJSON()
profiles : Profiles,
rootFolders: RootFolders.toJSON()
};
},
@@ -68,7 +68,7 @@ define(
var selected = this.editorGrid.getSelectedModels();
var monitored = this.ui.monitored.val();
var profile = this.ui.qualityProfile.val();
var profile = this.ui.profile.val();
var seasonFolder = this.ui.seasonFolder.val();
var rootFolder = this.ui.rootFolder.val();
@@ -82,7 +82,7 @@ define(
}
if (profile !== 'noChange') {
model.set('qualityProfileId', parseInt(profile, 10));
model.set('profileId', parseInt(profile, 10));
}
if (seasonFolder === 'true') {
@@ -113,7 +113,7 @@ define(
if (selectedCount === 0) {
this.ui.monitored.attr('disabled', '');
this.ui.qualityProfile.attr('disabled', '');
this.ui.profile.attr('disabled', '');
this.ui.seasonFolder.attr('disabled', '');
this.ui.rootFolder.attr('disabled', '');
this.ui.saveButton.attr('disabled', '');
@@ -122,7 +122,7 @@ define(
else {
this.ui.monitored.removeAttr('disabled', '');
this.ui.qualityProfile.removeAttr('disabled', '');
this.ui.profile.removeAttr('disabled', '');
this.ui.seasonFolder.removeAttr('disabled', '');
this.ui.rootFolder.removeAttr('disabled', '');
this.ui.saveButton.removeAttr('disabled', '');

View File

@@ -21,11 +21,11 @@
</div>
<div class="form-group col-md-2">
<label>Quality Profile</label>
<label>Profile</label>
<select class="form-control x-quality-profiles">
<select class="form-control x-profiles">
<option value="noChange">No change</option>
{{#each qualityProfiles.models}}
{{#each profiles.models}}
<option value="{{id}}">{{attributes.name}}</option>
{{/each}}
</select>

View File

@@ -7,7 +7,7 @@ define(
'Series/Index/EmptyView',
'Series/SeriesCollection',
'Cells/SeriesTitleCell',
'Cells/QualityProfileCell',
'Cells/ProfileCell',
'Cells/SeriesStatusCell',
'Cells/SeasonFolderCell',
'Shared/Toolbar/ToolbarLayout',
@@ -19,7 +19,7 @@ define(
EmptyView,
SeriesCollection,
SeriesTitleCell,
QualityProfileCell,
ProfileCell,
SeriesStatusCell,
SeasonFolderCell,
ToolbarLayout,
@@ -33,10 +33,10 @@ define(
},
ui: {
monitored : '.x-monitored',
qualityProfiles: '.x-quality-profiles',
rootFolder : '.x-root-folder',
selectedCount : '.x-selected-count'
monitored : '.x-monitored',
profiles : '.x-profiles',
rootFolder : '.x-root-folder',
selectedCount : '.x-selected-count'
},
events: {
@@ -47,36 +47,36 @@ define(
columns:
[
{
name : '',
cell : 'select-row',
headerCell: 'select-all',
sortable : false
name : '',
cell : 'select-row',
headerCell : 'select-all',
sortable : false
},
{
name : 'statusWeight',
label : '',
cell : SeriesStatusCell
name : 'statusWeight',
label : '',
cell : SeriesStatusCell
},
{
name : 'title',
label : 'Title',
cell : SeriesTitleCell,
cellValue : 'this'
name : 'title',
label : 'Title',
cell : SeriesTitleCell,
cellValue : 'this'
},
{
name : 'qualityProfileId',
label : 'Quality',
cell : QualityProfileCell
name : 'profileId',
label : 'Profile',
cell : ProfileCell
},
{
name : 'seasonFolder',
label : 'Season Folder',
cell : SeasonFolderCell
name : 'seasonFolder',
label : 'Season Folder',
cell : SeasonFolderCell
},
{
name : 'path',
label : 'Path',
cell : 'string'
name : 'path',
label : 'Path',
cell : 'string'
}
],

View File

@@ -45,7 +45,7 @@
{{seasonCountHelper}}
{{qualityProfile qualityProfileId}}
{{profile profileId}}
</div>
<div class="col-md-2 col-xs-4">
{{> EpisodeProgressPartial }}

View File

@@ -11,7 +11,7 @@ define(
'Cells/RelativeDateCell',
'Cells/SeriesTitleCell',
'Cells/TemplatedCell',
'Cells/QualityProfileCell',
'Cells/ProfileCell',
'Cells/EpisodeProgressCell',
'Cells/SeriesActionsCell',
'Cells/SeriesStatusCell',
@@ -29,7 +29,7 @@ define(
RelativeDateCell,
SeriesTitleCell,
TemplatedCell,
QualityProfileCell,
ProfileCell,
EpisodeProgressCell,
SeriesActionsCell,
SeriesStatusCell,
@@ -65,9 +65,9 @@ define(
cell : 'integer'
},
{
name : 'qualityProfileId',
label : 'Quality',
cell : QualityProfileCell
name : 'profileId',
label: 'Profile',
cell : ProfileCell
},
{
name : 'network',
@@ -165,7 +165,7 @@ define(
},
{
title: 'Quality',
name : 'qualityProfileId'
name : 'profileId'
},
{
title: 'Network',

View File

@@ -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) {

View File

@@ -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'

View File

@@ -4,6 +4,6 @@ define(
'marionette'
], function (Marionette) {
return Marionette.ItemView.extend({
template : 'Settings/Quality/Profile/Edit/EditQualityProfileItemViewTemplate'
template : 'Settings/Profile/Edit/EditProfileItemViewTemplate'
});
});

View File

@@ -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;
}
});
});

View File

@@ -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);
});

View File

@@ -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>

View File

@@ -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'
}
});
});

View File

@@ -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;
});

View File

@@ -0,0 +1,10 @@
'use strict';
define(
[
'backbone'
], function (Backbone) {
return Backbone.Model.extend({
});
});

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);
});
});

View File

@@ -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>

View File

@@ -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'

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}));
}
});
});

View File

@@ -0,0 +1,3 @@
<div class="row">
<div class="col-md-12" id="profile"/>
</div>

View File

@@ -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'
});
});

View File

@@ -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: {

View File

@@ -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>

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;
}
}

View File

@@ -25,7 +25,7 @@ define(
},
initialize: function (options) {
this.qualityProfileCollection = options.qualityProfiles;
this.profileCollection = options.profiles;
this.filesize = fileSize;
},

View File

@@ -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);
});

View File

@@ -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>

View File

@@ -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'
}
});
});

View File

@@ -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'
});
});

View File

@@ -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>

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}));
}
});

View File

@@ -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>

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);

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();

View File

@@ -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>

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";