1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-09-11 12:02:35 +02:00

Fixed: Multiple Calendar Issues, Other UI Fixes (#2685)

Fixes #2683 Fixes #1172 Fixes #563
This commit is contained in:
Qstick 2018-03-28 12:54:50 -04:00 committed by Leonardo Galli
parent 00877ad361
commit a2fa8c4594
27 changed files with 112 additions and 957 deletions

View File

@ -20,12 +20,6 @@ var QueueCollection = PageableCollection.extend({
mode : 'client',
findEpisode : function(episodeId) {
return _.find(this.fullCollection.models, function(queueModel) {
return queueModel.get('episode').id === episodeId;
});
},
findMovie : function(movieId) {
return _.find(this.fullCollection.models, function(queueModel) {
return queueModel.get('movie').id === movieId;
@ -33,14 +27,6 @@ var QueueCollection = PageableCollection.extend({
},
sortMappings : {
series : {
sortValue : function(model, attr) {
var series = model.get(attr);
return series.get('sortTitle');
}
},
movie : {
sortValue : function(model, attr) {
var movie = model.get(attr);
@ -49,22 +35,6 @@ var QueueCollection = PageableCollection.extend({
}
},
episode : {
sortValue : function(model, attr) {
var episode = model.get('episode');
return FormatHelpers.pad(episode.get('seasonNumber'), 4) + FormatHelpers.pad(episode.get('episodeNumber'), 4);
}
},
episodeTitle : {
sortValue : function(model, attr) {
var episode = model.get('episode');
return episode.get('title');
}
},
timeleft : {
sortValue : function(model, attr) {
var eta = model.get('estimatedCompletionTime');

View File

@ -2,8 +2,6 @@ var Marionette = require('marionette');
var Backgrid = require('backgrid');
var QueueCollection = require('./QueueCollection');
var MovieTitleCell = require('../../Cells/MovieTitleCell');
var EpisodeNumberCell = require('../../Cells/EpisodeNumberCell');
var EpisodeTitleCell = require('../../Cells/EpisodeTitleCell');
var QualityCell = require('../../Cells/QualityCell');
var QueueStatusCell = require('./QueueStatusCell');
var QueueActionsCell = require('./QueueActionsCell');
@ -32,17 +30,6 @@ module.exports = Marionette.Layout.extend({
label : 'Movie',
cell : MovieTitleCell
},
/*{
name : 'episode',
label : 'Episode',
cell : EpisodeNumberCell
},
{
name : 'episodeTitle',
label : 'Episode Title',
cell : EpisodeTitleCell,
cellValue : 'episode'
},*/
{
name : 'quality',
label : 'Quality',

View File

@ -1,9 +1,9 @@
var Backbone = require('backbone');
var EpisodeModel = require('../Movies/MovieModel');
var MovieModel = require('../Movies/MovieModel');
module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/calendar',
model : EpisodeModel,
model : MovieModel,
tableName : 'calendar',
comparator : function(model) {

View File

@ -1,10 +1,12 @@
var $ = require('jquery');
var vent = require('vent');
var Marionette = require('marionette');
var AppLayout = require('../AppLayout');
var moment = require('moment');
var CalendarCollection = require('./CalendarCollection');
var UiSettings = require('../Shared/UiSettingsModel');
var QueueCollection = require('../Activity/Queue/QueueCollection');
var MoviesDetailsLayout = require('../Movies/Details/MoviesDetailsLayout');
var Config = require('../Config');
require('../Mixins/backbone.signalr.mixin');
@ -89,7 +91,7 @@ module.exports = Marionette.ItemView.extend({
});
element.find('.chart').tooltip({
title : 'Episode is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle),
title : 'Movie is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle),
container : '.fc'
});
}
@ -144,7 +146,7 @@ module.exports = Marionette.ItemView.extend({
var self = this;
collection.each(function(model) {
var seriesTitle = model.get('title');
var movieTitle = model.get('title');
var start = model.get('inCinemas');
var startDate = new Date(start);
if (!(startD <= startDate && startDate <= endD)) {
@ -154,14 +156,15 @@ module.exports = Marionette.ItemView.extend({
var end = moment(start).add(runtime, 'minutes').toISOString();
var event = {
title : seriesTitle,
title : movieTitle,
start : moment(start),
end : moment(end),
allDay : true,
statusLevel : self._getStatusLevel(model, end),
downloading : QueueCollection.findMovie(model.get('id')),
model : model,
sortOrder : 0
sortOrder : 0,
url : "movies/" + model.get("titleSlug")
};
events.push(event);
@ -229,11 +232,7 @@ module.exports = Marionette.ItemView.extend({
viewRender : this._viewRender.bind(this),
eventRender : this._eventRender.bind(this),
eventAfterAllRender : this._eventAfterAllRender.bind(this),
windowResize : this._windowResize.bind(this),
eventClick : function(event) {
//vent.trigger(vent.Commands.ShowMovieDetails, { movie : event.model });
window.location.href = "movies/"+event.model.get("titleSlug");
}
windowResize : this._windowResize.bind(this)
};
if ($(window).width() < 768) {
@ -256,13 +255,16 @@ module.exports = Marionette.ItemView.extend({
};
}
options.titleFormat = "L";
options.columnFormat = "L"; /*{
month : 'ddd',
week : UiSettings.get('calendarWeekColumnHeader'),
day : 'dddd'
};*///For now ignore settings. TODO update that.
options.views = {
month: {
titleFormat: 'MMMM YYYY',
columnFormat: 'ddd'
},
list: {
titleFormat: 'L',
columnFormat: 'L'
}
},
options.timeFormat = UiSettings.get('timeFormat');

View File

@ -1,17 +1,29 @@
var Backbone = require('backbone');
var moment = require('moment');
var EpisodeModel = require('../Movies/MovieModel');
var MovieModel = require('../Movies/MovieModel');
module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/calendar',
model : EpisodeModel,
model : MovieModel,
comparator : function(model1, model2) {
var airDate1 = model1.get('inCinemas');
var airDate2 = model2.get('inCinemas');
var status1 = model1.get('status');
var status2 = model2.get('status');
if (status1 === 'inCinemas') {
airDate1 = model1.get('physicalRelease');
}
if (status2 === 'inCinemas') {
airDate2 = model2.get('physicalRelease');
}
var date1 = moment(airDate1);
var time1 = date1.unix();
var airDate2 = model2.get('inCinemas');
var date2 = moment(airDate2);
var time2 = date2.unix();

View File

@ -6,23 +6,7 @@ module.exports = Marionette.ItemView.extend({
template : 'Calendar/UpcomingItemViewTemplate',
tagName : 'div',
events : {
'click .x-episode-title' : '_showEpisodeDetails'
},
initialize : function() {
var start = this.model.get('inCinemas');
var runtime = this.model.get('runtime');
var end = moment(start).add(runtime, 'minutes');
this.model.set({
end : end.toISOString()
});
this.listenTo(this.model, 'change', this.render);
},
_showEpisodeDetails : function() {
vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : this.model });
}
});

View File

@ -1,18 +1,15 @@
<div class="event">
<div class="date {{StatusLevel}}">
<h1>{{Day airDateUtc}}</h1>
<h4>{{Month airDateUtc}}</h4>
{{#if_eq status compare="announced"}}
<h1>{{Day inCinemas}}</h1>
<h4>{{Month inCinemas}}</h4>
{{else}}
<h1>{{Day physicalRelease}}</h1>
<h4>{{Month physicalRelease}}</h4>
{{/if_eq}}
</div>
{{#with series}}
<a href="{{route}}">
<h4>{{title}}</h4>
</a>
{{/with}}
<p>{{StartTime airDateUtc}} {{#unless_today airDateUtc}}{{ShortDate airDateUtc}}{{/unless_today}}</p>
<p>
<span class="episode-title x-episode-title">
{{title}}
</span>
<span class="pull-right">{{seasonNumber}}x{{Pad2 episodeNumber}}</span>
</p>
</div>

View File

@ -96,7 +96,6 @@
.primary {
border-color : @btn-primary-bg;
color: white;
}
.info {

View File

@ -1,28 +0,0 @@
var Marionette = require('marionette');
var NzbDroneCell = require('./NzbDroneCell');
module.exports = NzbDroneCell.extend({
className : 'episode-progress-cell',
template : 'Cells/EpisodeProgressCellTemplate',
render : function() {
var episodeCount = this.model.get('episodeCount');
var episodeFileCount = this.model.get('episodeFileCount');
var percent = 100;
if (episodeCount > 0) {
percent = episodeFileCount / episodeCount * 100;
}
this.model.set('percentOfEpisodes', percent);
this.templateFunction = Marionette.TemplateCache.get(this.template);
var data = this.model.toJSON();
var html = this.templateFunction(data);
this.$el.html(html);
return this;
}
});

View File

@ -1 +0,0 @@
{{> EpisodeProgressPartial }}

View File

@ -1,127 +0,0 @@
var reqres = require('../reqres');
var Backbone = require('backbone');
var NzbDroneCell = require('./NzbDroneCell');
var QueueCollection = require('../Activity/Queue/QueueCollection');
var moment = require('moment');
var FormatHelpers = require('../Shared/FormatHelpers');
module.exports = NzbDroneCell.extend({
className : 'episode-status-cell',
render : function() {
this.listenTo(QueueCollection, 'sync', this._renderCell);
this._renderCell();
return this;
},
_renderCell : function() {
if (this.episodeFile) {
this.stopListening(this.episodeFile, 'change', this._refresh);
}
this.$el.empty();
if (this.model) {
var icon;
var tooltip;
var hasAired = moment(this.model.get('airDateUtc')).isBefore(moment());
this.episodeFile = this._getFile();
if (this.episodeFile) {
this.listenTo(this.episodeFile, 'change', this._refresh);
var quality = this.episodeFile.get('quality');
var revision = quality.revision;
var size = FormatHelpers.bytes(this.episodeFile.get('size'));
var title = 'Episode downloaded';
if (revision.real && revision.real > 0) {
title += '[REAL]';
}
if (revision.version && revision.version > 1) {
title += ' [PROPER]';
}
if (size !== '') {
title += ' - {0}'.format(size);
}
if (this.episodeFile.get('qualityCutoffNotMet')) {
this.$el.html('<span class="badge badge-inverse" title="{0}">{1}</span>'.format(title, quality.quality.name));
} else {
this.$el.html('<span class="badge" title="{0}">{1}</span>'.format(title, quality.quality.name));
}
return;
}
else {
var model = this.model;
var downloading = QueueCollection.findEpisode(model.get('id'));
if (downloading) {
var progress = 100 - (downloading.get('sizeleft') / downloading.get('size') * 100);
if (progress === 0) {
icon = 'icon-sonarr-downloading';
tooltip = 'Episode is downloading';
}
else {
this.$el.html('<div class="progress" title="Episode is downloading - {0}% {1}">'.format(progress.toFixed(1), downloading.get('title')) +
'<div class="progress-bar progress-bar-purple" style="width: {0}%;"></div></div>'.format(progress));
return;
}
}
else if (this.model.get('grabbed')) {
icon = 'icon-sonarr-downloading';
tooltip = 'Episode is downloading';
}
else if (!this.model.get('airDateUtc')) {
icon = 'icon-sonarr-tba';
tooltip = 'TBA';
}
else if (hasAired) {
icon = 'icon-sonarr-missing';
tooltip = 'Episode missing from disk';
} else {
icon = 'icon-sonarr-not-aired';
tooltip = 'Episode has not aired';
}
}
this.$el.html('<i class="{0}" title="{1}"/>'.format(icon, tooltip));
}
},
_getFile : function() {
var hasFile = this.model.get('hasFile');
if (hasFile) {
var episodeFile;
if (reqres.hasHandler(reqres.Requests.GetEpisodeFileById)) {
episodeFile = reqres.request(reqres.Requests.GetEpisodeFileById, this.model.get('episodeFileId'));
}
else if (this.model.has('episodeFile')) {
episodeFile = new Backbone.Model(this.model.get('episodeFile'));
}
if (episodeFile) {
return episodeFile;
}
}
return undefined;
}
});

View File

@ -1,29 +0,0 @@
var vent = require('vent');
var NzbDroneCell = require('./NzbDroneCell');
module.exports = NzbDroneCell.extend({
className : 'episode-title-cell',
events : {
'click' : '_showDetails'
},
render : function() {
var title = this.cellValue.get('title');
if (!title || title === '') {
title = 'TBA';
}
this.$el.html(title);
return this;
},
_showDetails : function() {
var hideSeriesLink = this.column.get('hideSeriesLink');
vent.trigger(vent.Commands.ShowEpisodeDetails, {
episode : this.cellValue,
hideSeriesLink : hideSeriesLink
});
}
});

View File

@ -1,66 +0,0 @@
var Handlebars = require('handlebars');
var FormatHelpers = require('../../Shared/FormatHelpers');
var moment = require('moment');
require('../../Activity/Queue/QueueCollection');
Handlebars.registerHelper('EpisodeNumber', function() {
if (this.series.seriesType === 'daily') {
return moment(this.airDate).format('L');
} else if (this.series.seriesType === 'anime' && this.absoluteEpisodeNumber !== undefined) {
return '{0}x{1} ({2})'.format(this.seasonNumber, FormatHelpers.pad(this.episodeNumber, 2), FormatHelpers.pad(this.absoluteEpisodeNumber, 2));
} else {
return '{0}x{1}'.format(this.seasonNumber, FormatHelpers.pad(this.episodeNumber, 2));
}
});
Handlebars.registerHelper('StatusLevel', function() {
var hasFile = this.hasFile;
var downloading = require('../../Activity/Queue/QueueCollection').findEpisode(this.id) || this.downloading;
var currentTime = moment();
var start = moment(this.airDateUtc);
var end = moment(this.end);
var monitored = this.series.monitored && this.monitored;
if (hasFile) {
return 'success';
}
if (downloading) {
return 'purple';
}
else if (!monitored) {
return 'unmonitored';
}
if (this.episodeNumber === 1) {
return 'premiere';
}
if (currentTime.isAfter(start) && currentTime.isBefore(end)) {
return 'warning';
}
if (start.isBefore(currentTime) && !hasFile) {
return 'danger';
}
return 'primary';
});
Handlebars.registerHelper('EpisodeProgressClass', function() {
if (this.episodeFileCount === this.episodeCount) {
if (this.status === 'continuing') {
return '';
}
return 'progress-bar-success';
}
if (this.monitored) {
return 'progress-bar-danger';
}
return 'progress-bar-warning';
});

View File

@ -1,6 +1,73 @@
var Handlebars = require('handlebars');
var StatusModel = require('../../System/StatusModel');
var FormatHelpers = require('../../Shared/FormatHelpers');
var moment = require('moment');
var _ = require('underscore');
require('../../Activity/Queue/QueueCollection');
Handlebars.registerHelper('GetStatus', function() {
var monitored = this.monitored;
var status = this.status;
//var inCinemas = this.inCinemas;
//var date = new Date(inCinemas);
//var timeSince = new Date().getTime() - date.getTime();
//var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
if (status === "announced") {
return new Handlebars.SafeString('<i class="icon-sonarr-movie-announced grid-icon" title=""></i>&nbsp;Announced');
}
if (status ==="inCinemas") {
return new Handlebars.SafeString('<i class="icon-sonarr-movie-cinemas grid-icon" title=""></i>&nbsp;In Cinemas');
}
if (status === 'released') {
return new Handlebars.SafeString('<i class="icon-sonarr-movie-released grid-icon" title=""></i>&nbsp;Released');
}
if (!monitored) {
return new Handlebars.SafeString('<i class="icon-sonarr-series-unmonitored grid-icon" title=""></i>&nbsp;Not Monitored');
}
});
Handlebars.registerHelper('route', function() {
return StatusModel.get('urlBase') + '/movies/' + this.titleSlug;
});
Handlebars.registerHelper('StatusLevel', function() {
var hasFile = this.hasFile;
var downloading = require('../../Activity/Queue/QueueCollection').findMovie(this.id) || this.downloading;
var currentTime = moment();
var monitored = this.monitored;
if (hasFile) {
return 'success';
}
else if (downloading) {
return 'purple';
}
else if (!monitored) {
return 'unmonitored';
}
else if (this.status === "inCinemas") {
return 'premiere';
}
else if (this.status === "released") {
return 'danger';
}
else if (this.status === "announced") {
return 'primary';
}
return 'primary';
});
Handlebars.registerHelper('poster', function() {
@ -75,33 +142,6 @@ Handlebars.registerHelper('alternativeTitlesString', function() {
return '"' + titles.slice(0,titles.length-1).join('", "') + '" and "' + titles[titles.length-1] + '"';
});
Handlebars.registerHelper('GetStatus', function() {
var monitored = this.monitored;
var status = this.status;
//var inCinemas = this.inCinemas;
//var date = new Date(inCinemas);
//var timeSince = new Date().getTime() - date.getTime();
//var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
if (status === "announced") {
return new Handlebars.SafeString('<i class="icon-sonarr-movie-announced grid-icon" title=""></i>&nbsp;Announced');
}
if (status ==="inCinemas") {
return new Handlebars.SafeString('<i class="icon-sonarr-movie-cinemas grid-icon" title=""></i>&nbsp;In Cinemas');
}
if (status === 'released') {
return new Handlebars.SafeString('<i class="icon-sonarr-movie-released grid-icon" title=""></i>&nbsp;Released');
}
if (!monitored) {
return new Handlebars.SafeString('<i class="icon-sonarr-series-unmonitored grid-icon" title=""></i>&nbsp;Not Monitored');
}
});
Handlebars.registerHelper('GetBannerStatus', function() {
var monitored = this.monitored;
var status = this.status;
@ -161,7 +201,6 @@ Handlebars.registerHelper("DownloadedQuality", function() {
return "";
});
Handlebars.registerHelper('inCinemas', function() {
var monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
@ -184,46 +223,6 @@ Handlebars.registerHelper('inCinemas', function() {
return "To be announced";
});
Handlebars.registerHelper('tvRageUrl', function() {
return 'http://www.tvrage.com/shows/id-' + this.tvRageId;
});
Handlebars.registerHelper('tvMazeUrl', function() {
return 'http://www.tvmaze.com/shows/' + this.tvMazeId + '/_';
});
Handlebars.registerHelper('route', function() {
return StatusModel.get('urlBase') + '/movies/' + this.titleSlug;
});
Handlebars.registerHelper('percentOfEpisodes', function() {
var episodeCount = this.episodeCount;
var episodeFileCount = this.episodeFileCount;
var percent = 100;
if (episodeCount > 0) {
percent = episodeFileCount / episodeCount * 100;
}
return percent;
});
Handlebars.registerHelper('seasonCountHelper', function() {
var seasonCount = this.seasonCount;
var continuing = this.status === 'continuing';
if (continuing) {
return new Handlebars.SafeString('<span class="label label-info">Season {0}</span>'.format(seasonCount));
}
if (seasonCount === 1) {
return new Handlebars.SafeString('<span class="label label-info">{0} Season</span>'.format(seasonCount));
}
return new Handlebars.SafeString('<span class="label label-info">{0} Seasons</span>'.format(seasonCount));
});
Handlebars.registerHelper('titleWithYear', function() {
if (this.title.endsWith(' ({0})'.format(this.year))) {
return this.title;
@ -234,4 +233,4 @@ Handlebars.registerHelper('titleWithYear', function() {
}
return new Handlebars.SafeString('{0} <span class="year">({1})</span>'.format(this.title, this.year));
});
});

View File

@ -3,8 +3,7 @@ require('handlebars.helpers');
require('./Helpers/DateTime');
require('./Helpers/Html');
require('./Helpers/Numbers');
require('./Helpers/Episode');
require('./Helpers/Series');
require('./Helpers/Movie');
require('./Helpers/Quality');
require('./Helpers/System');
require('./Helpers/EachReverse');

View File

@ -1,47 +0,0 @@
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 : 'Movies/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;
}
});

View File

@ -1,39 +0,0 @@
<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>

View File

@ -1,21 +0,0 @@
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;
}
});

View File

@ -45,7 +45,6 @@ module.exports = Marionette.Layout.extend({
},
events : {
'click .x-episode-file-editor' : '_showFiles',
'click .x-monitored' : '_toggleMonitored',
'click .x-edit' : '_editMovie',
'click .x-refresh' : '_refreshMovies',
@ -251,15 +250,6 @@ module.exports = Marionette.Layout.extend({
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');

View File

@ -9,9 +9,6 @@
{{title}} <span class="year">({{year}}{{#if secondaryYear}} / <a href="https://mappings.radarr.video/mapping/{{secondaryYearSourceId}}" target="_blank"><span title="Secondary year pulled from Radarr Mappings.
Click to head on over there and tell us whether this is correct or not.">{{secondaryYear}}</span></a>{{/if}})</span>
<div class="movie-actions pull-right">
<div class="x-episode-file-editor">
<i class="icon-sonarr-episode-file" title="Modify movie files"/>
</div>
<div class="x-refresh">
<i class="icon-sonarr-refresh icon-can-spin" title="Update movie info and scan disk"/>
</div>

View File

@ -1,44 +0,0 @@
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;

View File

@ -1,301 +0,0 @@
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 : 'Movies/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(-1, 'month');
var endDate = moment().add(1, 'year');
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);
}
});

View File

@ -1,50 +0,0 @@
<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">&nbsp;</span>
{{else}}
<span class="badge badge-warning season-status" title="Season is not monitored">&nbsp;</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>

View File

@ -1,4 +0,0 @@
<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>

View File

@ -222,14 +222,6 @@ var Collection = PageableCollection.extend({
return 0;
}
},
percentOfEpisodes : {
sortValue : function(model, attr) {
var percentOfEpisodes = model.get(attr);
var episodeCount = model.get('episodeCount');
return percentOfEpisodes + episodeCount / 1000000;
}
},
inCinemas : {
sortValue : function(model, attr) {

View File

@ -2,7 +2,6 @@ var Marionette = require('marionette');
var Backgrid = require('backgrid');
var ReleaseCollection = require('./ReleaseCollection');
var IndexerCell = require('../Cells/IndexerCell');
var EpisodeNumberCell = require('../Cells/EpisodeNumberCell');
var FileSizeCell = require('../Cells/FileSizeCell');
var QualityCell = require('../Cells/QualityCell');
var ApprovalStatusCell = require('../Cells/ApprovalStatusCell');
@ -37,12 +36,6 @@ module.exports = Marionette.Layout.extend({
sortable : true,
cell : ReleaseTitleCell
},
/*{
name : 'episodeNumbers',
episodes : 'episodeNumbers',
label : 'season',
cell : EpisodeNumberCell
},*/
{
name : 'size',
label : 'Size',

View File

@ -92,15 +92,6 @@ var Collection = PageableCollection.extend({
}
},
percentOfEpisodes : {
sortValue : function(model, attr) {
var percentOfEpisodes = model.get(attr);
var episodeCount = model.get('episodeCount');
return percentOfEpisodes + episodeCount / 1000000;
}
},
path : {
sortValue : function(model) {
var path = model.get('path');