Merge pull request #497 from Radarr/feature/net-import
Feature/net import
commit
a1cb5eb420
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.NetImport
|
||||
{
|
||||
public interface INetImport : IProvider
|
||||
{
|
||||
bool Enabled { get; }
|
||||
bool EnableAuto { get; }
|
||||
|
||||
IList<Movie> Fetch();
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.NetImport
|
||||
{
|
||||
public class NetImportPageableRequest : IEnumerable<NetImportRequest>
|
||||
{
|
||||
private readonly IEnumerable<NetImportRequest> _enumerable;
|
||||
|
||||
public NetImportPageableRequest(IEnumerable<NetImportRequest> enumerable)
|
||||
{
|
||||
_enumerable = enumerable;
|
||||
}
|
||||
|
||||
public IEnumerator<NetImportRequest> GetEnumerator()
|
||||
{
|
||||
return _enumerable.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _enumerable.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Core.NetImport
|
||||
{
|
||||
public class NetImportPageableRequestChain
|
||||
{
|
||||
private List<List<NetImportPageableRequest>> _chains;
|
||||
|
||||
public NetImportPageableRequestChain()
|
||||
{
|
||||
_chains = new List<List<NetImportPageableRequest>>();
|
||||
_chains.Add(new List<NetImportPageableRequest>());
|
||||
}
|
||||
|
||||
public int Tiers => _chains.Count;
|
||||
|
||||
public IEnumerable<NetImportPageableRequest> GetAllTiers()
|
||||
{
|
||||
return _chains.SelectMany(v => v);
|
||||
}
|
||||
|
||||
public IEnumerable<NetImportPageableRequest> GetTier(int index)
|
||||
{
|
||||
return _chains[index];
|
||||
}
|
||||
|
||||
public void Add(IEnumerable<NetImportRequest> request)
|
||||
{
|
||||
if (request == null) return;
|
||||
|
||||
_chains.Last().Add(new NetImportPageableRequest(request));
|
||||
}
|
||||
|
||||
public void AddTier(IEnumerable<NetImportRequest> request)
|
||||
{
|
||||
AddTier();
|
||||
Add(request);
|
||||
}
|
||||
|
||||
public void AddTier()
|
||||
{
|
||||
if (_chains.Last().Count == 0) return;
|
||||
|
||||
_chains.Add(new List<NetImportPageableRequest>());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +1,46 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="btn-group add-movies-btn-group btn-group-lg btn-block">
|
||||
<button type="button" class="btn btn-default col-md-10 col-xs-8 add-movies-import-btn x-import">
|
||||
<i class="icon-sonarr-hdd"/>
|
||||
Import existing movies on disk
|
||||
</button>
|
||||
<button class="btn btn-default col-md-2 col-xs-4 x-add-new"><i class="icon-sonarr-active hidden-xs"></i> Add New Movie</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="btn-group add-movies-btn-group btn-group-lg btn-block">
|
||||
<button type="button" class="btn btn-default col-md-7 col-xs-4 add-movies-import-btn x-import">
|
||||
<i class="icon-sonarr-hdd"/>
|
||||
Import existing movies on disk
|
||||
</button>
|
||||
<button class="btn btn-default col-md-2 col-xs-4 x-add-new"><i class="icon-sonarr-active hidden-xs"></i> Add New Movie</button>
|
||||
<button class="btn btn-default col-md-3 col-xs-4 x-add-lists"><i class="icon-sonarr-active hidden-xs"></i> Add Movies from Lists</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-horizontal" style="margin-top: 15px;">
|
||||
<div class="form-group" style="margin-bottom: 0px;">
|
||||
<label class="col-sm-3 control-label">Display Existing Movies</label>
|
||||
<div class="col-md-12">
|
||||
<div class="form-horizontal" style="margin-top: 15px;">
|
||||
<div id="show-existing-movies-toggle">
|
||||
<div class="form-group" style="margin-bottom: 0px;">
|
||||
<label class="col-sm-3 control-label">Display Existing Movies</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input class="x-show-existing" type="checkbox" checked="checked" name="showExisting"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input class="x-show-existing" type="checkbox" checked="checked" name="showExisting"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-info" title="Should Radarr display movies already in your collection?"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-info" title="Should Radarr display movies already in your collection?"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="add-movies-workspace"></div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div id="add-movies-workspace"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,18 @@
|
||||
var Backbone = require('backbone');
|
||||
var MovieModel = require('../../Movies/MovieModel');
|
||||
var _ = require('underscore');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/netimport/movies',
|
||||
model : MovieModel,
|
||||
|
||||
parse : function(response) {
|
||||
var self = this;
|
||||
|
||||
_.each(response, function(model) {
|
||||
model.id = undefined;
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
});
|
@ -0,0 +1,47 @@
|
||||
var Marionette = require('marionette');
|
||||
var ListItemView = require('./ListItemView');
|
||||
var vent = require('vent');
|
||||
|
||||
module.exports = Marionette.CollectionView.extend({
|
||||
itemView : ListItemView,
|
||||
|
||||
ui : {
|
||||
loadingList : '.x-loading-list'
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
|
||||
},
|
||||
|
||||
showCollection : function() {
|
||||
},
|
||||
//
|
||||
// appendHtml : function(collectionView, itemView, index) {
|
||||
// collectionView.ui.loadingFolders.before(itemView.el);
|
||||
// },
|
||||
//
|
||||
// _showAndSearch : function(index) {
|
||||
// var self = this;
|
||||
// var model = this.collection.at(index);
|
||||
//
|
||||
// if (model) {
|
||||
// var currentIndex = index;
|
||||
// var folderName = model.get('folder').name;
|
||||
// this.addItemView(model, this.getItemView(), index);
|
||||
// this.children.findByModel(model).search({ term : folderName }).always(function() {
|
||||
// if (!self.isClosed) {
|
||||
// self._showAndSearch(currentIndex + 1);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// else {
|
||||
// this.ui.loadingFolders.hide();
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// itemViewOptions : {
|
||||
// isExisting : true
|
||||
// }
|
||||
|
||||
});
|
@ -0,0 +1,4 @@
|
||||
<div class="x-list">
|
||||
<div class="x-loading-list">
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,254 @@
|
||||
var _ = require('underscore');
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
var AddFromListCollection = require('./AddFromListCollection');
|
||||
var AddFromListCollectionView = require('./AddFromListCollectionView');
|
||||
var AddListView = require("../../Settings/NetImport/Add/NetImportAddItemView");
|
||||
var EmptyView = require('../EmptyView');
|
||||
var NotFoundView = require('../NotFoundView');
|
||||
var ListCollection = require("../../Settings/NetImport/NetImportCollection");
|
||||
var ErrorView = require('../ErrorView');
|
||||
var LoadingView = require('../../Shared/LoadingView');
|
||||
var AppLayout = require('../../AppLayout');
|
||||
var InCinemasCell = require('../../Cells/InCinemasCell');
|
||||
var MovieTitleCell = require('../../Cells/MovieListTitleCell');
|
||||
var SelectAllCell = require('../../Cells/SelectAllCell');
|
||||
var TemplatedCell = require('../../Cells/TemplatedCell');
|
||||
var ProfileCell = require('../../Cells/ProfileCell');
|
||||
var MovieLinksCell = require('../../Cells/MovieLinksCell');
|
||||
var MovieActionCell = require('../../Cells/MovieActionCell');
|
||||
var MovieStatusCell = require('../../Cells/MovieStatusCell');
|
||||
var MovieDownloadStatusCell = require('../../Cells/MovieDownloadStatusCell');
|
||||
var DownloadedQualityCell = require('../../Cells/DownloadedQualityCell');
|
||||
var MoviesCollection = require('../../Movies/MoviesCollection');
|
||||
var Messenger = require('../../Shared/Messenger');
|
||||
require('jquery.dotdotdot');
|
||||
var SchemaModal = require('../../Settings/NetImport/Add/NetImportSchemaModal');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'AddMovies/List/AddFromListViewTemplate',
|
||||
|
||||
regions : {
|
||||
fetchResult : '#fetch-result'
|
||||
},
|
||||
|
||||
ui : {
|
||||
moviesSearch : '.x-movies-search',
|
||||
listSelection : ".x-list-selection",
|
||||
importSelected : ".x-import-selected"
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : '',
|
||||
cell : SelectAllCell,
|
||||
headerCell : 'select-all',
|
||||
sortable : false
|
||||
},
|
||||
{
|
||||
name : 'title',
|
||||
label : 'Title',
|
||||
cell : MovieTitleCell,
|
||||
cellValue : 'this',
|
||||
},
|
||||
{
|
||||
name : 'profileId',
|
||||
label : 'Profile',
|
||||
cell : ProfileCell
|
||||
},
|
||||
{
|
||||
name : 'this',
|
||||
label : 'Links',
|
||||
cell : MovieLinksCell,
|
||||
className : "movie-links-cell",
|
||||
sortable : false,
|
||||
}
|
||||
],
|
||||
|
||||
events : {
|
||||
'click .x-load-more' : '_onLoadMore',
|
||||
"change .x-list-selection" : "_listSelected",
|
||||
"click .x-fetch-list" : "_fetchList",
|
||||
"click .x-import-selected" : "_importSelected"
|
||||
},
|
||||
|
||||
initialize : function(options) {
|
||||
console.log(options);
|
||||
|
||||
this.isExisting = options.isExisting;
|
||||
//this.collection = new AddFromListCollection();
|
||||
|
||||
this.templateHelpers = {}
|
||||
this.listCollection = new ListCollection();
|
||||
this.templateHelpers.lists = this.listCollection.toJSON();
|
||||
|
||||
this.listenTo(this.listCollection, 'all', this._listsUpdated);
|
||||
this.listCollection.fetch();
|
||||
|
||||
this.collection = new AddFromListCollection();
|
||||
|
||||
this.listenTo(this.collection, 'sync', this._showResults);
|
||||
|
||||
/*this.listenTo(this.collection, 'sync', this._showResults);
|
||||
|
||||
this.resultCollectionView = new SearchResultCollectionView({
|
||||
collection : this.collection,
|
||||
isExisting : this.isExisting
|
||||
});*/
|
||||
|
||||
//this.throttledSearch = _.debounce(this.search, 1000, { trailing : true }).bind(this);
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
var self = this;
|
||||
this.ui.importSelected.hide();
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.ui.moviesSearch.focus();
|
||||
|
||||
},
|
||||
|
||||
search : function(options) {
|
||||
var self = this;
|
||||
|
||||
this.collection.reset();
|
||||
|
||||
if (!options.term || options.term === this.collection.term) {
|
||||
return Marionette.$.Deferred().resolve();
|
||||
}
|
||||
|
||||
this.searchResult.show(new LoadingView());
|
||||
this.collection.term = options.term;
|
||||
this.currentSearchPromise = this.collection.fetch({
|
||||
data : { term : options.term }
|
||||
});
|
||||
|
||||
this.currentSearchPromise.fail(function() {
|
||||
self._showError();
|
||||
});
|
||||
|
||||
return this.currentSearchPromise;
|
||||
},
|
||||
|
||||
_onMoviesAdded : function(options) {
|
||||
if (this.isExisting && options.movie.get('path') === this.model.get('folder').path) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
else if (!this.isExisting) {
|
||||
this.resultCollectionView.setExisting(options.movie.get('tmdbId'));
|
||||
/*this.collection.term = '';
|
||||
this.collection.reset();
|
||||
this._clearResults();
|
||||
this.ui.moviesSearch.val('');
|
||||
this.ui.moviesSearch.focus();*/ //TODO: Maybe add option wheter to clear search result.
|
||||
}
|
||||
},
|
||||
|
||||
_onLoadMore : function() {
|
||||
var showingAll = this.resultCollectionView.showMore();
|
||||
this.ui.searchBar.show();
|
||||
|
||||
if (showingAll) {
|
||||
this.ui.loadMore.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_listSelected : function() {
|
||||
var rootFolderValue = this.ui.listSelection.val();
|
||||
if (rootFolderValue === 'addNew') {
|
||||
//var rootFolderLayout = new SchemaModal(this.listCollection);
|
||||
//AppLayout.modalRegion.show(rootFolderLayout);
|
||||
SchemaModal.open(this.listCollection)
|
||||
}
|
||||
},
|
||||
|
||||
_fetchList : function() {
|
||||
var self = this;
|
||||
var listId = this.ui.listSelection.val();
|
||||
|
||||
this.fetchResult.show(new LoadingView());
|
||||
|
||||
this.currentFetchPromise = this.collection.fetch(
|
||||
{ data : { listId : listId} }
|
||||
)
|
||||
this.currentFetchPromise.fail(function() {
|
||||
self._showError();
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_listsUpdated : function() {
|
||||
this.templateHelpers.lists = this.listCollection.toJSON();
|
||||
this.render();
|
||||
},
|
||||
|
||||
_importSelected : function() {
|
||||
var selected = this.importGrid.getSelectedModels();
|
||||
console.log(selected);
|
||||
var promise = MoviesCollection.importFromList(selected);
|
||||
this.ui.importSelected.spinForPromise(promise);
|
||||
this.ui.importSelected.addClass('disabled');
|
||||
|
||||
Messenger.show({
|
||||
message : "Importing {0} movies. This can take multiple minutes depending on how many movies should be imported. Don't close this browser window until it is finished!".format(selected.length),
|
||||
hideOnNavigate : false,
|
||||
hideAfter : 30,
|
||||
type : "error"
|
||||
});
|
||||
|
||||
promise.done(function() {
|
||||
Messenger.show({
|
||||
message : "Imported movies from list.",
|
||||
hideAfter : 8,
|
||||
hideOnNavigate : true
|
||||
});
|
||||
});
|
||||
/*for (m in selected) {
|
||||
debugger;
|
||||
m.save()
|
||||
MoviesCollection.add(m);
|
||||
}*/
|
||||
|
||||
//MoviesCollection.save();
|
||||
},
|
||||
|
||||
_clearResults : function() {
|
||||
|
||||
if (!this.isExisting) {
|
||||
this.searchResult.show(new EmptyView());
|
||||
} else {
|
||||
this.searchResult.close();
|
||||
}
|
||||
},
|
||||
|
||||
_showResults : function() {
|
||||
if (this.collection.length === 0) {
|
||||
this.fetchResult.show(new NotFoundView({ term : "" }));
|
||||
} else {
|
||||
this.importGrid = new Backgrid.Grid({
|
||||
collection : this.collection,
|
||||
columns : this.columns,
|
||||
className : 'table table-hover'
|
||||
});
|
||||
this.fetchResult.show(this.importGrid);
|
||||
this.ui.importSelected.show();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_abortExistingSearch : function() {
|
||||
if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) {
|
||||
console.log('aborting previous pending search request.');
|
||||
this.currentSearchPromise.abort();
|
||||
} else {
|
||||
this._clearResults();
|
||||
}
|
||||
},
|
||||
|
||||
_showError : function() {
|
||||
this.fetchResult.show(new ErrorView({ term : "" }));
|
||||
}
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
<div class="x-search-bar">
|
||||
<div class="form-group" style="margin-bottom: 0px;">
|
||||
<label class="col-sm-1 control-label">List</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{{> ListSelectionPartial lists}}
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<button class="btn x-fetch-list">Fetch List</button>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-success x-import-selected"><i class="icon-sonarr-add"></i> Import Selected</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div id="fetch-result" class="result-list col-md-12"/>
|
||||
</div>
|
@ -0,0 +1,22 @@
|
||||
var _ = require('underscore');
|
||||
var vent = require('vent');
|
||||
var AppLayout = require('../../AppLayout');
|
||||
var Backbone = require('backbone');
|
||||
var Marionette = require('marionette');
|
||||
var Config = require('../../Config');
|
||||
var Messenger = require('../../Shared/Messenger');
|
||||
var AsValidatedView = require('../../Mixins/AsValidatedView');
|
||||
|
||||
require('jquery.dotdotdot');
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
|
||||
template : 'AddMovies/SearchResultViewTemplate',
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
AsValidatedView.apply(view);
|
||||
|
||||
module.exports = view;
|
@ -0,0 +1,3 @@
|
||||
<div class="fetch-item">
|
||||
ASDF
|
||||
</div>
|
@ -0,0 +1,7 @@
|
||||
var TemplatedCell = require('./TemplatedCell');
|
||||
|
||||
module.exports = TemplatedCell.extend({
|
||||
className : 'series-title-cell',
|
||||
template : 'Cells/MovieListTitleTemplate',
|
||||
|
||||
});
|
@ -0,0 +1 @@
|
||||
<a href="{{imdbUrl}}">{{title}}</a>
|
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,9 @@
|
||||
var ThingyAddCollectionView = require('../../ThingyAddCollectionView');
|
||||
var ThingyHeaderGroupView = require('../../ThingyHeaderGroupView');
|
||||
var AddItemView = require('./NetImportAddItemView');
|
||||
|
||||
module.exports = ThingyAddCollectionView.extend({
|
||||
itemView : ThingyHeaderGroupView.extend({ itemView : AddItemView }),
|
||||
itemViewContainer : '.add-indexer .items',
|
||||
template : 'Settings/NetImport/Add/NetImportAddCollectionViewTemplate'
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Add List</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info">
|
||||
Radarr supports any RSS movie lists as well as the one stated below.<br/>
|
||||
For more information on the individual lists, click on the info buttons.
|
||||
</div>
|
||||
<div class="add-indexer add-thingies">
|
||||
<ul class="items"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,51 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var AppLayout = require('../../../AppLayout');
|
||||
var Marionette = require('marionette');
|
||||
var EditView = require('../Edit/NetImportEditView');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Settings/NetImport/Add/NetImportAddItemViewTemplate',
|
||||
tagName : 'li',
|
||||
className : 'add-thingy-item',
|
||||
|
||||
events : {
|
||||
'click .x-preset' : '_addPreset',
|
||||
'click' : '_add'
|
||||
},
|
||||
|
||||
initialize : function(options) {
|
||||
this.targetCollection = options.targetCollection;
|
||||
},
|
||||
|
||||
_addPreset : function(e) {
|
||||
var presetName = $(e.target).closest('.x-preset').attr('data-id');
|
||||
var presetData = _.where(this.model.get('presets'), { name : presetName })[0];
|
||||
|
||||
this.model.set(presetData);
|
||||
|
||||
this._openEdit();
|
||||
},
|
||||
|
||||
_add : function(e) {
|
||||
if ($(e.target).closest('.btn,.btn-group').length !== 0 && $(e.target).closest('.x-custom').length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._openEdit();
|
||||
},
|
||||
|
||||
_openEdit : function() {
|
||||
this.model.set({
|
||||
id : undefined,
|
||||
enableAuto : this.model.get('enableAuto')
|
||||
});
|
||||
|
||||
var editView = new EditView({
|
||||
model : this.model,
|
||||
targetCollection : this.targetCollection
|
||||
});
|
||||
|
||||
AppLayout.modalRegion.show(editView);
|
||||
}
|
||||
});
|
@ -0,0 +1,30 @@
|
||||
<div class="add-thingy">
|
||||
<div>
|
||||
{{implementationName}}
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{{#if_gt presets.length compare=0}}
|
||||
<button class="btn btn-xs btn-default x-custom">
|
||||
Custom
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
Presets
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{#each presets}}
|
||||
<li class="x-preset" data-id="{{name}}">
|
||||
<a>{{name}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if_gt}}
|
||||
{{#if infoLink}}
|
||||
<a class="btn btn-xs btn-default x-info" href="{{infoLink}}">
|
||||
<i class="icon-sonarr-form-info"/>
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,40 @@
|
||||
var _ = require('underscore');
|
||||
var AppLayout = require('../../../AppLayout');
|
||||
var Backbone = require('backbone');
|
||||
var SchemaCollection = require('../NetImportCollection');
|
||||
var AddCollectionView = require('./NetImportAddCollectionView');
|
||||
|
||||
module.exports = {
|
||||
open : function(collection) {
|
||||
var schemaCollection = new SchemaCollection();
|
||||
var originalUrl = schemaCollection.url;
|
||||
schemaCollection.url = schemaCollection.url + '/schema';
|
||||
schemaCollection.fetch();
|
||||
schemaCollection.url = originalUrl;
|
||||
|
||||
var groupedSchemaCollection = new Backbone.Collection();
|
||||
|
||||
schemaCollection.on('sync', function() {
|
||||
|
||||
var groups = schemaCollection.groupBy(function(model, iterator) {
|
||||
return model.get('protocol');
|
||||
});
|
||||
//key is "undefined", which is being placed in the header
|
||||
var modelCollection = _.map(groups, function(values, key, list) {
|
||||
return {
|
||||
//"header" : key,
|
||||
collection : values
|
||||
};
|
||||
});
|
||||
|
||||
groupedSchemaCollection.reset(modelCollection);
|
||||
});
|
||||
|
||||
var view = new AddCollectionView({
|
||||
collection : groupedSchemaCollection,
|
||||
targetCollection : collection
|
||||
});
|
||||
|
||||
AppLayout.modalRegion.show(view);
|
||||
}
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Settings/Indexers/Delete/IndexerDeleteViewTemplate',
|
||||
|
||||
events : {
|
||||
'click .x-confirm-delete' : '_delete'
|
||||
},
|
||||
|
||||
_delete : function() {
|
||||
this.model.destroy({
|
||||
wait : true,
|
||||
success : function() {
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -0,0 +1,13 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Delete Indexer</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>
|
@ -0,0 +1,175 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var DeleteView = require('../Delete/IndexerDeleteView');
|
||||
var Profiles = require('../../../Profile/ProfileCollection');
|
||||
var AsModelBoundView = require('../../../Mixins/AsModelBoundView');
|
||||
var AsValidatedView = require('../../../Mixins/AsValidatedView');
|
||||
var AsEditModalView = require('../../../Mixins/AsEditModalView');
|
||||
var RootFolders = require('../../../AddMovies/RootFolders/RootFolderCollection');
|
||||
var RootFolderLayout = require('../../../AddMovies/RootFolders/RootFolderLayout');
|
||||
var Config = require('../../../Config');
|
||||
require('../../../Form/FormBuilder');
|
||||
require('../../../Mixins/AutoComplete');
|
||||
require('bootstrap');
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Settings/NetImport/Edit/NetImportEditViewTemplate',
|
||||
|
||||
ui : {
|
||||
profile : '.x-profile',
|
||||
rootFolder : '.x-root-folder',
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-back' : '_back',
|
||||
'click .x-captcha-refresh' : '_onRefreshCaptcha',
|
||||
'change .x-root-folder' : '_rootFolderChanged',
|
||||
},
|
||||
|
||||
_deleteView : DeleteView,
|
||||
|
||||
initialize : function(options) {
|
||||
this.targetCollection = options.targetCollection;
|
||||
this.templateHelpers = {};
|
||||
|
||||
this._configureTemplateHelpers();
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
this.listenTo(RootFolders, 'all', this._rootFoldersUpdated);
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId);
|
||||
if (RootFolders.get(defaultRoot)) {
|
||||
this.ui.rootFolder.val(defaultRoot);
|
||||
}
|
||||
},
|
||||
|
||||
_onBeforeSave : function() {
|
||||
var profile = this.ui.profile.val();
|
||||
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
||||
this.model.set({
|
||||
profileId : profile,
|
||||
rootFolderPath : rootFolderPath,
|
||||
})
|
||||
},
|
||||
|
||||
_onAfterSave : function() {
|
||||
this.targetCollection.add(this.model, { merge : true });
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
},
|
||||
|
||||
_onAfterSaveAndAdd : function() {
|
||||
this.targetCollection.add(this.model, { merge : true });
|
||||
|
||||
require('../Add/NetImportSchemaModal').open(this.targetCollection);
|
||||
},
|
||||
|
||||
_back : function() {
|
||||
if (this.model.isNew()) {
|
||||
this.model.destroy();
|
||||
}
|
||||
|
||||
require('../Add/NetImportSchemaModal').open(this.targetCollection);
|
||||
},
|
||||
|
||||
_configureTemplateHelpers : function() {
|
||||
this.templateHelpers.profiles = Profiles.toJSON();
|
||||
this.templateHelpers.rootFolders = RootFolders.toJSON();
|
||||
},
|
||||
|
||||
_rootFolderChanged : function() {
|
||||
var rootFolderValue = this.ui.rootFolder.val();
|
||||
if (rootFolderValue === 'addNew') {
|
||||
var rootFolderLayout = new RootFolderLayout();
|
||||
this.listenToOnce(rootFolderLayout, 'folderSelected', this._setRootFolder);
|
||||
AppLayout.modalRegion.show(rootFolderLayout);
|
||||
} else {
|
||||
Config.setValue(Config.Keys.DefaultRootFolderId, rootFolderValue);
|
||||
}
|
||||
},
|
||||
|
||||
_rootFoldersUpdated : function() {
|
||||
this._configureTemplateHelpers();
|
||||
debugger;
|
||||
this.render();
|
||||
},
|
||||
|
||||
_onRefreshCaptcha : function(event) {
|
||||
var self = this;
|
||||
|
||||
var target = $(event.target).parents('.input-group');
|
||||
|
||||
this.ui.indicator.show();
|
||||
|
||||
this.model.requestAction("checkCaptcha")
|
||||
.then(function(result) {
|
||||
if (!result.captchaRequest) {
|
||||
self.model.setFieldValue('CaptchaToken', '');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return self._showCaptcha(target, result.captchaRequest);
|
||||
})
|
||||
.always(function() {
|
||||
self.ui.indicator.hide();
|
||||
});
|
||||
},
|
||||
|
||||
_showCaptcha : function(target, captchaRequest) {
|
||||
var self = this;
|
||||
|
||||
var widget = $('<div class="g-recaptcha"></div>').insertAfter(target);
|
||||
|
||||
return this._loadRecaptchaWidget(widget[0], captchaRequest.siteKey, captchaRequest.secretToken)
|
||||
.then(function(captchaResponse) {
|
||||
target.parents('.form-group').removeAllErrors();
|
||||
widget.remove();
|
||||
|
||||
var queryParams = {
|
||||
responseUrl : captchaRequest.responseUrl,
|
||||
ray : captchaRequest.ray,
|
||||
captchaResponse: captchaResponse
|
||||
};
|
||||
|
||||
return self.model.requestAction("getCaptchaCookie", queryParams);
|
||||
})
|
||||
.then(function(response) {
|
||||
self.model.setFieldValue('CaptchaToken', response.captchaToken);
|
||||
});
|
||||
},
|
||||
|
||||
_loadRecaptchaWidget : function(widget, sitekey, stoken) {
|
||||
var promise = $.Deferred();
|
||||
|
||||
var renderWidget = function() {
|
||||
window.grecaptcha.render(widget, {
|
||||
'sitekey' : sitekey,
|
||||
'stoken' : stoken,
|
||||
'callback' : promise.resolve
|
||||
});
|
||||
};
|
||||
|
||||
if (window.grecaptcha) {
|
||||
renderWidget();
|
||||
} else {
|
||||
window.grecaptchaLoadCallback = function() {
|
||||
delete window.grecaptchaLoadCallback;
|
||||
renderWidget();
|
||||
};
|
||||
|
||||
$.getScript('https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadCallback&render=explicit')
|
||||
.fail(function() { promise.reject(); });
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
|
||||
AsModelBoundView.call(view);
|
||||
AsValidatedView.call(view);
|
||||
AsEditModalView.call(view);
|
||||
|
||||
module.exports = view;
|
@ -0,0 +1,106 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" aria-hidden="true" data-dismiss="modal">×</button>
|
||||
{{#if id}}
|
||||
<h3>Edit - {{implementationName}}</h3>
|
||||
{{else}}
|
||||
<h3>Add - {{implementationName}}</h3>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-body indexer-modal">
|
||||
<div class="form-horizontal">
|
||||
<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">Enable Automatic Sync</label>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="enableAuto" {{#if enableAuto}} checked="checked" {{/if}} />
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"></div>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-warning" title="" data-original-title="New movies found by this list are automatically added to your collection."></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Add Movies Monitored</label>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="shouldMonitor" {{#if shouldMonitor}} checked="checked" {{/if}} />
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"></div>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-info" title="" data-original-title="If enabled, movies found by this list are added and monitored."></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Quality Profile</label>
|
||||
|
||||
<div class="col-sm-5">
|
||||
{{> ProfileSelectionPartial profiles}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Folder</label>
|
||||
|
||||
<div class="col-sm-5">
|
||||
{{> RootFolderSelectionPartial rootFolders}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{formBuilder}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{{#if id}}
|
||||
<button class="btn btn-danger pull-left x-delete">Delete</button>
|
||||
{{else}}
|
||||
<button class="btn pull-left x-back">Back</button>
|
||||
{{/if}}
|
||||
<span class="indicator x-indicator"><i class="icon-sonarr-spinner fa-spin"></i></span>
|
||||
<button class="btn x-test">test <i class="x-test-icon icon-sonarr-test"/></button>
|
||||
<button class="btn" data-dismiss="modal">Cancel</button>
|
||||
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary x-save">Save</button>
|
||||
<button class="btn btn-icon-only btn-primary dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="save-and-add x-save-and-add">
|
||||
save and add
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
<select class="col-md-4 form-control x-list-selection" validation-name="ListSelection">
|
||||
<option value="0">All</option>
|
||||
{{#if this}}
|
||||
{{#each this}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<option value="">Select List</option>
|
||||
{{/if}}
|
||||
<option value="addNew">Add a new list</option>
|
||||
</select>
|
@ -0,0 +1,13 @@
|
||||
var Backbone = require('backbone');
|
||||
var NetImportModel = require('./NetImportModel');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
model : NetImportModel,
|
||||
url : window.NzbDrone.ApiRoot + '/netimport',
|
||||
|
||||
comparator : function(left, right, collection) {
|
||||
var result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
<fieldset>
|
||||
<legend>Lists</legend>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<ul class="list-list thingies">
|
||||
<li>
|
||||
<div class="list-item thingy add-card x-add-card">
|
||||
<span class="center well">
|
||||
<i class="icon-sonarr-add"/>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue