Completely overhauled how import exclusions work.

Currently new exclusions can only be added when adding new movies or deleting old ones. Not manually in the settings menu.
Movies can now be hidden in the new discover feature by using the new import exclusions!
This commit is contained in:
Leonardo Galli
2017-05-09 20:44:07 +02:00
parent ab28bfead2
commit fc1585e900
28 changed files with 478 additions and 110 deletions

View File

@@ -6,6 +6,7 @@ var AddMoviesCollection = require('./AddMoviesCollection');
var SearchResultCollectionView = require('./SearchResultCollectionView');
var EmptyView = require('./EmptyView');
var NotFoundView = require('./NotFoundView');
var DiscoverEmptyView = require('./DiscoverEmptyView');
var ErrorView = require('./ErrorView');
var LoadingView = require('../Shared/LoadingView');
var FullMovieCollection = require("../Movies/FullMovieCollection");
@@ -112,14 +113,10 @@ module.exports = Marionette.Layout.extend({
if (this.isDiscover) {
this.ui.searchBar.hide();
if (FullMovieCollection.length > 0) {
this._discoverRecos();
} else {
this.listenTo(FullMovieCollection, "sync", this._discover);
}
if (this.collection.length == 0) {
this._discoverRecos();
/*if (this.collection.length == 0) {
this.searchResult.show(new LoadingView());
}
}*/
}
},
@@ -192,8 +189,13 @@ module.exports = Marionette.Layout.extend({
_showResults : function() {
if (!this.isClosed) {
if (this.collection.length === 0) {
this.ui.searchBar.show();
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
if (this.isDiscover) {
this.searchResult.show(new DiscoverEmptyView());
} else {
this.ui.searchBar.show();
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
}
} else {
this.searchResult.show(this.resultCollectionView);
if (!this.showingAll) {
@@ -224,11 +226,10 @@ module.exports = Marionette.Layout.extend({
if (this.collection.action === action) {
return
}
this.collection.reset();
this.searchResult.show(new LoadingView());
this.collection.action = action;
this.collection.fetch({
data : { action : action }
});
this.currentSearchPromise = this.collection.fetch();
},
_discoverRecos : function() {

View File

@@ -0,0 +1,5 @@
var Marionette = require('marionette');
module.exports = Marionette.CompositeView.extend({
template : 'AddMovies/DiscoverEmptyViewTemplate'
});

View File

@@ -0,0 +1,6 @@
<div class="text-center col-md-12">
<h3>
No movies left to discover. Come back at another time :)
</h3>
</div>

View File

@@ -7,6 +7,7 @@ var Profiles = require('../Profile/ProfileCollection');
var RootFolders = require('./RootFolders/RootFolderCollection');
var RootFolderLayout = require('./RootFolders/RootFolderLayout');
var FullMovieCollection = require('../Movies/FullMovieCollection');
var ImportExclusionModel = require("../Settings/NetImport/ImportExclusionModel");
var Config = require('../Config');
var Messenger = require('../Shared/Messenger');
var AsValidatedView = require('../Mixins/AsValidatedView');
@@ -33,6 +34,7 @@ var view = Marionette.ItemView.extend({
events : {
'click .x-add' : '_addWithoutSearch',
'click .x-add-search' : '_addAndSearch',
"click .x-ignore" : "_ignoreMovie",
'change .x-profile' : '_profileChanged',
'change .x-root-folder' : '_rootFolderChanged',
'change .x-season-folder' : '_seasonFolderChanged',
@@ -239,6 +241,13 @@ var view = Marionette.ItemView.extend({
});
},
_ignoreMovie : function() {
var exclusion = new ImportExclusionModel({tmdbId : this.model.get("tmdbId"),
movieTitle : this.model.get("title"), movieYear : this.model.get("year")});
exclusion.save();
this.remove();
},
_rootFoldersUpdated : function() {
this._configureTemplateHelpers();
this.render();

View File

@@ -106,6 +106,10 @@
<button class="btn btn-success add x-add-search" title="Add and Search for movie">
<i class="icon-sonarr-search"></i>
</button>
<button class="btn btn-warning ignore x-ignore" title="Ignore this movie, so it does not show up anymore">
<i class="icon-sonarr-ignore"></i>
</button>
</div>
</div>
{{else}}

View File

@@ -304,6 +304,10 @@
.fa-icon-color(@brand-danger);
}
.icon-sonarr-ignore {
.fa-icon-content(@fa-var-eye-slash);
}
.icon-sonarr-deleted {
.fa-icon-content(@fa-var-trash);
}

View File

@@ -0,0 +1,24 @@
var vent = require('vent');
var Backgrid = require('backgrid');
module.exports = Backgrid.Cell.extend({
className : 'delete-episode-file-cell',
events : {
'click' : '_onClick'
},
render : function() {
this.$el.empty();
this.$el.html('<i class="icon-sonarr-delete" title="Delete exclusion."></i>');
return this;
},
_onClick : function() {
var self = this;
this.model.destroy();
}
});

View File

@@ -0,0 +1,18 @@
var NzbDroneCell = require('../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({
className : 'exclusion-title-cell',
render : function() {
this.$el.empty();
var title = this.model.get("movieTitle");
var year = this.model.get("movieYear");
var str = title;
if (year > 1800) {
str += " ("+year+")";
}
this.$el.html(str);
return this;
}
});

View File

@@ -0,0 +1,7 @@
var Backbone = require('backbone');
var _ = require('underscore');
module.exports = Backbone.Model.extend({
urlRoot : window.NzbDrone.ApiRoot + '/exclusions',
});

View File

@@ -0,0 +1,9 @@
var Backbone = require('backbone');
var NetImportModel = require('./ImportExclusionModel');
var ImportExclusionsCollection = Backbone.Collection.extend({
model : NetImportModel,
url : window.NzbDrone.ApiRoot + '/exclusions',
});
module.exports = new ImportExclusionsCollection();

View File

@@ -3,6 +3,14 @@ var NetImportCollection = require('./NetImportCollection');
var CollectionView = require('./NetImportCollectionView');
var OptionsView = require('./Options/NetImportOptionsView');
var RootFolderCollection = require('../../AddMovies/RootFolders/RootFolderCollection');
var ImportExclusionsCollection = require('./ImportExclusionsCollection');
var SelectAllCell = require('../../Cells/SelectAllCell');
var DeleteExclusionCell = require('./DeleteExclusionCell');
var ExclusionTitleCell = require("./ExclusionTitleCell");
var _ = require('underscore');
var vent = require('vent');
var Backgrid = require('backgrid');
var $ = require('jquery');
module.exports = Marionette.Layout.extend({
template : 'Settings/NetImport/NetImportLayoutTemplate',
@@ -10,18 +18,58 @@ module.exports = Marionette.Layout.extend({
regions : {
lists : '#x-lists-region',
listOption : '#x-list-options-region',
importExclusions : "#exclusions"
},
columns: [{
name: '',
cell: SelectAllCell,
headerCell: 'select-all',
sortable: false
}, {
name: 'tmdbId',
label: 'TMDBID',
cell: Backgrid.StringCell,
sortable: false,
}, {
name: 'movieTitle',
label: 'Title',
cell: ExclusionTitleCell,
cellValue: 'this',
}, {
name: 'this',
label: '',
cell: DeleteExclusionCell,
sortable: false,
}],
initialize : function() {
this.indexersCollection = new NetImportCollection();
this.indexersCollection.fetch();
RootFolderCollection.fetch().done(function() {
RootFolderCollection.synced = true;
});
ImportExclusionsCollection.fetch().done(function() {
ImportExclusionsCollection.synced = true;
});
},
onShow : function() {
this.listenTo(ImportExclusionsCollection, "sync", this._showExclusions);
if (ImportExclusionsCollection.synced === true) {
this._showExclusions();
}
this.lists.show(new CollectionView({ collection : this.indexersCollection }));
this.listOption.show(new OptionsView({ model : this.model }));
},
_showExclusions : function() {
this.exclusionGrid = new Backgrid.Grid({
collection: ImportExclusionsCollection,
columns: this.columns,
className: 'table table-hover'
});
this.importExclusions.show(this.exclusionGrid);
}
});

View File

@@ -1,4 +1,9 @@
<div id="x-lists-region"></div>
<div class="form-horizontal">
<div id="x-list-options-region"></div>
<fieldset>
<legend>Import Exclusions</legend>
<div id="exclusions">
</div>
</fieldset>
</div>

View File

@@ -1,6 +1,11 @@
var Marionette = require('marionette');
var AsModelBoundView = require('../../../Mixins/AsModelBoundView');
var AsValidatedView = require('../../../Mixins/AsValidatedView');
var ImportExclusionsCollection = require('./../ImportExclusionsCollection');
var SelectAllCell = require('../../../Cells/SelectAllCell');
var _ = require('underscore');
var vent = require('vent');
var Backgrid = require('backgrid');
var $ = require('jquery');
require('../../../Mixins/TagInput');
require('bootstrap');
@@ -22,10 +27,10 @@ var view = Marionette.ItemView.extend({
'click .x-revoke-trakt-tokens' : '_revokeTraktTokens'
},
initialize : function() {
initialize : function() {
},
onShow : function() {
var params = new URLSearchParams(window.location.search);
var oauth = params.get('access');
@@ -39,78 +44,25 @@ var view = Marionette.ItemView.extend({
//Config.setValue("traktRefreshToken", refresh);
var tokenExpiry = Math.floor(Date.now() / 1000) + 4838400;
this.ui.tokenExpiry.val(tokenExpiry).trigger('change'); // this means the token will expire in 8 weeks (4838400 seconds)
//Config.setValue("traktTokenExpiry",tokenExpiry);
//Config.setValue("traktTokenExpiry",tokenExpiry);
//this.model.isSaved = false;
//window.alert("Trakt Authentication Complete - Click Save to make the change take effect");
}
if (this.ui.authToken.val() && this.ui.refreshToken.val()){
this.ui.resetTokensButton.hide();
this.ui.revokeTokensButton.show();
this.ui.resetTokensButton.hide();
this.ui.revokeTokensButton.show();
} else {
this.ui.resetTokensButton.show();
this.ui.revokeTokensButton.hide();
this.ui.resetTokensButton.show();
this.ui.revokeTokensButton.hide();
}
},
onRender : function() {
this.ui.importExclusions.tagsinput({
trimValue : true,
tagClass : 'label label-danger',
/*itemText : function(item) {
var uri;
var text;
if (item.startsWith('tt')) {
uri = window.NzbDrone.ApiRoot + '/movies/lookup/imdb?imdbId='+item;
}
else {
uri = window.NzbDrone.ApiRoot + '/movies/lookup/tmdb?tmdbId='+item;
}
var promise = $.ajax({
url : uri,
type : 'GET',
async : false,
});
promise.success(function(response) {
text=response['title']+' ('+response['year']+')';
});
promise.error(function(request, status, error) {
text=item;
});
return text;
}*/
});
this.ui.importExclusions.on('beforeItemAdd', function(event) {
var uri;
if (event.item.startsWith('tt')) {
uri = window.NzbDrone.ApiRoot + '/movies/lookup/imdb?imdbId='+event.item;
}
else {
uri = window.NzbDrone.ApiRoot + '/movies/lookup/tmdb?tmdbId='+event.item;
}
var promise = $.ajax({
url : uri,
type : 'GET',
async : false,
});
promise.success(function(response) {
event.cancel=false;
//var newText = response['tmdbId']+'-';
//if (event.item.startsWith('tt')) {
// newText = newText+'['+event.item+']';
//}
event.item = response.titleSlug;//+' ('+response['year']+')-'+response['tmdbId'];
});
promise.error(function(request, status, error) {
event.cancel = true;
window.alert(event.item+' is not a valid! Must be valid tt#### IMDB ID or #### TMDB ID');
});
return event;
});
},
},
ui : {
resetTraktTokens : '.x-reset-trakt-tokens',
@@ -118,7 +70,7 @@ var view = Marionette.ItemView.extend({
refreshToken : '.x-trakt-refresh-token',
resetTokensButton : '.x-reset-trakt-tokens',
revokeTokensButton : '.x-revoke-trakt-tokens',
tokenExpiry : '.x-trakt-token-expiry',
tokenExpiry : '.x-trakt-token-expiry',
importExclusions : '.x-import-exclusions'
},
@@ -131,15 +83,16 @@ var view = Marionette.ItemView.extend({
_revokeTraktTokens : function() {
if (window.confirm("Log out of trakt.tv?")){
//TODO: need to implement this: http://docs.trakt.apiary.io/#reference/authentication-oauth/revoke-token/revoke-an-access_token
//TODO: need to implement this: http://docs.trakt.apiary.io/#reference/authentication-oauth/revoke-token/revoke-an-access_token
this.ui.authToken.val('').trigger('change');
this.ui.refreshToken.val('').trigger('change');
this.ui.tokenExpiry.val(0).trigger('change');
this.ui.refreshToken.val('').trigger('change');
this.ui.tokenExpiry.val(0).trigger('change');
this.ui.resetTokensButton.show();
this.ui.revokeTokensButton.hide();
window.alert("Logged out of Trakt.tv - Click Save to make the change take effect");
this.ui.revokeTokensButton.hide();
window.alert("Logged out of Trakt.tv - Click Save to make the change take effect");
}
}
},
});

View File

@@ -30,17 +30,17 @@
</select>
</div>
</div>
<div class="form-group">
<!--<div class="form-group">
<label class="col-sm-3 control-label">Import Exclusions</label>
<div class="col-sm-1 col-sm-push-4 help-inline">
<i class="icon-sonarr-form-warning" title="Movies in this field will not be imported even if they exist on your lists."/>
<i class="icon-sonarr-form-info" title="Comma separated imdbid or tmdbid: tt0120915,216138,tt0121765"/>
</div>
<div class="col-sm-4 col-sm-pull-1">
<input type="text" name="importExclusions" class="form-control x-import-exclusions"/>
</div>
</div>
</div>-->
<legend>Trakt Authentication</legend>
<div class="form-group">
<label class="col-sm-1 control-label">Auth Token</label>
@@ -58,5 +58,6 @@
<button class="btn btn-danger btn-icon-only x-reset-trakt-tokens" title="Reset Trakt Tokens"><i class="icon-sonarr-refresh"></i></button>
<button class="btn btn-danger btn-icon-only x-revoke-trakt-tokens" title="Revoke Trakt Tokens"><i class="icon-sonarr-logout"></i></button>
</div >
</div>
</div>
</fieldset>

View File

@@ -14,6 +14,7 @@ var IndexerCollection = require('./Indexers/IndexerCollection');
var IndexerSettingsModel = require('./Indexers/IndexerSettingsModel');
var NetImportSettingsModel = require("./NetImport/NetImportSettingsModel");
var NetImportCollection = require('./NetImport/NetImportCollection');
var ImportExclusionsCollection = require('./NetImport/ImportExclusionsCollection');
var NetImportLayout = require('./NetImport/NetImportLayout');
var DownloadClientLayout = require('./DownloadClient/DownloadClientLayout');
var DownloadClientSettingsModel = require('./DownloadClient/DownloadClientSettingsModel');