commit
b359e1c175
@ -1,5 +1,11 @@
|
|||||||
<ul>
|
<ul>
|
||||||
{{#each this}}
|
{{#each this}}
|
||||||
<li>{{this}}</li>
|
<li>
|
||||||
|
{{#if reason}}
|
||||||
|
{{reason}}
|
||||||
|
{{else}}
|
||||||
|
{{this}}
|
||||||
|
{{/if}}
|
||||||
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var vent = require('../../vent');
|
||||||
|
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||||
|
var SelectEpisodeLayout = require('../Episode/SelectEpisodeLayout');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'episodes-cell editable',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click' : '_onClick'
|
||||||
|
},
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
|
||||||
|
var episodes = this.model.get('episodes');
|
||||||
|
|
||||||
|
if (episodes)
|
||||||
|
{
|
||||||
|
var episodeNumbers = _.map(episodes, 'episodeNumber');
|
||||||
|
|
||||||
|
this.$el.html(episodeNumbers.join(', '));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClick : function () {
|
||||||
|
var series = this.model.get('series');
|
||||||
|
var seasonNumber = this.model.get('seasonNumber');
|
||||||
|
|
||||||
|
if (series === undefined || seasonNumber === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = new SelectEpisodeLayout({ series: series, seasonNumber: seasonNumber });
|
||||||
|
|
||||||
|
this.listenTo(view, 'manualimport:selected:episodes', this._setEpisodes);
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.OpenModal2Command, view);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setEpisodes : function (e) {
|
||||||
|
this.model.set('episodes', e.episodes);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,16 @@
|
|||||||
|
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'path-cell',
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
|
||||||
|
var relativePath = this.model.get('relativePath');
|
||||||
|
var path = this.model.get('path');
|
||||||
|
|
||||||
|
this.$el.html('<div title="{0}">{1}</div>'.format(path, relativePath));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,23 @@
|
|||||||
|
var vent = require('../../vent');
|
||||||
|
var QualityCell = require('../../Cells/QualityCell');
|
||||||
|
var SelectQualityLayout = require('../Quality/SelectQualityLayout');
|
||||||
|
|
||||||
|
module.exports = QualityCell.extend({
|
||||||
|
className : 'quality-cell editable',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click' : '_onClick'
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClick : function () {
|
||||||
|
var view = new SelectQualityLayout();
|
||||||
|
|
||||||
|
this.listenTo(view, 'manualimport:selected:quality', this._setQuality);
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.OpenModal2Command, view);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setQuality : function (e) {
|
||||||
|
this.model.set('quality', e.quality);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,47 @@
|
|||||||
|
var vent = require('../../vent');
|
||||||
|
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||||
|
var SelectSeasonLayout = require('../Season/SelectSeasonLayout');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'season-cell editable',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click' : '_onClick'
|
||||||
|
},
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
|
||||||
|
if (this.model.has('seasonNumber')) {
|
||||||
|
this.$el.html(this.model.get('seasonNumber'));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delegateEvents();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClick : function () {
|
||||||
|
var series = this.model.get('series');
|
||||||
|
|
||||||
|
if (!series) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = new SelectSeasonLayout({ seasons: series.seasons });
|
||||||
|
|
||||||
|
this.listenTo(view, 'manualimport:selected:season', this._setSeason);
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.OpenModal2Command, view);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setSeason : function (e) {
|
||||||
|
if (this.model.has('seasonNumber') && e.seasonNumber === this.model.get('seasonNumber')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model.set({
|
||||||
|
seasonNumber : e.seasonNumber,
|
||||||
|
episodes : []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,45 @@
|
|||||||
|
var vent = require('../../vent');
|
||||||
|
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||||
|
var SelectSeriesLayout = require('../Series/SelectSeriesLayout');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'series-title-cell editable',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click' : '_onClick'
|
||||||
|
},
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
|
||||||
|
var series = this.model.get('series');
|
||||||
|
|
||||||
|
if (series)
|
||||||
|
{
|
||||||
|
this.$el.html(series.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delegateEvents();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClick : function () {
|
||||||
|
var view = new SelectSeriesLayout();
|
||||||
|
|
||||||
|
this.listenTo(view, 'manualimport:selected:series', this._setSeries);
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.OpenModal2Command, view);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setSeries : function (e) {
|
||||||
|
if (this.model.has('series') && e.model.id === this.model.get('series').id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model.set({
|
||||||
|
series : e.model.toJSON(),
|
||||||
|
seasonNumber : undefined,
|
||||||
|
episodes : []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,5 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.CompositeView.extend({
|
||||||
|
template : 'ManualImport/EmptyViewTemplate'
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
No video files were found in the selected folder.
|
@ -0,0 +1,81 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var vent = require('vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var Backgrid = require('backgrid');
|
||||||
|
var EpisodeCollection = require('../../Series/EpisodeCollection');
|
||||||
|
var LoadingView = require('../../Shared/LoadingView');
|
||||||
|
var SelectAllCell = require('../../Cells/SelectAllCell');
|
||||||
|
var EpisodeNumberCell = require('../../Series/Details/EpisodeNumberCell');
|
||||||
|
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||||
|
var SelectEpisodeRow = require('./SelectEpisodeRow');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
template : 'ManualImport/Episode/SelectEpisodeLayoutTemplate',
|
||||||
|
|
||||||
|
regions : {
|
||||||
|
episodes : '.x-episodes'
|
||||||
|
},
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click .x-select' : '_selectEpisodes'
|
||||||
|
},
|
||||||
|
|
||||||
|
columns : [
|
||||||
|
{
|
||||||
|
name : '',
|
||||||
|
cell : SelectAllCell,
|
||||||
|
headerCell : 'select-all',
|
||||||
|
sortable : false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'episodeNumber',
|
||||||
|
label : '#',
|
||||||
|
cell : EpisodeNumberCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'title',
|
||||||
|
label : 'Title',
|
||||||
|
hideSeriesLink : true,
|
||||||
|
cell : 'string',
|
||||||
|
sortable : false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'airDateUtc',
|
||||||
|
label : 'Air Date',
|
||||||
|
cell : RelativeDateCell
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
this.series = options.series;
|
||||||
|
this.seasonNumber = options.seasonNumber;
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
this.episodes.show(new LoadingView());
|
||||||
|
|
||||||
|
this.episodeCollection = new EpisodeCollection({ seriesId : this.series.id });
|
||||||
|
this.episodeCollection.fetch();
|
||||||
|
|
||||||
|
this.listenToOnce(this.episodeCollection, 'sync', function () {
|
||||||
|
|
||||||
|
this.episodeView = new Backgrid.Grid({
|
||||||
|
columns : this.columns,
|
||||||
|
collection : this.episodeCollection.bySeason(this.seasonNumber),
|
||||||
|
className : 'table table-hover season-grid',
|
||||||
|
row : SelectEpisodeRow
|
||||||
|
});
|
||||||
|
|
||||||
|
this.episodes.show(this.episodeView);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_selectEpisodes : function () {
|
||||||
|
var episodes = _.map(this.episodeView.getSelectedModels(), function (episode) {
|
||||||
|
return episode.toJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.trigger('manualimport:selected:episodes', { episodes: episodes });
|
||||||
|
vent.trigger(vent.Commands.CloseModal2Command);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,21 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="manual-import-modal">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
Manual Import - Select Episode(s)
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 x-episodes"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-default" data-dismiss="modal">cancel</button>
|
||||||
|
<button class="btn btn-success x-select" data-dismiss="modal">select episodes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,20 @@
|
|||||||
|
var Backgrid = require('backgrid');
|
||||||
|
|
||||||
|
module.exports = Backgrid.Row.extend({
|
||||||
|
className : 'select-episode-row',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click' : '_toggle'
|
||||||
|
},
|
||||||
|
|
||||||
|
_toggle : function(e) {
|
||||||
|
|
||||||
|
if (e.target.type === 'checkbox') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var checked = this.$el.find('.select-row-cell :checkbox').prop('checked');
|
||||||
|
|
||||||
|
this.model.trigger('backgrid:select', this.model, !checked);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
require('../../Mixins/FileBrowser');
|
||||||
|
|
||||||
|
module.exports = Marionette.ItemView.extend({
|
||||||
|
template : 'ManualImport/Folder/SelectFolderViewTemplate',
|
||||||
|
|
||||||
|
ui : {
|
||||||
|
path : '.x-path',
|
||||||
|
buttons : '.x-button'
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'click .x-manual-import' : '_manualImport',
|
||||||
|
'click .x-automatic-import' : '_automaticImport',
|
||||||
|
'change .x-path' : '_updateButtons',
|
||||||
|
'keyup .x-path' : '_updateButtons'
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
this.ui.path.fileBrowser();
|
||||||
|
this._updateButtons();
|
||||||
|
},
|
||||||
|
|
||||||
|
path : function() {
|
||||||
|
return this.ui.path.val();
|
||||||
|
},
|
||||||
|
|
||||||
|
_manualImport : function () {
|
||||||
|
if (this.ui.path.val()) {
|
||||||
|
this.trigger('manualImport', { folder: this.ui.path.val() });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_automaticImport : function () {
|
||||||
|
if (this.ui.path.val()) {
|
||||||
|
this.trigger('automaticImport', { folder: this.ui.path.val() });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateButtons : function () {
|
||||||
|
if (this.ui.path.val()) {
|
||||||
|
this.ui.buttons.removeAttr('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this.ui.buttons.attr('disabled', 'disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,21 @@
|
|||||||
|
<div class="select-folder">
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<input type="text" class="form-control x-path" placeholder="Select a folder to import" name="path">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
<button class="btn btn-primary btn-lg btn-block x-automatic-import x-button"><i class="icon-sonarr-search-automatic"></i> Import File(s) Automatically</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
<button class="btn btn-primary btn-lg btn-block x-manual-import x-button"><i class="icon-sonarr-search-manual"></i> Manual Import</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,74 @@
|
|||||||
|
var PageableCollection = require('backbone.pageable');
|
||||||
|
var ManualImportModel = require('./ManualImportModel');
|
||||||
|
var AsSortedCollection = require('../Mixins/AsSortedCollection');
|
||||||
|
|
||||||
|
var Collection = PageableCollection.extend({
|
||||||
|
model : ManualImportModel,
|
||||||
|
url : window.NzbDrone.ApiRoot + '/manualimport',
|
||||||
|
|
||||||
|
state : {
|
||||||
|
sortKey : 'quality',
|
||||||
|
order : 1,
|
||||||
|
pageSize : 100000
|
||||||
|
},
|
||||||
|
|
||||||
|
mode : 'client',
|
||||||
|
|
||||||
|
originalFetch : PageableCollection.prototype.fetch,
|
||||||
|
|
||||||
|
initialize : function (options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (!options.folder && !options.downloadId) {
|
||||||
|
throw 'folder or downloadId is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.folder = options.folder;
|
||||||
|
this.downloadId = options.downloadId;
|
||||||
|
},
|
||||||
|
|
||||||
|
fetch : function(options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
options.data = { folder : this.folder, downloadId : this.downloadId };
|
||||||
|
|
||||||
|
return this.originalFetch.call(this, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
sortMappings : {
|
||||||
|
series : {
|
||||||
|
sortValue : function(model, attr, order) {
|
||||||
|
var series = model.get(attr);
|
||||||
|
|
||||||
|
if (series) {
|
||||||
|
return series.sortTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
quality : {
|
||||||
|
sortKey : 'qualityWeight'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
comparator : function(model1, model2) {
|
||||||
|
var quality1 = model1.get('quality');
|
||||||
|
var quality2 = model2.get('quality');
|
||||||
|
|
||||||
|
if (quality1 < quality2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quality1 > quality2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Collection = AsSortedCollection.call(Collection);
|
||||||
|
|
||||||
|
module.exports = Collection;
|
@ -0,0 +1,222 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var vent = require('vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var Backgrid = require('backgrid');
|
||||||
|
var CommandController = require('../Commands/CommandController');
|
||||||
|
var EmptyView = require('./EmptyView');
|
||||||
|
var SelectFolderView = require('./Folder/SelectFolderView');
|
||||||
|
var LoadingView = require('../Shared/LoadingView');
|
||||||
|
var ManualImportRow = require('./ManualImportRow');
|
||||||
|
var SelectAllCell = require('../Cells/SelectAllCell');
|
||||||
|
var PathCell = require('./Cells/PathCell');
|
||||||
|
var SeriesCell = require('./Cells/SeriesCell');
|
||||||
|
var SeasonCell = require('./Cells/SeasonCell');
|
||||||
|
var EpisodesCell = require('./Cells/EpisodesCell');
|
||||||
|
var QualityCell = require('./Cells/QualityCell');
|
||||||
|
var FileSizeCell = require('../Cells/FileSizeCell');
|
||||||
|
var ApprovalStatusCell = require('../Cells/ApprovalStatusCell');
|
||||||
|
var ManualImportCollection = require('./ManualImportCollection');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
className : 'modal-lg',
|
||||||
|
template : 'ManualImport/ManualImportLayoutTemplate',
|
||||||
|
|
||||||
|
regions : {
|
||||||
|
workspace : '.x-workspace'
|
||||||
|
},
|
||||||
|
|
||||||
|
ui : {
|
||||||
|
importButton : '.x-import'
|
||||||
|
},
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click .x-import' : '_import'
|
||||||
|
},
|
||||||
|
|
||||||
|
columns : [
|
||||||
|
{
|
||||||
|
name : '',
|
||||||
|
cell : SelectAllCell,
|
||||||
|
headerCell : 'select-all',
|
||||||
|
sortable : false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'relativePath',
|
||||||
|
label : 'Relative Path',
|
||||||
|
cell : PathCell,
|
||||||
|
sortable : true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'series',
|
||||||
|
label : 'Series',
|
||||||
|
cell : SeriesCell,
|
||||||
|
sortable : true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'seasonNumber',
|
||||||
|
label : 'Season',
|
||||||
|
cell : SeasonCell,
|
||||||
|
sortable : true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'episodes',
|
||||||
|
label : 'Episode(s)',
|
||||||
|
cell : EpisodesCell,
|
||||||
|
sortable : false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'quality',
|
||||||
|
label : 'Quality',
|
||||||
|
cell : QualityCell,
|
||||||
|
sortable : true
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'size',
|
||||||
|
label : 'Size',
|
||||||
|
cell : FileSizeCell,
|
||||||
|
sortable : true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'rejections',
|
||||||
|
label : '<i class="icon-sonarr-header-rejections" />',
|
||||||
|
tooltip : 'Rejections',
|
||||||
|
cell : ApprovalStatusCell,
|
||||||
|
sortable : false,
|
||||||
|
sortType : 'fixed',
|
||||||
|
direction : 'ascending'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
this.folder = options.folder;
|
||||||
|
this.downloadId = options.downloadId;
|
||||||
|
this.title = options.title;
|
||||||
|
|
||||||
|
//TODO: remove (just for testing)
|
||||||
|
this.folder = 'C:\\Test';
|
||||||
|
// this.folder = 'E:\\X-Server';
|
||||||
|
|
||||||
|
this.templateHelpers = {
|
||||||
|
title : this.title || this.folder
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
|
||||||
|
if (this.folder || this.downloadId) {
|
||||||
|
this._showLoading();
|
||||||
|
this._loadCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this._showSelectFolder();
|
||||||
|
this.ui.importButton.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_showLoading : function () {
|
||||||
|
this.workspace.show(new LoadingView());
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadCollection : function () {
|
||||||
|
this.manualImportCollection = new ManualImportCollection({ folder: this.folder, downloadId: this.downloadId });
|
||||||
|
this.manualImportCollection.fetch();
|
||||||
|
|
||||||
|
this.listenTo(this.manualImportCollection, 'sync', this._showTable);
|
||||||
|
this.listenTo(this.manualImportCollection, 'backgrid:selected', this._updateButtons);
|
||||||
|
},
|
||||||
|
|
||||||
|
_showTable : function () {
|
||||||
|
if (this.manualImportCollection.length === 0) {
|
||||||
|
this.workspace.show(new EmptyView());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileView = new Backgrid.Grid({
|
||||||
|
columns : this.columns,
|
||||||
|
collection : this.manualImportCollection,
|
||||||
|
className : 'table table-hover',
|
||||||
|
row : ManualImportRow
|
||||||
|
});
|
||||||
|
|
||||||
|
this.workspace.show(this.fileView);
|
||||||
|
this._updateButtons();
|
||||||
|
},
|
||||||
|
|
||||||
|
_showSelectFolder : function () {
|
||||||
|
this.selectFolderView = new SelectFolderView();
|
||||||
|
this.workspace.show(this.selectFolderView);
|
||||||
|
|
||||||
|
this.listenTo(this.selectFolderView, 'manualImport', this._manualImport);
|
||||||
|
this.listenTo(this.selectFolderView, 'automaticImport', this._automaticImport);
|
||||||
|
},
|
||||||
|
|
||||||
|
_manualImport : function (e) {
|
||||||
|
this.folder = e.folder;
|
||||||
|
this.templateHelpers.title = this.folder;
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
_automaticImport : function (e) {
|
||||||
|
CommandController.Execute('downloadedEpisodesScan', {
|
||||||
|
name : 'downloadedEpisodesScan',
|
||||||
|
path : e.folder
|
||||||
|
});
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.CloseModalCommand);
|
||||||
|
},
|
||||||
|
|
||||||
|
_import : function () {
|
||||||
|
var selected = this.fileView.getSelectedModels();
|
||||||
|
|
||||||
|
if (selected.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandController.Execute('manualImport', {
|
||||||
|
name : 'manualImport',
|
||||||
|
files : _.map(selected, function (file) {
|
||||||
|
return {
|
||||||
|
path : file.get('path'),
|
||||||
|
seriesId : file.get('series').id,
|
||||||
|
episodeIds : _.map(file.get('episodes'), 'id'),
|
||||||
|
quality : file.get('quality'),
|
||||||
|
downloadId : file.get('downloadId')
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.CloseModalCommand);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateButtons : function (model, selected) {
|
||||||
|
if (!this.fileView) {
|
||||||
|
this.ui.importButton.attr('disabled', 'disabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedModels = this.fileView.getSelectedModels();
|
||||||
|
var selectedCount = 0;
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
selectedCount = _.any(selectedModels, { id : model.id }) ? selectedModels.length : selectedModels.length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
selectedCount = _.any(selectedModels, { id : model.id }) ? selectedModels.length - 1 : selectedModels.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedCount === 0) {
|
||||||
|
this.ui.importButton.attr('disabled', 'disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this.ui.importButton.removeAttr('disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="manual-import-modal">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
Manual Import - {{#if title}}{{title}}{{else}}Select Folder{{/if}}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="x-workspace"></div>
|
||||||
|
<div class="x-footer"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-default" data-dismiss="modal">cancel</button>
|
||||||
|
<button class="btn btn-success x-import" disabled="disabled">import</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,4 @@
|
|||||||
|
var Backbone = require('backbone');
|
||||||
|
|
||||||
|
module.exports = Backbone.Model.extend({
|
||||||
|
});
|
@ -0,0 +1,34 @@
|
|||||||
|
var Backgrid = require('backgrid');
|
||||||
|
|
||||||
|
module.exports = Backgrid.Row.extend({
|
||||||
|
className : 'manual-import-row',
|
||||||
|
|
||||||
|
_originalInit : Backgrid.Row.prototype.initialize,
|
||||||
|
_originalRender : Backgrid.Row.prototype.render,
|
||||||
|
|
||||||
|
initialize : function () {
|
||||||
|
this._originalInit.apply(this, arguments);
|
||||||
|
|
||||||
|
this.listenTo(this.model, 'change', this._setError);
|
||||||
|
},
|
||||||
|
|
||||||
|
render : function () {
|
||||||
|
this._originalRender.apply(this, arguments);
|
||||||
|
this._setError();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setError : function () {
|
||||||
|
if (this.model.has('series') &&
|
||||||
|
this.model.has('seasonNumber') &&
|
||||||
|
(this.model.has('episodes') && this.model.get('episodes').length > 0)&&
|
||||||
|
this.model.has('quality')) {
|
||||||
|
this.$el.removeClass('manual-import-error');
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this.$el.addClass('manual-import-error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,43 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var vent = require('../../vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var LoadingView = require('../../Shared/LoadingView');
|
||||||
|
var ProfileSchemaCollection = require('../../Settings/Profile/ProfileSchemaCollection');
|
||||||
|
var SelectQualityView = require('./SelectQualityView');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
template : 'ManualImport/Quality/SelectQualityLayoutTemplate',
|
||||||
|
|
||||||
|
regions : {
|
||||||
|
quality : '.x-quality'
|
||||||
|
},
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click .x-select' : '_selectQuality'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize : function() {
|
||||||
|
this.profileSchemaCollection = new ProfileSchemaCollection();
|
||||||
|
this.profileSchemaCollection.fetch();
|
||||||
|
|
||||||
|
this.listenTo(this.profileSchemaCollection, 'sync', this._showQuality);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
this.quality.show(new LoadingView());
|
||||||
|
},
|
||||||
|
|
||||||
|
_showQuality : function () {
|
||||||
|
var qualities = _.map(this.profileSchemaCollection.first().get('items'), function (quality) {
|
||||||
|
return quality.quality;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selectQualityView = new SelectQualityView({ qualities: qualities });
|
||||||
|
this.quality.show(this.selectQualityView);
|
||||||
|
},
|
||||||
|
|
||||||
|
_selectQuality : function () {
|
||||||
|
this.trigger('manualimport:selected:quality', { quality: this.selectQualityView.selectedQuality() });
|
||||||
|
vent.trigger(vent.Commands.CloseModal2Command);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,19 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="manual-import-modal">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
Manual Import - Select Quality
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="x-quality"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-default" data-dismiss="modal">cancel</button>
|
||||||
|
<button class="btn btn-success x-select" data-dismiss="modal">select quality</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,37 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.ItemView.extend({
|
||||||
|
template : 'ManualImport/Quality/SelectQualityViewTemplate',
|
||||||
|
|
||||||
|
ui : {
|
||||||
|
select : '.x-select-quality',
|
||||||
|
proper : 'x-proper'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
this.qualities = options.qualities;
|
||||||
|
|
||||||
|
this.templateHelpers = {
|
||||||
|
qualities: this.qualities
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedQuality : function () {
|
||||||
|
var selected = parseInt(this.ui.select.val(), 10);
|
||||||
|
var proper = this.ui.proper.prop('checked');
|
||||||
|
|
||||||
|
var quality = _.find(this.qualities, function(q) {
|
||||||
|
return q.id === selected;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
quality : quality,
|
||||||
|
revision : {
|
||||||
|
version : proper ? 2 : 1,
|
||||||
|
real : 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,33 @@
|
|||||||
|
<div class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">Quality</label>
|
||||||
|
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<select class="form-control x-select-quality">
|
||||||
|
<option value="-1">Select Quality</option>
|
||||||
|
{{#each qualities}}
|
||||||
|
<option value="{{id}}">{{name}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">Proper</label>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="checkbox toggle well">
|
||||||
|
<input type="checkbox" class="x-proper"/>
|
||||||
|
<p>
|
||||||
|
<span>Yes</span>
|
||||||
|
<span>No</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
var vent = require('vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
template : 'ManualImport/Season/SelectSeasonLayoutTemplate',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'change .x-select-season' : '_selectSeason'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
|
||||||
|
this.templateHelpers = {
|
||||||
|
seasons : options.seasons
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_selectSeason : function (e) {
|
||||||
|
var seasonNumber = parseInt(e.target.value, 10);
|
||||||
|
|
||||||
|
if (seasonNumber === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trigger('manualimport:selected:season', { seasonNumber: seasonNumber });
|
||||||
|
vent.trigger(vent.Commands.CloseModal2Command);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,29 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="manual-import-modal">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
Manual Import - Select Season
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group col-md-4 col-md-offset-4">
|
||||||
|
<select class="form-control x-select-season">
|
||||||
|
<option value="-1">Select Season</option>
|
||||||
|
{{#each seasons}}
|
||||||
|
<option value="{{seasonNumber}}">Season {{seasonNumber}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-default" data-dismiss="modal">cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var vent = require('vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var Backgrid = require('backgrid');
|
||||||
|
var SeriesCollection = require('../../Series/SeriesCollection');
|
||||||
|
var SelectRow = require('./SelectSeriesRow');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
template : 'ManualImport/Series/SelectSeriesLayoutTemplate',
|
||||||
|
|
||||||
|
regions : {
|
||||||
|
series : '.x-series'
|
||||||
|
},
|
||||||
|
|
||||||
|
ui : {
|
||||||
|
filter : '.x-filter'
|
||||||
|
},
|
||||||
|
|
||||||
|
columns : [
|
||||||
|
{
|
||||||
|
name : 'title',
|
||||||
|
label : 'Title',
|
||||||
|
cell : 'String',
|
||||||
|
sortValue : 'sortTitle'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
initialize : function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.seriesCollection = SeriesCollection.clone();
|
||||||
|
|
||||||
|
_.each(this.seriesCollection.models, function (model) {
|
||||||
|
model.collection = self.seriesCollection;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.listenTo(this.seriesCollection, 'row:selected', this._onSelected);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
this.seriesView = new Backgrid.Grid({
|
||||||
|
columns : this.columns,
|
||||||
|
collection : this.seriesCollection,
|
||||||
|
className : 'table table-hover season-grid',
|
||||||
|
row : SelectRow
|
||||||
|
});
|
||||||
|
|
||||||
|
this.series.show(this.seriesView);
|
||||||
|
this._setupFilter();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupFilter : function () {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
//TODO: This should be a mixin (same as Add Series searching)
|
||||||
|
this.ui.filter.keyup(function(e) {
|
||||||
|
if (_.contains([
|
||||||
|
9,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
35,
|
||||||
|
36,
|
||||||
|
37,
|
||||||
|
38,
|
||||||
|
39,
|
||||||
|
40,
|
||||||
|
91,
|
||||||
|
92,
|
||||||
|
93
|
||||||
|
], e.keyCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self._filter(self.ui.filter.val());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_filter : function (term) {
|
||||||
|
this.seriesCollection.setFilter(['title', term, 'contains']);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSelected : function (e) {
|
||||||
|
this.trigger('manualimport:selected:series', { model: e.model });
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.CloseModal2Command);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,30 @@
|
|||||||
|
<div class="modal-content">
|
||||||
|
<div class="manual-import-modal">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
Manual Import - Select Series
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control x-filter" placeholder="Filter series" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 x-series"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-default" data-dismiss="modal">cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
var Backgrid = require('backgrid');
|
||||||
|
|
||||||
|
module.exports = Backgrid.Row.extend({
|
||||||
|
className : 'select-row select-series-row',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click' : '_onClick'
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClick : function() {
|
||||||
|
this.model.collection.trigger('row:selected', { model: this.model });
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.ItemView.extend({
|
||||||
|
template : 'ManualImport/Summary/ManualImportSummaryViewTemplate',
|
||||||
|
|
||||||
|
initialize : function (options) {
|
||||||
|
var episodes = _.map(options.episodes, function (episode) {
|
||||||
|
return episode.toJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.templateHelpers = {
|
||||||
|
file : options.file,
|
||||||
|
series : options.series,
|
||||||
|
season : options.season,
|
||||||
|
episodes : episodes,
|
||||||
|
quality : options.quality
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,19 @@
|
|||||||
|
<dl class="dl-horizontal">
|
||||||
|
|
||||||
|
<dt>Path:</dt>
|
||||||
|
<dd>{{file}}</dd>
|
||||||
|
|
||||||
|
<dt>Series:</dt>
|
||||||
|
<dd>{{series.title}}</dd>
|
||||||
|
|
||||||
|
<dt>Season:</dt>
|
||||||
|
<dd>{{season.seasonNumber}}</dd>
|
||||||
|
|
||||||
|
{{#each episodes}}
|
||||||
|
<dt>Episode:</dt>
|
||||||
|
<dd>{{episodeNumber}} - {{title}}</dd>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<dt>Quality:</dt>
|
||||||
|
<dd>{{quality.name}}</dd>
|
||||||
|
</dl>
|
@ -0,0 +1,39 @@
|
|||||||
|
@import "../Shared/Styles/card.less";
|
||||||
|
@import "../Shared/Styles/clickable.less";
|
||||||
|
@import "../Content/Bootstrap/variables";
|
||||||
|
|
||||||
|
.manual-import-modal {
|
||||||
|
.path-cell {
|
||||||
|
word-break : break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-size-cell {
|
||||||
|
min-width : 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editable {
|
||||||
|
.clickable();
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
.clickable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-row {
|
||||||
|
.clickable();
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-folder {
|
||||||
|
.buttons {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual-import-error {
|
||||||
|
background-color : #fdefef;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue