Movies should now show on the main page. However, a lot has to be done to the detail controller before it is really going to work.
parent
5ebfac6cc8
commit
b7c70d750a
@ -0,0 +1,47 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||||
|
var reqres = require('../../reqres');
|
||||||
|
var SeriesCollection = require('../SeriesCollection');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'episode-number-cell',
|
||||||
|
template : 'Series/Details/EpisodeNumberCellTemplate',
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
this.$el.html(this.model.get('episodeNumber'));
|
||||||
|
|
||||||
|
var series = SeriesCollection.get(this.model.get('seriesId'));
|
||||||
|
|
||||||
|
if (series.get('seriesType') === 'anime' && this.model.has('absoluteEpisodeNumber')) {
|
||||||
|
this.$el.html('{0} ({1})'.format(this.model.get('episodeNumber'), this.model.get('absoluteEpisodeNumber')));
|
||||||
|
}
|
||||||
|
|
||||||
|
var alternateTitles = [];
|
||||||
|
|
||||||
|
if (reqres.hasHandler(reqres.Requests.GetAlternateNameBySeasonNumber)) {
|
||||||
|
alternateTitles = reqres.request(reqres.Requests.GetAlternateNameBySeasonNumber, this.model.get('seriesId'), this.model.get('seasonNumber'), this.model.get('sceneSeasonNumber'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.model.get('sceneSeasonNumber') > 0 || this.model.get('sceneEpisodeNumber') > 0 || this.model.has('sceneAbsoluteEpisodeNumber') || alternateTitles.length > 0) {
|
||||||
|
this.templateFunction = Marionette.TemplateCache.get(this.template);
|
||||||
|
|
||||||
|
var json = this.model.toJSON();
|
||||||
|
json.alternateTitles = alternateTitles;
|
||||||
|
|
||||||
|
var html = this.templateFunction(json);
|
||||||
|
|
||||||
|
this.$el.popover({
|
||||||
|
content : html,
|
||||||
|
html : true,
|
||||||
|
trigger : 'hover',
|
||||||
|
title : 'Scene Information',
|
||||||
|
placement : 'right',
|
||||||
|
container : this.$el
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delegateEvents();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,39 @@
|
|||||||
|
<div class="scene-info">
|
||||||
|
{{#if sceneSeasonNumber}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="key">Season</div>
|
||||||
|
<div class="value">{{sceneSeasonNumber}}</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if sceneEpisodeNumber}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="key">Episode</div>
|
||||||
|
<div class="value">{{sceneEpisodeNumber}}</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if sceneAbsoluteEpisodeNumber}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="key">Absolute</div>
|
||||||
|
<div class="value">{{sceneAbsoluteEpisodeNumber}}</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if alternateTitles}}
|
||||||
|
<div class="row">
|
||||||
|
{{#if_gt alternateTitles.length compare="1"}}
|
||||||
|
<div class="key">Titles</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="key">Title</div>
|
||||||
|
{{/if_gt}}
|
||||||
|
<div class="value">
|
||||||
|
<ul>
|
||||||
|
{{#each alternateTitles}}
|
||||||
|
<li>{{title}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
@ -0,0 +1,21 @@
|
|||||||
|
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||||
|
var SeriesCollection = require('../SeriesCollection');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'episode-warning-cell',
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
|
||||||
|
if (this.model.get('unverifiedSceneNumbering')) {
|
||||||
|
this.$el.html('<i class="icon-sonarr-form-warning" title="Scene number hasn\'t been verified yet."></i>');
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (SeriesCollection.get(this.model.get('seriesId')).get('seriesType') === 'anime' && this.model.get('seasonNumber') > 0 && !this.model.has('absoluteEpisodeNumber')) {
|
||||||
|
this.$el.html('<i class="icon-sonarr-form-warning" title="Episode does not have an absolute episode number"></i>');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delegateEvents();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.ItemView.extend({
|
||||||
|
template : 'Series/Details/InfoViewTemplate',
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
this.episodeFileCollection = options.episodeFileCollection;
|
||||||
|
|
||||||
|
this.listenTo(this.model, 'change', this.render);
|
||||||
|
this.listenTo(this.episodeFileCollection, 'sync', this.render);
|
||||||
|
},
|
||||||
|
|
||||||
|
templateHelpers : function() {
|
||||||
|
return {
|
||||||
|
fileCount : this.episodeFileCollection.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,73 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
{{profile profileId}}
|
||||||
|
|
||||||
|
{{#if network}}
|
||||||
|
<span class="label label-info">{{network}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span class="label label-info">{{runtime}} minutes</span>
|
||||||
|
<span class="label label-info">{{path}}</span>
|
||||||
|
|
||||||
|
{{#if ratings}}
|
||||||
|
<span class="label label-info" title="{{ratings.votes}} vote{{#if_gt ratings.votes compare="1"}}s{{/if_gt}}">{{ratings.value}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span class="label label-info">{{Bytes sizeOnDisk}}</span>
|
||||||
|
|
||||||
|
{{#if_eq fileCount compare="1"}}
|
||||||
|
<span class="label label-info"> 1 file</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="label label-info"> {{fileCount}} files</span>
|
||||||
|
{{/if_eq}}
|
||||||
|
|
||||||
|
{{#if_eq status compare="continuing"}}
|
||||||
|
<span class="label label-info">Continuing</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="label label-default">Ended</span>
|
||||||
|
{{/if_eq}}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<span class="series-info-links">
|
||||||
|
<!--<a href="{{traktUrl}}" class="label label-info">Trakt</a>
|
||||||
|
|
||||||
|
<a href="{{tvdbUrl}}" class="label label-info">The TVDB</a>-->
|
||||||
|
|
||||||
|
{{#if imdbId}}
|
||||||
|
<a href="{{imdbUrl}}" class="label label-info">IMDB</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if tvRageId}}
|
||||||
|
<a href="{{tvRageUrl}}" class="label label-info">TV Rage</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if tvMazeId}}
|
||||||
|
<a href="{{tvMazeUrl}}" class="label label-info">TV Maze</a>
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if alternateTitles}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{{#each alternateTitles}}
|
||||||
|
{{#if_eq seasonNumber compare="-1"}}
|
||||||
|
<span class="label label-default">{{title}}</span>
|
||||||
|
{{/if_eq}}
|
||||||
|
|
||||||
|
{{#if_eq sceneSeasonNumber compare="-1"}}
|
||||||
|
<span class="label label-default">{{title}}</span>
|
||||||
|
{{/if_eq}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if tags}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{{tagDisplay tags}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
@ -0,0 +1,264 @@
|
|||||||
|
var $ = require('jquery');
|
||||||
|
var _ = require('underscore');
|
||||||
|
var vent = require('vent');
|
||||||
|
var reqres = require('../../reqres');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var Backbone = require('backbone');
|
||||||
|
var MoviesCollection = require('../MoviesCollection');
|
||||||
|
var InfoView = require('./InfoView');
|
||||||
|
var CommandController = require('../../Commands/CommandController');
|
||||||
|
var LoadingView = require('../../Shared/LoadingView');
|
||||||
|
var EpisodeFileEditorLayout = require('../../EpisodeFile/Editor/EpisodeFileEditorLayout');
|
||||||
|
require('backstrech');
|
||||||
|
require('../../Mixins/backbone.signalr.mixin');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
itemViewContainer : '.x-movie-seasons',
|
||||||
|
template : 'Movies/Details/MoviesDetailsTemplate',
|
||||||
|
|
||||||
|
regions : {
|
||||||
|
seasons : '#seasons',
|
||||||
|
info : '#info'
|
||||||
|
},
|
||||||
|
|
||||||
|
ui : {
|
||||||
|
header : '.x-header',
|
||||||
|
monitored : '.x-monitored',
|
||||||
|
edit : '.x-edit',
|
||||||
|
refresh : '.x-refresh',
|
||||||
|
rename : '.x-rename',
|
||||||
|
search : '.x-search',
|
||||||
|
poster : '.x-movie-poster',
|
||||||
|
manualSearch : '.x-manual-search'
|
||||||
|
},
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click .x-episode-file-editor' : '_openEpisodeFileEditor',
|
||||||
|
'click .x-monitored' : '_toggleMonitored',
|
||||||
|
'click .x-edit' : '_editMovies',
|
||||||
|
'click .x-refresh' : '_refreshMovies',
|
||||||
|
'click .x-rename' : '_renameMovies',
|
||||||
|
'click .x-search' : '_moviesSearch',
|
||||||
|
'click .x-manual-search' : '_manualSearchM'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize : function() {
|
||||||
|
this.moviesCollection = MoviesCollection.clone();
|
||||||
|
this.moviesCollection.shadowCollection.bindSignalR();
|
||||||
|
|
||||||
|
this.listenTo(this.model, 'change:monitored', this._setMonitoredState);
|
||||||
|
this.listenTo(this.model, 'remove', this._moviesRemoved);
|
||||||
|
this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
|
||||||
|
|
||||||
|
this.listenTo(this.model, 'change', function(model, options) {
|
||||||
|
if (options && options.changeSource === 'signalr') {
|
||||||
|
this._refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.listenTo(this.model, 'change:images', this._updateImages);
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow : function() {
|
||||||
|
this._showBackdrop();
|
||||||
|
this._showSeasons();
|
||||||
|
this._setMonitoredState();
|
||||||
|
this._showInfo();
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
CommandController.bindToCommand({
|
||||||
|
element : this.ui.refresh,
|
||||||
|
command : {
|
||||||
|
name : 'refreshMovies'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
CommandController.bindToCommand({
|
||||||
|
element : this.ui.search,
|
||||||
|
command : {
|
||||||
|
name : 'moviesSearch'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CommandController.bindToCommand({
|
||||||
|
element : this.ui.rename,
|
||||||
|
command : {
|
||||||
|
name : 'renameFiles',
|
||||||
|
movieId : this.model.id,
|
||||||
|
seasonNumber : -1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose : function() {
|
||||||
|
if (this._backstrech) {
|
||||||
|
this._backstrech.destroy();
|
||||||
|
delete this._backstrech;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('body').removeClass('backdrop');
|
||||||
|
reqres.removeHandler(reqres.Requests.GetEpisodeFileById);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getImage : function(type) {
|
||||||
|
var image = _.where(this.model.get('images'), { coverType : type });
|
||||||
|
|
||||||
|
if (image && image[0]) {
|
||||||
|
return image[0].url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
_toggleMonitored : function() {
|
||||||
|
var savePromise = this.model.save('monitored', !this.model.get('monitored'), { wait : true });
|
||||||
|
|
||||||
|
this.ui.monitored.spinForPromise(savePromise);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setMonitoredState : function() {
|
||||||
|
var monitored = this.model.get('monitored');
|
||||||
|
|
||||||
|
this.ui.monitored.removeAttr('data-idle-icon');
|
||||||
|
this.ui.monitored.removeClass('fa-spin icon-sonarr-spinner');
|
||||||
|
|
||||||
|
if (monitored) {
|
||||||
|
this.ui.monitored.addClass('icon-sonarr-monitored');
|
||||||
|
this.ui.monitored.removeClass('icon-sonarr-unmonitored');
|
||||||
|
this.$el.removeClass('movie-not-monitored');
|
||||||
|
} else {
|
||||||
|
this.ui.monitored.addClass('icon-sonarr-unmonitored');
|
||||||
|
this.ui.monitored.removeClass('icon-sonarr-monitored');
|
||||||
|
this.$el.addClass('movie-not-monitored');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_editMovies : function() {
|
||||||
|
vent.trigger(vent.Commands.EditMoviesCommand, { movie : this.model });
|
||||||
|
},
|
||||||
|
|
||||||
|
_refreshMovies : function() {
|
||||||
|
CommandController.Execute('refreshMovies', {
|
||||||
|
name : 'refreshMovies',
|
||||||
|
movieId : this.model.id
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_moviesRemoved : function() {
|
||||||
|
Backbone.history.navigate('/', { trigger : true });
|
||||||
|
},
|
||||||
|
|
||||||
|
_renameMovies : function() {
|
||||||
|
vent.trigger(vent.Commands.ShowRenamePreview, { movie : this.model });
|
||||||
|
},
|
||||||
|
|
||||||
|
_moviesSearch : function() {
|
||||||
|
CommandController.Execute('moviesSearch', {
|
||||||
|
name : 'moviesSearch',
|
||||||
|
movieId : this.model.id
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showSeasons : function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
reqres.setHandler(reqres.Requests.GetEpisodeFileById, function(episodeFileId) {
|
||||||
|
return self.episodeFileCollection.get(episodeFileId);
|
||||||
|
});
|
||||||
|
|
||||||
|
reqres.setHandler(reqres.Requests.GetAlternateNameBySeasonNumber, function(moviesId, seasonNumber, sceneSeasonNumber) {
|
||||||
|
if (self.model.get('id') !== moviesId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sceneSeasonNumber === undefined) {
|
||||||
|
sceneSeasonNumber = seasonNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.where(self.model.get('alternateTitles'),
|
||||||
|
function(alt) {
|
||||||
|
return alt.sceneSeasonNumber === sceneSeasonNumber || alt.seasonNumber === seasonNumber;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$.when(this.episodeCollection.fetch(), this.episodeFileCollection.fetch()).done(function() {
|
||||||
|
var seasonCollectionView = new SeasonCollectionView({
|
||||||
|
collection : self.seasonCollection,
|
||||||
|
episodeCollection : self.episodeCollection,
|
||||||
|
movies : self.model
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!self.isClosed) {
|
||||||
|
self.seasons.show(seasonCollectionView);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showInfo : function() {
|
||||||
|
this.info.show(new InfoView({
|
||||||
|
model : this.model,
|
||||||
|
episodeFileCollection : this.episodeFileCollection
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_commandComplete : function(options) {
|
||||||
|
if (options.command.get('name') === 'renamefiles') {
|
||||||
|
if (options.command.get('moviesId') === this.model.get('id')) {
|
||||||
|
this._refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_refresh : function() {
|
||||||
|
this.seasonCollection.add(this.model.get('seasons'), { merge : true });
|
||||||
|
this.episodeCollection.fetch();
|
||||||
|
this.episodeFileCollection.fetch();
|
||||||
|
|
||||||
|
this._setMonitoredState();
|
||||||
|
this._showInfo();
|
||||||
|
},
|
||||||
|
|
||||||
|
_openEpisodeFileEditor : function() {
|
||||||
|
var view = new EpisodeFileEditorLayout({
|
||||||
|
movies : this.model,
|
||||||
|
episodeCollection : this.episodeCollection
|
||||||
|
});
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.OpenModalCommand, view);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateImages : function () {
|
||||||
|
var poster = this._getImage('poster');
|
||||||
|
|
||||||
|
if (poster) {
|
||||||
|
this.ui.poster.attr('src', poster);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._showBackdrop();
|
||||||
|
},
|
||||||
|
|
||||||
|
_showBackdrop : function () {
|
||||||
|
$('body').addClass('backdrop');
|
||||||
|
var fanArt = this._getImage('fanart');
|
||||||
|
|
||||||
|
if (fanArt) {
|
||||||
|
this._backstrech = $.backstretch(fanArt);
|
||||||
|
} else {
|
||||||
|
$('body').removeClass('backdrop');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_manualSearchM : function() {
|
||||||
|
console.warn("Manual Search started");
|
||||||
|
console.warn(this.model.get("moviesId"));
|
||||||
|
console.warn(this.model)
|
||||||
|
console.warn(this.episodeCollection);
|
||||||
|
vent.trigger(vent.Commands.ShowEpisodeDetails, {
|
||||||
|
episode : this.episodeCollection.models[0],
|
||||||
|
hideMoviesLink : true,
|
||||||
|
openingTab : 'search'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,38 @@
|
|||||||
|
<div class="row movie-page-header">
|
||||||
|
<div class="visible-lg col-lg-2 poster">
|
||||||
|
{{poster}}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 col-lg-10">
|
||||||
|
<div>
|
||||||
|
<h1 class="header-text">
|
||||||
|
<i class="x-monitored" title="Toggle monitored state for movie"/>
|
||||||
|
{{title}}
|
||||||
|
<div class="movie-actions pull-right">
|
||||||
|
<div class="x-episode-file-editor">
|
||||||
|
<i class="icon-sonarr-episode-file" title="Modify episode files for movie"/>
|
||||||
|
</div>
|
||||||
|
<div class="x-refresh">
|
||||||
|
<i class="icon-sonarr-refresh icon-can-spin" title="Update movie info and scan disk"/>
|
||||||
|
</div>
|
||||||
|
<div class="x-rename">
|
||||||
|
<i class="icon-sonarr-rename" title="Preview rename for all episodes"/>
|
||||||
|
</div>
|
||||||
|
<div class="x-search">
|
||||||
|
<i class="icon-sonarr-search" title="Search for movie"/>
|
||||||
|
</div>
|
||||||
|
<div class="x-manual-search">
|
||||||
|
<i class="icon-sonarr-search-manual" title="Manual Search"/>
|
||||||
|
</div>
|
||||||
|
<div class="x-edit">
|
||||||
|
<i class="icon-sonarr-edit" title="Edit movie"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="movie-detail-overview">
|
||||||
|
{{overview}}
|
||||||
|
</div>
|
||||||
|
<div id="info" class="movie-info"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="seasons"></div>
|
@ -0,0 +1,44 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var SeasonLayout = require('./SeasonLayout');
|
||||||
|
var AsSortedCollectionView = require('../../Mixins/AsSortedCollectionView');
|
||||||
|
|
||||||
|
var view = Marionette.CollectionView.extend({
|
||||||
|
|
||||||
|
itemView : SeasonLayout,
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
if (!options.episodeCollection) {
|
||||||
|
throw 'episodeCollection is needed';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.episodeCollection = options.episodeCollection;
|
||||||
|
this.series = options.series;
|
||||||
|
},
|
||||||
|
|
||||||
|
itemViewOptions : function() {
|
||||||
|
return {
|
||||||
|
episodeCollection : this.episodeCollection,
|
||||||
|
series : this.series
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onEpisodeGrabbed : function(message) {
|
||||||
|
if (message.episode.series.id !== this.episodeCollection.seriesId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
_.each(message.episode.episodes, function(episode) {
|
||||||
|
var ep = self.episodeCollection.get(episode.id);
|
||||||
|
ep.set('downloading', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AsSortedCollectionView.call(view);
|
||||||
|
|
||||||
|
module.exports = view;
|
@ -0,0 +1,301 @@
|
|||||||
|
var vent = require('vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var Backgrid = require('backgrid');
|
||||||
|
var ToggleCell = require('../../Cells/EpisodeMonitoredCell');
|
||||||
|
var EpisodeTitleCell = require('../../Cells/EpisodeTitleCell');
|
||||||
|
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||||
|
var EpisodeStatusCell = require('../../Cells/EpisodeStatusCell');
|
||||||
|
var EpisodeActionsCell = require('../../Cells/EpisodeActionsCell');
|
||||||
|
var EpisodeNumberCell = require('./EpisodeNumberCell');
|
||||||
|
var EpisodeWarningCell = require('./EpisodeWarningCell');
|
||||||
|
var CommandController = require('../../Commands/CommandController');
|
||||||
|
var EpisodeFileEditorLayout = require('../../EpisodeFile/Editor/EpisodeFileEditorLayout');
|
||||||
|
var moment = require('moment');
|
||||||
|
var _ = require('underscore');
|
||||||
|
var Messenger = require('../../Shared/Messenger');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
template : 'Series/Details/SeasonLayoutTemplate',
|
||||||
|
|
||||||
|
ui : {
|
||||||
|
seasonSearch : '.x-season-search',
|
||||||
|
seasonMonitored : '.x-season-monitored',
|
||||||
|
seasonRename : '.x-season-rename'
|
||||||
|
},
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click .x-season-episode-file-editor' : '_openEpisodeFileEditor',
|
||||||
|
'click .x-season-monitored' : '_seasonMonitored',
|
||||||
|
'click .x-season-search' : '_seasonSearch',
|
||||||
|
'click .x-season-rename' : '_seasonRename',
|
||||||
|
'click .x-show-hide-episodes' : '_showHideEpisodes',
|
||||||
|
'dblclick .series-season h2' : '_showHideEpisodes'
|
||||||
|
},
|
||||||
|
|
||||||
|
regions : {
|
||||||
|
episodeGrid : '.x-episode-grid'
|
||||||
|
},
|
||||||
|
|
||||||
|
columns : [
|
||||||
|
{
|
||||||
|
name : 'monitored',
|
||||||
|
label : '',
|
||||||
|
cell : ToggleCell,
|
||||||
|
trueClass : 'icon-sonarr-monitored',
|
||||||
|
falseClass : 'icon-sonarr-unmonitored',
|
||||||
|
tooltip : 'Toggle monitored status',
|
||||||
|
sortable : false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'episodeNumber',
|
||||||
|
label : '#',
|
||||||
|
cell : EpisodeNumberCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'this',
|
||||||
|
label : '',
|
||||||
|
cell : EpisodeWarningCell,
|
||||||
|
sortable : false,
|
||||||
|
className : 'episode-warning-cell'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'this',
|
||||||
|
label : 'Title',
|
||||||
|
hideSeriesLink : true,
|
||||||
|
cell : EpisodeTitleCell,
|
||||||
|
sortable : false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'airDateUtc',
|
||||||
|
label : 'Air Date',
|
||||||
|
cell : RelativeDateCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'status',
|
||||||
|
label : 'Status',
|
||||||
|
cell : EpisodeStatusCell,
|
||||||
|
sortable : false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'this',
|
||||||
|
label : '',
|
||||||
|
cell : EpisodeActionsCell,
|
||||||
|
sortable : false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
templateHelpers : function() {
|
||||||
|
var episodeCount = this.episodeCollection.filter(function(episode) {
|
||||||
|
return episode.get('hasFile') || episode.get('monitored') && moment(episode.get('airDateUtc')).isBefore(moment());
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
var episodeFileCount = this.episodeCollection.where({ hasFile : true }).length;
|
||||||
|
var percentOfEpisodes = 100;
|
||||||
|
|
||||||
|
if (episodeCount > 0) {
|
||||||
|
percentOfEpisodes = episodeFileCount / episodeCount * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
showingEpisodes : this.showingEpisodes,
|
||||||
|
episodeCount : episodeCount,
|
||||||
|
episodeFileCount : episodeFileCount,
|
||||||
|
percentOfEpisodes : percentOfEpisodes
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
if (!options.episodeCollection) {
|
||||||
|
throw 'episodeCollection is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.series = options.series;
|
||||||
|
this.fullEpisodeCollection = options.episodeCollection;
|
||||||
|
this.episodeCollection = this.fullEpisodeCollection.bySeason(this.model.get('seasonNumber'));
|
||||||
|
this._updateEpisodeCollection();
|
||||||
|
|
||||||
|
this.showingEpisodes = this._shouldShowEpisodes();
|
||||||
|
|
||||||
|
this.listenTo(this.model, 'sync', this._afterSeasonMonitored);
|
||||||
|
this.listenTo(this.episodeCollection, 'sync', this.render);
|
||||||
|
|
||||||
|
this.listenTo(this.fullEpisodeCollection, 'sync', this._refreshEpisodes);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
if (this.showingEpisodes) {
|
||||||
|
this._showEpisodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setSeasonMonitoredState();
|
||||||
|
|
||||||
|
CommandController.bindToCommand({
|
||||||
|
element : this.ui.seasonSearch,
|
||||||
|
command : {
|
||||||
|
name : 'seasonSearch',
|
||||||
|
seriesId : this.series.id,
|
||||||
|
seasonNumber : this.model.get('seasonNumber')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CommandController.bindToCommand({
|
||||||
|
element : this.ui.seasonRename,
|
||||||
|
command : {
|
||||||
|
name : 'renameFiles',
|
||||||
|
seriesId : this.series.id,
|
||||||
|
seasonNumber : this.model.get('seasonNumber')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_seasonSearch : function() {
|
||||||
|
CommandController.Execute('seasonSearch', {
|
||||||
|
name : 'seasonSearch',
|
||||||
|
seriesId : this.series.id,
|
||||||
|
seasonNumber : this.model.get('seasonNumber')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_seasonRename : function() {
|
||||||
|
vent.trigger(vent.Commands.ShowRenamePreview, {
|
||||||
|
series : this.series,
|
||||||
|
seasonNumber : this.model.get('seasonNumber')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_seasonMonitored : function() {
|
||||||
|
if (!this.series.get('monitored')) {
|
||||||
|
|
||||||
|
Messenger.show({
|
||||||
|
message : 'Unable to change monitored state when series is not monitored',
|
||||||
|
type : 'error'
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = 'monitored';
|
||||||
|
this.model.set(name, !this.model.get(name));
|
||||||
|
this.series.setSeasonMonitored(this.model.get('seasonNumber'));
|
||||||
|
|
||||||
|
var savePromise = this.series.save().always(this._afterSeasonMonitored.bind(this));
|
||||||
|
|
||||||
|
this.ui.seasonMonitored.spinForPromise(savePromise);
|
||||||
|
},
|
||||||
|
|
||||||
|
_afterSeasonMonitored : function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
_.each(this.episodeCollection.models, function(episode) {
|
||||||
|
episode.set({ monitored : self.model.get('monitored') });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setSeasonMonitoredState : function() {
|
||||||
|
this.ui.seasonMonitored.removeClass('icon-sonarr-spinner fa-spin');
|
||||||
|
|
||||||
|
if (this.model.get('monitored')) {
|
||||||
|
this.ui.seasonMonitored.addClass('icon-sonarr-monitored');
|
||||||
|
this.ui.seasonMonitored.removeClass('icon-sonarr-unmonitored');
|
||||||
|
} else {
|
||||||
|
this.ui.seasonMonitored.addClass('icon-sonarr-unmonitored');
|
||||||
|
this.ui.seasonMonitored.removeClass('icon-sonarr-monitored');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_showEpisodes : function() {
|
||||||
|
this.episodeGrid.show(new Backgrid.Grid({
|
||||||
|
columns : this.columns,
|
||||||
|
collection : this.episodeCollection,
|
||||||
|
className : 'table table-hover season-grid'
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_shouldShowEpisodes : function() {
|
||||||
|
var startDate = moment().add('month', -1);
|
||||||
|
var endDate = moment().add('year', 1);
|
||||||
|
|
||||||
|
return this.episodeCollection.some(function(episode) {
|
||||||
|
var airDate = episode.get('airDateUtc');
|
||||||
|
|
||||||
|
if (airDate) {
|
||||||
|
var airDateMoment = moment(airDate);
|
||||||
|
|
||||||
|
if (airDateMoment.isAfter(startDate) && airDateMoment.isBefore(endDate)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showHideEpisodes : function() {
|
||||||
|
if (this.showingEpisodes) {
|
||||||
|
this.showingEpisodes = false;
|
||||||
|
this.episodeGrid.close();
|
||||||
|
} else {
|
||||||
|
this.showingEpisodes = true;
|
||||||
|
this._showEpisodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.templateHelpers.showingEpisodes = this.showingEpisodes;
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
_episodeMonitoredToggled : function(options) {
|
||||||
|
var model = options.model;
|
||||||
|
var shiftKey = options.shiftKey;
|
||||||
|
|
||||||
|
if (!this.episodeCollection.get(model.get('id'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shiftKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastToggled = this.episodeCollection.lastToggled;
|
||||||
|
|
||||||
|
if (!lastToggled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentIndex = this.episodeCollection.indexOf(model);
|
||||||
|
var lastIndex = this.episodeCollection.indexOf(lastToggled);
|
||||||
|
|
||||||
|
var low = Math.min(currentIndex, lastIndex);
|
||||||
|
var high = Math.max(currentIndex, lastIndex);
|
||||||
|
var range = _.range(low + 1, high);
|
||||||
|
|
||||||
|
this.episodeCollection.lastToggled = model;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateEpisodeCollection : function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.episodeCollection.add(this.fullEpisodeCollection.bySeason(this.model.get('seasonNumber')).models, { merge : true });
|
||||||
|
|
||||||
|
this.episodeCollection.each(function(model) {
|
||||||
|
model.episodeCollection = self.episodeCollection;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_refreshEpisodes : function() {
|
||||||
|
this._updateEpisodeCollection();
|
||||||
|
this.episodeCollection.fullCollection.sort();
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
_openEpisodeFileEditor : function() {
|
||||||
|
var view = new EpisodeFileEditorLayout({
|
||||||
|
model : this.model,
|
||||||
|
series : this.series,
|
||||||
|
episodeCollection : this.episodeCollection
|
||||||
|
});
|
||||||
|
|
||||||
|
vent.trigger(vent.Commands.OpenModalCommand, view);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,50 @@
|
|||||||
|
<div class="series-season" id="season-{{seasonNumber}}">
|
||||||
|
<h2>
|
||||||
|
<i class="x-season-monitored season-monitored clickable" title="Toggle season monitored status"/>
|
||||||
|
|
||||||
|
{{#if seasonNumber}}
|
||||||
|
Season {{seasonNumber}}
|
||||||
|
{{else}}
|
||||||
|
Specials
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
|
||||||
|
{{#if_eq episodeCount compare=0}}
|
||||||
|
{{#if monitored}}
|
||||||
|
<span class="badge badge-primary season-status" title="No aired episodes"> </span>
|
||||||
|
{{else}}
|
||||||
|
<span class="badge badge-warning season-status" title="Season is not monitored"> </span>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if_eq percentOfEpisodes compare=100}}
|
||||||
|
<span class="badge badge-success season-status" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded">{{episodeFileCount}} / {{episodeCount}}</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="badge badge-danger season-status" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded">{{episodeFileCount}} / {{episodeCount}}</span>
|
||||||
|
{{/if_eq}}
|
||||||
|
{{/if_eq}}
|
||||||
|
|
||||||
|
<span class="season-actions pull-right">
|
||||||
|
<div class="x-season-episode-file-editor">
|
||||||
|
<i class="icon-sonarr-episode-file" title="Modify episode files for season"/>
|
||||||
|
</div>
|
||||||
|
<div class="x-season-rename">
|
||||||
|
<i class="icon-sonarr-rename" title="Preview rename for season {{seasonNumber}}"/>
|
||||||
|
</div>
|
||||||
|
<div class="x-season-search">
|
||||||
|
<i class="icon-sonarr-search" title="Search for monitored episodes in season {{seasonNumber}}"/>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<div class="show-hide-episodes x-show-hide-episodes">
|
||||||
|
<h4>
|
||||||
|
{{#if showingEpisodes}}
|
||||||
|
<i class="icon-sonarr-panel-hide"/>
|
||||||
|
Hide Episodes
|
||||||
|
{{else}}
|
||||||
|
<i class="icon-sonarr-panel-show"/>
|
||||||
|
Show Episodes
|
||||||
|
{{/if}}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="x-episode-grid table-responsive"></div>
|
||||||
|
</div>
|
@ -0,0 +1,16 @@
|
|||||||
|
<div class="no-series">
|
||||||
|
<div class="row">
|
||||||
|
<div class="well col-md-12">
|
||||||
|
<i class="icon-sonarr-comment"/>
|
||||||
|
You must be new around here, You should add some series.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
<a href="/addmovies" class='btn btn-lg btn-block btn-success x-add-series'>
|
||||||
|
<i class='icon-sonarr-add'></i>
|
||||||
|
Add Movie
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,5 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.CompositeView.extend({
|
||||||
|
template : 'Series/Index/EmptyTemplate'
|
||||||
|
});
|
@ -0,0 +1,4 @@
|
|||||||
|
<div class="progress episode-progress">
|
||||||
|
<span class="progressbar-back-text">{{episodeFileCount}} / {{episodeCount}}</span>
|
||||||
|
<div class="progress-bar {{EpisodeProgressClass}} episode-progress" style="width:{{percentOfEpisodes}}%"><span class="progressbar-front-text">{{episodeFileCount}} / {{episodeCount}}</span></div>
|
||||||
|
</div>
|
@ -0,0 +1,4 @@
|
|||||||
|
var Backbone = require('backbone');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
module.exports = Backbone.Model.extend({});
|
@ -0,0 +1,5 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.CompositeView.extend({
|
||||||
|
template : 'Series/Index/FooterViewTemplate'
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="series-legend legend col-xs-6 col-sm-4">
|
||||||
|
<ul class='legend-labels'>
|
||||||
|
<li><span class="progress-bar"></span>Continuing (All episodes downloaded)</li>
|
||||||
|
<li><span class="progress-bar-success"></span>Ended (All episodes downloaded)</li>
|
||||||
|
<li><span class="progress-bar-danger"></span>Missing Episodes (Series monitored)</li>
|
||||||
|
<li><span class="progress-bar-warning"></span>Missing Episodes (Series not monitored)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-5 col-sm-7">
|
||||||
|
<div class="row">
|
||||||
|
<div class="series-stats col-sm-4">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Series</dt>
|
||||||
|
<dd>{{series}}</dd>
|
||||||
|
|
||||||
|
<dt>Ended</dt>
|
||||||
|
<dd>{{ended}}</dd>
|
||||||
|
|
||||||
|
<dt>Continuing</dt>
|
||||||
|
<dd>{{continuing}}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="series-stats col-sm-4">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Monitored</dt>
|
||||||
|
<dd>{{monitored}}</dd>
|
||||||
|
|
||||||
|
<dt>Unmonitored</dt>
|
||||||
|
<dd>{{unmonitored}}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="series-stats col-sm-4">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Episodes</dt>
|
||||||
|
<dd>{{episodes}}</dd>
|
||||||
|
|
||||||
|
<dt>Files</dt>
|
||||||
|
<dd>{{episodeFiles}}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,35 @@
|
|||||||
|
var vent = require('vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var CommandController = require('../../Commands/CommandController');
|
||||||
|
|
||||||
|
module.exports = Marionette.ItemView.extend({
|
||||||
|
ui : {
|
||||||
|
refresh : '.x-refresh'
|
||||||
|
},
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click .x-edit' : '_editSeries',
|
||||||
|
'click .x-refresh' : '_refreshSeries'
|
||||||
|
},
|
||||||
|
|
||||||
|
onRender : function() {
|
||||||
|
CommandController.bindToCommand({
|
||||||
|
element : this.ui.refresh,
|
||||||
|
command : {
|
||||||
|
name : 'refreshSeries',
|
||||||
|
seriesId : this.model.get('id')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_editSeries : function() {
|
||||||
|
vent.trigger(vent.Commands.EditSeriesCommand, { series : this.model });
|
||||||
|
},
|
||||||
|
|
||||||
|
_refreshSeries : function() {
|
||||||
|
CommandController.Execute('refreshSeries', {
|
||||||
|
name : 'refreshSeries',
|
||||||
|
seriesId : this.model.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,354 @@
|
|||||||
|
var _ = require('underscore');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var Backgrid = require('backgrid');
|
||||||
|
var PosterCollectionView = require('./Posters/SeriesPostersCollectionView');
|
||||||
|
var ListCollectionView = require('./Overview/SeriesOverviewCollectionView');
|
||||||
|
var EmptyView = require('./EmptyView');
|
||||||
|
var MoviesCollection = require('../MoviesCollection');
|
||||||
|
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||||
|
var SeriesTitleCell = require('../../Cells/SeriesTitleCell');
|
||||||
|
var TemplatedCell = require('../../Cells/TemplatedCell');
|
||||||
|
var ProfileCell = require('../../Cells/ProfileCell');
|
||||||
|
var EpisodeProgressCell = require('../../Cells/EpisodeProgressCell');
|
||||||
|
var SeriesActionsCell = require('../../Cells/SeriesActionsCell');
|
||||||
|
var SeriesStatusCell = require('../../Cells/SeriesStatusCell');
|
||||||
|
var FooterView = require('./FooterView');
|
||||||
|
var FooterModel = require('./FooterModel');
|
||||||
|
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
|
||||||
|
require('../../Mixins/backbone.signalr.mixin');
|
||||||
|
|
||||||
|
module.exports = Marionette.Layout.extend({
|
||||||
|
template : 'Movies/Index/MoviesIndexLayoutTemplate',
|
||||||
|
|
||||||
|
regions : {
|
||||||
|
seriesRegion : '#x-series',
|
||||||
|
toolbar : '#x-toolbar',
|
||||||
|
toolbar2 : '#x-toolbar2',
|
||||||
|
footer : '#x-series-footer'
|
||||||
|
},
|
||||||
|
|
||||||
|
columns : [
|
||||||
|
{
|
||||||
|
name : 'statusWeight',
|
||||||
|
label : '',
|
||||||
|
cell : SeriesStatusCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'title',
|
||||||
|
label : 'Title',
|
||||||
|
cell : SeriesTitleCell,
|
||||||
|
cellValue : 'this',
|
||||||
|
sortValue : 'sortTitle'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'seasonCount',
|
||||||
|
label : 'Seasons',
|
||||||
|
cell : 'integer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'profileId',
|
||||||
|
label : 'Profile',
|
||||||
|
cell : ProfileCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'network',
|
||||||
|
label : 'Network',
|
||||||
|
cell : 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'nextAiring',
|
||||||
|
label : 'Next Airing',
|
||||||
|
cell : RelativeDateCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'percentOfEpisodes',
|
||||||
|
label : 'Episodes',
|
||||||
|
cell : EpisodeProgressCell,
|
||||||
|
className : 'episode-progress-cell'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'this',
|
||||||
|
label : '',
|
||||||
|
sortable : false,
|
||||||
|
cell : SeriesActionsCell
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
leftSideButtons : {
|
||||||
|
type : 'default',
|
||||||
|
storeState : false,
|
||||||
|
collapse : true,
|
||||||
|
items : [
|
||||||
|
{
|
||||||
|
title : 'Add Movie',
|
||||||
|
icon : 'icon-sonarr-add',
|
||||||
|
route : 'addmovies'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Season Pass',
|
||||||
|
icon : 'icon-sonarr-monitored',
|
||||||
|
route : 'seasonpass'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Series Editor',
|
||||||
|
icon : 'icon-sonarr-edit',
|
||||||
|
route : 'serieseditor'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'RSS Sync',
|
||||||
|
icon : 'icon-sonarr-rss',
|
||||||
|
command : 'rsssync',
|
||||||
|
errorMessage : 'RSS Sync Failed!'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Update Library',
|
||||||
|
icon : 'icon-sonarr-refresh',
|
||||||
|
command : 'refreshseries',
|
||||||
|
successMessage : 'Library was updated!',
|
||||||
|
errorMessage : 'Library update failed!'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize : function() {
|
||||||
|
this.seriesCollection = MoviesCollection.clone();
|
||||||
|
this.seriesCollection.shadowCollection.bindSignalR();
|
||||||
|
|
||||||
|
this.listenTo(this.seriesCollection.shadowCollection, 'sync', function(model, collection, options) {
|
||||||
|
this.seriesCollection.fullCollection.resetFiltered();
|
||||||
|
this._renderView();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.listenTo(this.seriesCollection.shadowCollection, 'add', function(model, collection, options) {
|
||||||
|
this.seriesCollection.fullCollection.resetFiltered();
|
||||||
|
this._renderView();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.listenTo(this.seriesCollection.shadowCollection, 'remove', function(model, collection, options) {
|
||||||
|
this.seriesCollection.fullCollection.resetFiltered();
|
||||||
|
this._renderView();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sortingOptions = {
|
||||||
|
type : 'sorting',
|
||||||
|
storeState : false,
|
||||||
|
viewCollection : this.seriesCollection,
|
||||||
|
items : [
|
||||||
|
{
|
||||||
|
title : 'Title',
|
||||||
|
name : 'title'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Seasons',
|
||||||
|
name : 'seasonCount'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Quality',
|
||||||
|
name : 'profileId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Network',
|
||||||
|
name : 'network'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Next Airing',
|
||||||
|
name : 'nextAiring'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title : 'Episodes',
|
||||||
|
name : 'percentOfEpisodes'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
this.filteringOptions = {
|
||||||
|
type : 'radio',
|
||||||
|
storeState : true,
|
||||||
|
menuKey : 'series.filterMode',
|
||||||
|
defaultAction : 'all',
|
||||||
|
items : [
|
||||||
|
{
|
||||||
|
key : 'all',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'All',
|
||||||
|
icon : 'icon-sonarr-all',
|
||||||
|
callback : this._setFilter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'monitored',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Monitored Only',
|
||||||
|
icon : 'icon-sonarr-monitored',
|
||||||
|
callback : this._setFilter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'continuing',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Continuing Only',
|
||||||
|
icon : 'icon-sonarr-series-continuing',
|
||||||
|
callback : this._setFilter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'ended',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Ended Only',
|
||||||
|
icon : 'icon-sonarr-series-ended',
|
||||||
|
callback : this._setFilter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'missing',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Missing',
|
||||||
|
icon : 'icon-sonarr-missing',
|
||||||
|
callback : this._setFilter
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
this.viewButtons = {
|
||||||
|
type : 'radio',
|
||||||
|
storeState : true,
|
||||||
|
menuKey : 'seriesViewMode',
|
||||||
|
defaultAction : 'listView',
|
||||||
|
items : [
|
||||||
|
{
|
||||||
|
key : 'posterView',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Posters',
|
||||||
|
icon : 'icon-sonarr-view-poster',
|
||||||
|
callback : this._showPosters
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'listView',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Overview List',
|
||||||
|
icon : 'icon-sonarr-view-list',
|
||||||
|
callback : this._showList
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key : 'tableView',
|
||||||
|
title : '',
|
||||||
|
tooltip : 'Table',
|
||||||
|
icon : 'icon-sonarr-view-table',
|
||||||
|
callback : this._showTable
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow : function() {
|
||||||
|
this._showToolbar();
|
||||||
|
this._fetchCollection();
|
||||||
|
},
|
||||||
|
|
||||||
|
_showTable : function() {
|
||||||
|
this.currentView = new Backgrid.Grid({
|
||||||
|
collection : this.seriesCollection,
|
||||||
|
columns : this.columns,
|
||||||
|
className : 'table table-hover'
|
||||||
|
});
|
||||||
|
|
||||||
|
this._renderView();
|
||||||
|
},
|
||||||
|
|
||||||
|
_showList : function() {
|
||||||
|
this.currentView = new ListCollectionView({
|
||||||
|
collection : this.seriesCollection
|
||||||
|
});
|
||||||
|
|
||||||
|
this._renderView();
|
||||||
|
},
|
||||||
|
|
||||||
|
_showPosters : function() {
|
||||||
|
this.currentView = new PosterCollectionView({
|
||||||
|
collection : this.seriesCollection
|
||||||
|
});
|
||||||
|
|
||||||
|
this._renderView();
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderView : function() {
|
||||||
|
if (MoviesCollection.length === 0) {
|
||||||
|
this.seriesRegion.show(new EmptyView());
|
||||||
|
|
||||||
|
this.toolbar.close();
|
||||||
|
this.toolbar2.close();
|
||||||
|
} else {
|
||||||
|
this.seriesRegion.show(this.currentView);
|
||||||
|
|
||||||
|
this._showToolbar();
|
||||||
|
this._showFooter();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_fetchCollection : function() {
|
||||||
|
this.seriesCollection.fetch();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setFilter : function(buttonContext) {
|
||||||
|
var mode = buttonContext.model.get('key');
|
||||||
|
|
||||||
|
this.seriesCollection.setFilterMode(mode);
|
||||||
|
},
|
||||||
|
|
||||||
|
_showToolbar : function() {
|
||||||
|
if (this.toolbar.currentView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toolbar2.show(new ToolbarLayout({
|
||||||
|
right : [
|
||||||
|
this.filteringOptions
|
||||||
|
],
|
||||||
|
context : this
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.toolbar.show(new ToolbarLayout({
|
||||||
|
right : [
|
||||||
|
this.sortingOptions,
|
||||||
|
this.viewButtons
|
||||||
|
],
|
||||||
|
left : [
|
||||||
|
this.leftSideButtons
|
||||||
|
],
|
||||||
|
context : this
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_showFooter : function() {
|
||||||
|
var footerModel = new FooterModel();
|
||||||
|
var series = MoviesCollection.models.length;
|
||||||
|
var episodes = 0;
|
||||||
|
var episodeFiles = 0;
|
||||||
|
var ended = 0;
|
||||||
|
var continuing = 0;
|
||||||
|
var monitored = 0;
|
||||||
|
|
||||||
|
_.each(MoviesCollection.models, function(model) {
|
||||||
|
episodes += model.get('episodeCount');
|
||||||
|
episodeFiles += model.get('episodeFileCount');
|
||||||
|
|
||||||
|
if (model.get('status').toLowerCase() === 'ended') {
|
||||||
|
ended++;
|
||||||
|
} else {
|
||||||
|
continuing++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model.get('monitored')) {
|
||||||
|
monitored++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
footerModel.set({
|
||||||
|
series : series,
|
||||||
|
ended : ended,
|
||||||
|
continuing : continuing,
|
||||||
|
monitored : monitored,
|
||||||
|
unmonitored : series - monitored,
|
||||||
|
episodes : episodes,
|
||||||
|
episodeFiles : episodeFiles
|
||||||
|
});
|
||||||
|
|
||||||
|
this.footer.show(new FooterView({ model : footerModel }));
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,12 @@
|
|||||||
|
<div class="toolbars">
|
||||||
|
<div id="x-toolbar"></div>
|
||||||
|
<div id="x-toolbar2"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div id="x-series" class="table-responsive"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="x-series-footer"></div>
|
@ -0,0 +1,8 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
var ListItemView = require('./SeriesOverviewItemView');
|
||||||
|
|
||||||
|
module.exports = Marionette.CompositeView.extend({
|
||||||
|
itemView : ListItemView,
|
||||||
|
itemViewContainer : '#x-series-list',
|
||||||
|
template : 'Series/Index/Overview/SeriesOverviewCollectionViewTemplate'
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
<div id="x-series-list"/>
|
@ -0,0 +1,7 @@
|
|||||||
|
var vent = require('vent');
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
var SeriesIndexItemView = require('../MoviesIndexItemView');
|
||||||
|
|
||||||
|
module.exports = SeriesIndexItemView.extend({
|
||||||
|
template : 'Movies/Index/Overview/MoviesOverviewItemViewTemplate'
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
<div class="series-item">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2 col-xs-3">
|
||||||
|
<a href="{{route}}">
|
||||||
|
{{poster}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10 col-xs-9">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10 col-xs-10">
|
||||||
|
<a href="{{route}}" target="_blank">
|
||||||
|
<h2>{{title}}</h2>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 col-xs-2">
|
||||||
|
<div class="pull-right series-overview-list-actions">
|
||||||
|
<i class="icon-sonarr-refresh x-refresh" title="Update series info and scan disk"/>
|
||||||
|
<i class="icon-sonarr-edit x-edit" title="Edit Series"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 col-xs-12">
|
||||||
|
<a href="{{route}}">
|
||||||
|
<div>
|
||||||
|
{{overview}}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10 col-xs-8">
|
||||||
|
{{#if_eq status compare="ended"}}
|
||||||
|
<span class="label label-danger">Ended</span>
|
||||||
|
{{/if_eq}}
|
||||||
|
|
||||||
|
{{#if nextAiring}}
|
||||||
|
<span class="label label-default">{{RelativeDate nextAiring}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{seasonCountHelper}}
|
||||||
|
|
||||||
|
{{profile profileId}}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 col-xs-4">
|
||||||
|
{{> EpisodeProgressPartial }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,8 @@
|
|||||||
|
var Marionette = require('marionette');
|
||||||
|
var PosterItemView = require('./SeriesPostersItemView');
|
||||||
|
|
||||||
|
module.exports = Marionette.CompositeView.extend({
|
||||||
|
itemView : PosterItemView,
|
||||||
|
itemViewContainer : '#x-series-posters',
|
||||||
|
template : 'Series/Index/Posters/SeriesPostersCollectionViewTemplate'
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
<ul id="x-series-posters" class="series-posters"></ul>
|
@ -0,0 +1,19 @@
|
|||||||
|
var SeriesIndexItemView = require('../MoviesIndexItemView');
|
||||||
|
|
||||||
|
module.exports = SeriesIndexItemView.extend({
|
||||||
|
tagName : 'li',
|
||||||
|
template : 'Movies/Index/Posters/SeriesPostersItemViewTemplate',
|
||||||
|
|
||||||
|
initialize : function() {
|
||||||
|
this.events['mouseenter .x-series-poster-container'] = 'posterHoverAction';
|
||||||
|
this.events['mouseleave .x-series-poster-container'] = 'posterHoverAction';
|
||||||
|
|
||||||
|
this.ui.controls = '.x-series-controls';
|
||||||
|
this.ui.title = '.x-title';
|
||||||
|
},
|
||||||
|
|
||||||
|
posterHoverAction : function() {
|
||||||
|
this.ui.controls.slideToggle();
|
||||||
|
this.ui.title.slideToggle();
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,30 @@
|
|||||||
|
<div class="series-posters-item">
|
||||||
|
<div class="center">
|
||||||
|
<div class="series-poster-container x-series-poster-container">
|
||||||
|
<div class="series-controls x-series-controls">
|
||||||
|
<i class="icon-sonarr-refresh x-refresh" title="Refresh Series"/>
|
||||||
|
<i class="icon-sonarr-edit x-edit" title="Edit Series"/>
|
||||||
|
</div>
|
||||||
|
{{#unless_eq status compare="continuing"}}
|
||||||
|
<div class="ended-banner">Ended</div>
|
||||||
|
{{/unless_eq}}
|
||||||
|
<a href="{{route}}">
|
||||||
|
{{poster}}
|
||||||
|
<div class="center title">{{title}}</div>
|
||||||
|
</a>
|
||||||
|
<div class="hidden-title x-title">
|
||||||
|
{{title}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="center">
|
||||||
|
<div class="labels">
|
||||||
|
{{> EpisodeProgressPartial }}
|
||||||
|
|
||||||
|
{{#if nextAiring}}
|
||||||
|
<span class="label label-default">{{RelativeDate nextAiring}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,34 @@
|
|||||||
|
var NzbDroneController = require('../Shared/NzbDroneController');
|
||||||
|
var AppLayout = require('../AppLayout');
|
||||||
|
var MoviesCollection = require('./MoviesCollection');
|
||||||
|
var MoviesIndexLayout = require('./Index/MoviesIndexLayout');
|
||||||
|
var MoviesDetailsLayout = require('./Details/MoviesDetailsLayout');
|
||||||
|
|
||||||
|
module.exports = NzbDroneController.extend({
|
||||||
|
_originalInit : NzbDroneController.prototype.initialize,
|
||||||
|
|
||||||
|
initialize : function() {
|
||||||
|
this.route('', this.series);
|
||||||
|
this.route('movies', this.series);
|
||||||
|
this.route('movies/:query', this.seriesDetails);
|
||||||
|
|
||||||
|
this._originalInit.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
series : function() {
|
||||||
|
this.setTitle('Movies');
|
||||||
|
this.showMainRegion(new MoviesIndexLayout());
|
||||||
|
},
|
||||||
|
|
||||||
|
seriesDetails : function(query) {
|
||||||
|
var series = MoviesCollection.where({ titleSlug : query });
|
||||||
|
|
||||||
|
if (series.length !== 0) {
|
||||||
|
var targetMovie = series[0];
|
||||||
|
this.setTitle(targetMovie.get('title'));
|
||||||
|
this.showMainRegion(new MoviesDetailsLayout({ model : targetMovie }));
|
||||||
|
} else {
|
||||||
|
this.showNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in new issue