mirror of https://github.com/lidarr/Lidarr
parent
965508910e
commit
8b98cd8825
|
@ -13,12 +13,13 @@ namespace NzbDrone.Api.Albums
|
|||
{
|
||||
|
||||
public string Title { get; set; }
|
||||
public int ArtistId { get; set; }
|
||||
public string Label { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public string Path { get; set; }
|
||||
public int ProfileId { get; set; }
|
||||
public Ratings Ratings { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
public List<string> Genres { get; set; }
|
||||
public ArtistResource Artist { get; set; }
|
||||
public List<MediaCover> Images { get; set; }
|
||||
|
@ -34,6 +35,7 @@ namespace NzbDrone.Api.Albums
|
|||
return new AlbumResource
|
||||
{
|
||||
Id = model.Id,
|
||||
ArtistId = model.ArtistId,
|
||||
Label = model.Label,
|
||||
Path = model.Path,
|
||||
ProfileId = model.ProfileId,
|
||||
|
@ -55,6 +57,7 @@ namespace NzbDrone.Api.Albums
|
|||
return new Core.Music.Album
|
||||
{
|
||||
Id = resource.Id,
|
||||
ArtistId = resource.ArtistId,
|
||||
Label = resource.Label,
|
||||
Path = resource.Path,
|
||||
Monitored = resource.Monitored,
|
||||
|
|
|
@ -8,6 +8,7 @@ using Ical.Net.Interfaces.Serialization;
|
|||
using Ical.Net.Serialization;
|
||||
using Ical.Net.Serialization.iCalendar.Factory;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Music;
|
||||
using Nancy.Responses;
|
||||
using NzbDrone.Core.Tags;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
@ -16,13 +17,13 @@ namespace NzbDrone.Api.Calendar
|
|||
{
|
||||
public class CalendarFeedModule : NzbDroneFeedModule
|
||||
{
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly IAlbumService _albumService;
|
||||
private readonly ITagService _tagService;
|
||||
|
||||
public CalendarFeedModule(IEpisodeService episodeService, ITagService tagService)
|
||||
public CalendarFeedModule(IAlbumService albumService, ITagService tagService)
|
||||
: base("calendar")
|
||||
{
|
||||
_episodeService = episodeService;
|
||||
_albumService = albumService;
|
||||
_tagService = tagService;
|
||||
|
||||
Get["/NzbDrone.ics"] = options => GetCalendarFeed();
|
||||
|
@ -86,7 +87,7 @@ namespace NzbDrone.Api.Calendar
|
|||
tags.AddRange(tagInput.Split(',').Select(_tagService.GetTag).Select(t => t.Id));
|
||||
}
|
||||
|
||||
var episodes = _episodeService.EpisodesBetweenDates(start, end, unmonitored);
|
||||
var albums = _albumService.AlbumsBetweenDates(start, end, unmonitored);
|
||||
var calendar = new Ical.Net.Calendar
|
||||
{
|
||||
// This will need to point to the hosted web site
|
||||
|
@ -96,43 +97,28 @@ namespace NzbDrone.Api.Calendar
|
|||
|
||||
|
||||
|
||||
foreach (var episode in episodes.OrderBy(v => v.AirDateUtc.Value))
|
||||
foreach (var album in albums.OrderBy(v => v.ReleaseDate))
|
||||
{
|
||||
if (premiersOnly && (episode.SeasonNumber == 0 || episode.EpisodeNumber != 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//if (premiersOnly && (album.SeasonNumber == 0 || album.EpisodeNumber != 1))
|
||||
//{
|
||||
// continue;
|
||||
//}
|
||||
|
||||
if (tags.Any() && tags.None(episode.Series.Tags.Contains))
|
||||
if (tags.Any() && tags.None(album.Artist.Tags.Contains))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var occurrence = calendar.Create<Event>();
|
||||
occurrence.Uid = "NzbDrone_album_" + episode.Id;
|
||||
occurrence.Status = episode.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
|
||||
occurrence.Description = episode.Overview;
|
||||
occurrence.Categories = new List<string>() { episode.Series.Network };
|
||||
occurrence.Uid = "NzbDrone_album_" + album.Id;
|
||||
//occurrence.Status = album.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
|
||||
//occurrence.Description = album.Overview;
|
||||
//occurrence.Categories = new List<string>() { album.Artist. };
|
||||
|
||||
if (asAllDay)
|
||||
{
|
||||
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value) { HasTime = false };
|
||||
}
|
||||
else
|
||||
{
|
||||
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value) { HasTime = true };
|
||||
occurrence.End = new CalDateTime(episode.AirDateUtc.Value.AddMinutes(episode.Series.Runtime)) { HasTime = true };
|
||||
}
|
||||
occurrence.Start = new CalDateTime(album.ReleaseDate.Value) { HasTime = false };
|
||||
|
||||
occurrence.Summary =$"{album.Artist.Name} - {album.Title}";
|
||||
|
||||
switch (episode.Series.SeriesType)
|
||||
{
|
||||
case SeriesTypes.Daily:
|
||||
occurrence.Summary = $"{episode.Series.Title} - {episode.Title}";
|
||||
break;
|
||||
default:
|
||||
occurrence.Summary =$"{episode.Series.Title} - {episode.SeasonNumber}x{episode.EpisodeNumber:00} - {episode.Title}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var serializer = (IStringSerializer) new SerializerFactory().Build(calendar.GetType(), new SerializationContext());
|
||||
|
|
|
@ -2,24 +2,26 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.Episodes;
|
||||
using NzbDrone.Api.Albums;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace NzbDrone.Api.Calendar
|
||||
{
|
||||
public class CalendarModule : EpisodeModuleWithSignalR
|
||||
public class CalendarModule : AlbumModuleWithSignalR
|
||||
{
|
||||
public CalendarModule(IEpisodeService episodeService,
|
||||
ISeriesService seriesService,
|
||||
public CalendarModule(IAlbumService albumService,
|
||||
IArtistService artistService,
|
||||
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||
IBroadcastSignalRMessage signalRBroadcaster)
|
||||
: base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "calendar")
|
||||
: base(albumService, artistService, qualityUpgradableSpecification, signalRBroadcaster, "calendar")
|
||||
{
|
||||
GetResourceAll = GetCalendar;
|
||||
}
|
||||
|
||||
private List<EpisodeResource> GetCalendar()
|
||||
private List<AlbumResource> GetCalendar()
|
||||
{
|
||||
var start = DateTime.Today;
|
||||
var end = DateTime.Today.AddDays(2);
|
||||
|
@ -33,9 +35,9 @@ namespace NzbDrone.Api.Calendar
|
|||
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
||||
if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value);
|
||||
|
||||
var resources = MapToResource(_episodeService.EpisodesBetweenDates(start, end, includeUnmonitored), true, true);
|
||||
var resources = MapToResource(_albumService.AlbumsBetweenDates(start, end, includeUnmonitored), true);
|
||||
|
||||
return resources.OrderBy(e => e.AirDateUtc).ToList();
|
||||
return resources.OrderBy(e => e.ReleaseDate).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@ namespace NzbDrone.Core.Music
|
|||
Images = new List<MediaCover.MediaCover>();
|
||||
}
|
||||
|
||||
public const string RELEASE_DATE_FORMAT = "yyyy-MM-dd";
|
||||
|
||||
public string ForeignAlbumId { get; set; }
|
||||
public int ArtistId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string CleanTitle { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
public string Label { get; set; }
|
||||
//public int TrackCount { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Marr.Data.QGen;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
@ -11,6 +13,7 @@ namespace NzbDrone.Core.Music
|
|||
List<Album> GetAlbums(int artistId);
|
||||
Album FindByName(string cleanTitle);
|
||||
Album FindById(string spotifyId);
|
||||
List<Album> AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored);
|
||||
void SetMonitoredFlat(Album album, bool monitored);
|
||||
}
|
||||
|
||||
|
@ -36,6 +39,22 @@ namespace NzbDrone.Core.Music
|
|||
return Query.Where(s => s.ForeignAlbumId == foreignAlbumId).SingleOrDefault();
|
||||
}
|
||||
|
||||
public List<Album> AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored)
|
||||
{
|
||||
var query = Query.Join<Album, Artist>(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistId == s.Id)
|
||||
.Where<Album>(e => e.ReleaseDate >= startDate)
|
||||
.AndWhere(e => e.ReleaseDate <= endDate);
|
||||
|
||||
|
||||
if (!includeUnmonitored)
|
||||
{
|
||||
query.AndWhere(e => e.Monitored)
|
||||
.AndWhere(e => e.Artist.Monitored);
|
||||
}
|
||||
|
||||
return query.ToList();
|
||||
}
|
||||
|
||||
public void SetMonitoredFlat(Album album, bool monitored)
|
||||
{
|
||||
album.Monitored = monitored;
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace NzbDrone.Core.Music
|
|||
Album UpdateAlbum(Album album);
|
||||
List<Album> UpdateAlbums(List<Album> album);
|
||||
void SetAlbumMonitored(int albumId, bool monitored);
|
||||
List<Album> AlbumsBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
||||
void InsertMany(List<Album> albums);
|
||||
void UpdateMany(List<Album> albums);
|
||||
void DeleteMany(List<Album> albums);
|
||||
|
@ -110,6 +111,13 @@ namespace NzbDrone.Core.Music
|
|||
_albumRepository.SetFields(album, s => s.AddOptions);
|
||||
}
|
||||
|
||||
public List<Album> AlbumsBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
|
||||
{
|
||||
var albums = _albumRepository.AlbumsBetweenDates(start.ToUniversalTime(), end.ToUniversalTime(), includeUnmonitored);
|
||||
|
||||
return albums;
|
||||
}
|
||||
|
||||
public void InsertMany(List<Album> albums)
|
||||
{
|
||||
_albumRepository.InsertMany(albums);
|
||||
|
|
|
@ -403,7 +403,14 @@ namespace NzbDrone.Core.Organizer
|
|||
{
|
||||
tokenHandlers["{Album Title}"] = m => album.Title;
|
||||
tokenHandlers["{Album CleanTitle}"] = m => CleanTitle(album.Title);
|
||||
tokenHandlers["{Release Year}"] = m => album.ReleaseDate.Year.ToString();
|
||||
if (album.ReleaseDate.HasValue)
|
||||
{
|
||||
tokenHandlers["{Release Year}"] = m => album.ReleaseDate.Value.Year.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenHandlers["{Release Year}"] = m => "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private string AddSeasonEpisodeNumberingTokens(string pattern, Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Episode> episodes, NamingConfig namingConfig)
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
var Marionette = require('marionette');
|
||||
var SummaryLayout = require('./Summary/AlbumSummaryLayout');
|
||||
var SearchLayout = require('./Search/AlbumSearchLayout');
|
||||
var AlbumHistoryLayout = require('./History/AlbumHistoryLayout');
|
||||
var ArtistCollection = require('../Artist/ArtistCollection');
|
||||
var Messenger = require('../Shared/Messenger');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
className : 'modal-lg',
|
||||
template : 'Album/AlbumDetailsLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
summary : '#album-summary',
|
||||
history : '#album-history',
|
||||
search : '#album-search'
|
||||
},
|
||||
|
||||
ui : {
|
||||
summary : '.x-album-summary',
|
||||
history : '.x-album-history',
|
||||
search : '.x-album-search',
|
||||
monitored : '.x-album-monitored'
|
||||
},
|
||||
|
||||
events : {
|
||||
|
||||
'click .x-album-summary' : '_showSummary',
|
||||
'click .x-album-history' : '_showHistory',
|
||||
'click .x-album-search' : '_showSearch',
|
||||
'click .x-album-monitored' : '_toggleMonitored'
|
||||
},
|
||||
|
||||
templateHelpers : {},
|
||||
|
||||
initialize : function(options) {
|
||||
|
||||
this.templateHelpers.hideArtistLink = options.hideArtistLink;
|
||||
|
||||
|
||||
this.artist = ArtistCollection.get(this.model.get('artistId'));
|
||||
|
||||
this.templateHelpers.artist = this.artist.toJSON();
|
||||
this.openingTab = options.openingTab || 'summary';
|
||||
|
||||
this.listenTo(this.model, 'sync', this._setMonitoredState);
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
this.searchLayout = new SearchLayout({ model : this.model });
|
||||
|
||||
if (this.openingTab === 'search') {
|
||||
this.searchLayout.startManualSearch = true;
|
||||
this._showSearch();
|
||||
}
|
||||
|
||||
else {
|
||||
this._showSummary();
|
||||
}
|
||||
|
||||
this._setMonitoredState();
|
||||
|
||||
if (this.artist.get('monitored')) {
|
||||
this.$el.removeClass('artist-not-monitored');
|
||||
}
|
||||
|
||||
else {
|
||||
this.$el.addClass('artist-not-monitored');
|
||||
}
|
||||
},
|
||||
|
||||
_showSummary : function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.summary.tab('show');
|
||||
this.summary.show(new SummaryLayout({
|
||||
model : this.model,
|
||||
artist : this.artist
|
||||
}));
|
||||
},
|
||||
|
||||
_showHistory : function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.history.tab('show');
|
||||
this.history.show(new AlbumHistoryLayout({
|
||||
model : this.model,
|
||||
artist : this.artist
|
||||
}));
|
||||
},
|
||||
|
||||
_showSearch : function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.search.tab('show');
|
||||
this.search.show(this.searchLayout);
|
||||
},
|
||||
|
||||
_toggleMonitored : function() {
|
||||
if (!this.series.get('monitored')) {
|
||||
|
||||
Messenger.show({
|
||||
message : 'Unable to change monitored state when artist is not monitored',
|
||||
type : 'error'
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var name = 'monitored';
|
||||
this.model.set(name, !this.model.get(name), { silent : true });
|
||||
|
||||
this.ui.monitored.addClass('icon-lidarr-spinner fa-spin');
|
||||
this.model.save();
|
||||
},
|
||||
|
||||
_setMonitoredState : function() {
|
||||
this.ui.monitored.removeClass('fa-spin icon-lidarr-spinner');
|
||||
|
||||
if (this.model.get('monitored')) {
|
||||
this.ui.monitored.addClass('icon-lidarr-monitored');
|
||||
this.ui.monitored.removeClass('icon-lidarr-unmonitored');
|
||||
} else {
|
||||
this.ui.monitored.addClass('icon-lidarr-unmonitored');
|
||||
this.ui.monitored.removeClass('icon-lidarr-monitored');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
<div class="modal-content">
|
||||
<div class="album-detail-modal">
|
||||
<div class="modal-header">
|
||||
<span class="hidden-artist-title x-artist-title">{{artist.name}}</span>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
|
||||
<h3>
|
||||
<i class="icon-lidarr-monitored x-album-monitored album-monitored" title="Toggle monitored status" />
|
||||
{{title}} ({{albumYear}})
|
||||
</h3>
|
||||
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul class="nav nav-tabs" id="myTab">
|
||||
<li><a href="#album-summary" class="x-album-summary">Summary</a></li>
|
||||
<li><a href="#album-history" class="x-album-history">History</a></li>
|
||||
<li><a href="#album-search" class="x-album-search">Search</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="album-summary"/>
|
||||
<div class="tab-pane" id="album-history"/>
|
||||
<div class="tab-pane" id="album-search"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{{#unless hideArtistLink}}
|
||||
{{#with artist}}
|
||||
<a href="{{route}}" class="btn btn-default pull-left" data-dismiss="modal">Go to Artist</a>
|
||||
{{/with}}
|
||||
{{/unless}}
|
||||
|
||||
<button class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,35 @@
|
|||
var $ = require('jquery');
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'album-actions-cell',
|
||||
|
||||
events : {
|
||||
'click .x-failed' : '_markAsFailed'
|
||||
},
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
|
||||
if (this.model.get('eventType') === 'grabbed') {
|
||||
this.$el.html('<i class="icon-lidarr-delete x-failed" title="Mark download as failed"></i>');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_markAsFailed : function() {
|
||||
var url = window.NzbDrone.ApiRoot + '/history/failed';
|
||||
var data = {
|
||||
id : this.model.get('id')
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url : url,
|
||||
type : 'POST',
|
||||
data : data
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
var $ = require('jquery');
|
||||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||
var HistoryDetailsView = require('../../Activity/History/Details/HistoryDetailsView');
|
||||
require('bootstrap');
|
||||
|
||||
module.exports = NzbDroneCell.extend({
|
||||
className : 'album-history-details-cell',
|
||||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
this.$el.html('<i class="icon-lidarr-form-info"></i>');
|
||||
|
||||
var html = new HistoryDetailsView({ model : this.model }).render().$el;
|
||||
|
||||
this.$el.popover({
|
||||
content : html,
|
||||
html : true,
|
||||
trigger : 'hover',
|
||||
title : 'Details',
|
||||
placement : 'left',
|
||||
container : this.$el
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
var HistoryCollection = require('../../Activity/History/HistoryCollection');
|
||||
var EventTypeCell = require('../../Cells/EventTypeCell');
|
||||
var QualityCell = require('../../Cells/QualityCell');
|
||||
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||
var AlbumHistoryActionsCell = require('./AlbumHistoryActionsCell');
|
||||
var AlbumHistoryDetailsCell = require('./AlbumHistoryDetailsCell');
|
||||
var NoHistoryView = require('./NoHistoryView');
|
||||
var LoadingView = require('../../Shared/LoadingView');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Album/History/AlbumHistoryLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
historyTable : '.history-table'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'eventType',
|
||||
label : '',
|
||||
cell : EventTypeCell,
|
||||
cellValue : 'this'
|
||||
},
|
||||
{
|
||||
name : 'sourceTitle',
|
||||
label : 'Source Title',
|
||||
cell : 'string'
|
||||
},
|
||||
{
|
||||
name : 'quality',
|
||||
label : 'Quality',
|
||||
cell : QualityCell
|
||||
},
|
||||
{
|
||||
name : 'date',
|
||||
label : 'Date',
|
||||
cell : RelativeDateCell
|
||||
},
|
||||
{
|
||||
name : 'this',
|
||||
label : '',
|
||||
cell : AlbumHistoryDetailsCell,
|
||||
sortable : false
|
||||
},
|
||||
{
|
||||
name : 'this',
|
||||
label : '',
|
||||
cell : AlbumHistoryActionsCell,
|
||||
sortable : false
|
||||
}
|
||||
],
|
||||
|
||||
initialize : function(options) {
|
||||
this.model = options.model;
|
||||
this.artist = options.artist;
|
||||
|
||||
this.collection = new HistoryCollection({
|
||||
albumId : this.model.id,
|
||||
tableName : 'albumHistory'
|
||||
});
|
||||
this.collection.fetch();
|
||||
this.listenTo(this.collection, 'sync', this._showTable);
|
||||
},
|
||||
|
||||
onRender : function() {
|
||||
this.historyTable.show(new LoadingView());
|
||||
},
|
||||
|
||||
_showTable : function() {
|
||||
if (this.collection.any()) {
|
||||
this.historyTable.show(new Backgrid.Grid({
|
||||
collection : this.collection,
|
||||
columns : this.columns,
|
||||
className : 'table table-hover table-condensed'
|
||||
}));
|
||||
}
|
||||
|
||||
else {
|
||||
this.historyTable.show(new NoHistoryView());
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
<div class="history-table table-responsive"></div>
|
|
@ -0,0 +1,5 @@
|
|||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Album/History/NoHistoryViewTemplate'
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
<p class="text-warning">
|
||||
No history for this album.
|
||||
</p>
|
|
@ -0,0 +1,82 @@
|
|||
var vent = require('vent');
|
||||
var Marionette = require('marionette');
|
||||
var ButtonsView = require('./ButtonsView');
|
||||
var ManualSearchLayout = require('./ManualLayout');
|
||||
var ReleaseCollection = require('../../Release/ReleaseCollection');
|
||||
var CommandController = require('../../Commands/CommandController');
|
||||
var LoadingView = require('../../Shared/LoadingView');
|
||||
var NoResultsView = require('./NoResultsView');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Album/Search/AlbumSearchLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
main : '#album-search-region'
|
||||
},
|
||||
|
||||
events : {
|
||||
'click .x-search-auto' : '_searchAuto',
|
||||
'click .x-search-manual' : '_searchManual',
|
||||
'click .x-search-back' : '_showButtons'
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
this.mainView = new ButtonsView();
|
||||
this.releaseCollection = new ReleaseCollection();
|
||||
|
||||
this.listenTo(this.releaseCollection, 'sync', this._showSearchResults);
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
if (this.startManualSearch) {
|
||||
this._searchManual();
|
||||
}
|
||||
|
||||
else {
|
||||
this._showMainView();
|
||||
}
|
||||
},
|
||||
|
||||
_searchAuto : function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
CommandController.Execute('episodeSearch', {
|
||||
episodeIds : [this.model.get('id')] //TODO Refactor for Albums search
|
||||
});
|
||||
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
},
|
||||
|
||||
_searchManual : function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.mainView = new LoadingView();
|
||||
this._showMainView();
|
||||
this.releaseCollection.fetchEpisodeReleases(this.model.id); //TODO Refactor for Albums
|
||||
},
|
||||
|
||||
_showMainView : function() {
|
||||
this.main.show(this.mainView);
|
||||
},
|
||||
|
||||
_showButtons : function() {
|
||||
this.mainView = new ButtonsView();
|
||||
this._showMainView();
|
||||
},
|
||||
|
||||
_showSearchResults : function() {
|
||||
if (this.releaseCollection.length === 0) {
|
||||
this.mainView = new NoResultsView();
|
||||
}
|
||||
|
||||
else {
|
||||
this.mainView = new ManualSearchLayout({ collection : this.releaseCollection });
|
||||
}
|
||||
|
||||
this._showMainView();
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
<div id="album-search-region"></div>
|
|
@ -0,0 +1,5 @@
|
|||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Album/Search/ButtonsViewTemplate'
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
<div class="search-buttons">
|
||||
<button class="btn btn-lg btn-block x-search-auto"><i class="icon-lidarr-search-automatic"/> Automatic Search</button>
|
||||
<button class="btn btn-lg btn-block btn-primary x-search-manual"><i class="icon-lidarr-search-manual"/> Manual Search</button>
|
||||
</div>
|
|
@ -0,0 +1,86 @@
|
|||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
var ReleaseTitleCell = require('../../Cells/ReleaseTitleCell');
|
||||
var FileSizeCell = require('../../Cells/FileSizeCell');
|
||||
var QualityCell = require('../../Cells/QualityCell');
|
||||
var ApprovalStatusCell = require('../../Cells/ApprovalStatusCell');
|
||||
var DownloadReportCell = require('../../Release/DownloadReportCell');
|
||||
var AgeCell = require('../../Release/AgeCell');
|
||||
var ProtocolCell = require('../../Release/ProtocolCell');
|
||||
var PeersCell = require('../../Release/PeersCell');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Album/Search/ManualLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
grid : '#album-release-grid'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'protocol',
|
||||
label : 'Source',
|
||||
cell : ProtocolCell
|
||||
},
|
||||
{
|
||||
name : 'age',
|
||||
label : 'Age',
|
||||
cell : AgeCell
|
||||
},
|
||||
{
|
||||
name : 'title',
|
||||
label : 'Title',
|
||||
cell : ReleaseTitleCell
|
||||
},
|
||||
{
|
||||
name : 'indexer',
|
||||
label : 'Indexer',
|
||||
cell : Backgrid.StringCell
|
||||
},
|
||||
{
|
||||
name : 'size',
|
||||
label : 'Size',
|
||||
cell : FileSizeCell
|
||||
},
|
||||
{
|
||||
name : 'seeders',
|
||||
label : 'Peers',
|
||||
cell : PeersCell
|
||||
},
|
||||
{
|
||||
name : 'quality',
|
||||
label : 'Quality',
|
||||
cell : QualityCell
|
||||
},
|
||||
{
|
||||
name : 'rejections',
|
||||
label : '<i class="icon-lidarr-header-rejections" />',
|
||||
tooltip : 'Rejections',
|
||||
cell : ApprovalStatusCell,
|
||||
sortable : true,
|
||||
sortType : 'fixed',
|
||||
direction : 'ascending',
|
||||
title : 'Release Rejected'
|
||||
},
|
||||
{
|
||||
name : 'download',
|
||||
label : '<i class="icon-lidarr-download" />',
|
||||
tooltip : 'Auto-Search Prioritization',
|
||||
cell : DownloadReportCell,
|
||||
sortable : true,
|
||||
sortType : 'fixed',
|
||||
direction : 'ascending'
|
||||
}
|
||||
],
|
||||
|
||||
onShow : function() {
|
||||
if (!this.isClosed) {
|
||||
this.grid.show(new Backgrid.Grid({
|
||||
row : Backgrid.Row,
|
||||
columns : this.columns,
|
||||
collection : this.collection,
|
||||
className : 'table table-hover'
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
<div id="album-release-grid" class="table-responsive"></div>
|
||||
<button class="btn x-search-back">Back</button>
|
|
@ -0,0 +1,5 @@
|
|||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Album/Search/NoResultsViewTemplate'
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
<div>No results found</div>
|
|
@ -0,0 +1,119 @@
|
|||
var reqres = require('../../reqres');
|
||||
var Marionette = require('marionette');
|
||||
var Backgrid = require('backgrid');
|
||||
var TrackFileModel = require('../../Artist/TrackFileModel');
|
||||
var TrackFileCollection = require('../../Artist/TrackFileCollection');
|
||||
var FileSizeCell = require('../../Cells/FileSizeCell');
|
||||
var QualityCell = require('../../Cells/QualityCell');
|
||||
var DeleteEpisodeFileCell = require('../../Cells/DeleteEpisodeFileCell');
|
||||
var NoFileView = require('./NoFileView');
|
||||
var LoadingView = require('../../Shared/LoadingView');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Album/Summary/AlbumSummaryLayoutTemplate',
|
||||
|
||||
regions : {
|
||||
overview : '.album-overview',
|
||||
activity : '.album-file-info'
|
||||
},
|
||||
|
||||
columns : [
|
||||
{
|
||||
name : 'path',
|
||||
label : 'Path',
|
||||
cell : 'string',
|
||||
sortable : false
|
||||
},
|
||||
{
|
||||
name : 'size',
|
||||
label : 'Size',
|
||||
cell : FileSizeCell,
|
||||
sortable : false
|
||||
},
|
||||
{
|
||||
name : 'quality',
|
||||
label : 'Quality',
|
||||
cell : QualityCell,
|
||||
sortable : false,
|
||||
editable : true
|
||||
},
|
||||
{
|
||||
name : 'this',
|
||||
label : '',
|
||||
cell : DeleteEpisodeFileCell,
|
||||
sortable : false
|
||||
}
|
||||
],
|
||||
|
||||
templateHelpers : {},
|
||||
|
||||
initialize : function(options) {
|
||||
if (!this.model.artist) {
|
||||
this.templateHelpers.artist = options.artist.toJSON();
|
||||
}
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
if (this.model.get('hasFile')) { //TODO Refactor for Albums
|
||||
var episodeFileId = this.model.get('episodeFileId');
|
||||
|
||||
if (reqres.hasHandler(reqres.Requests.GetEpisodeFileById)) {
|
||||
var episodeFile = reqres.request(reqres.Requests.GetEpisodeFileById, episodeFileId);
|
||||
this.trackFileCollection = new TrackFileCollection(episodeFile, { seriesId : this.model.get('seriesId') });
|
||||
this.listenTo(episodeFile, 'destroy', this._episodeFileDeleted);
|
||||
|
||||
this._showTable();
|
||||
}
|
||||
|
||||
else {
|
||||
this.activity.show(new LoadingView());
|
||||
|
||||
var self = this;
|
||||
var newEpisodeFile = new TrackFileModel({ id : episodeFileId });
|
||||
this.episodeFileCollection = new TrackFileCollection(newEpisodeFile, { seriesId : this.model.get('seriesId') });
|
||||
var promise = newEpisodeFile.fetch();
|
||||
this.listenTo(newEpisodeFile, 'destroy', this._trackFileDeleted);
|
||||
|
||||
promise.done(function() {
|
||||
self._showTable();
|
||||
});
|
||||
}
|
||||
|
||||
this.listenTo(this.episodeFileCollection, 'add remove', this._collectionChanged);
|
||||
}
|
||||
|
||||
else {
|
||||
this._showNoFileView();
|
||||
}
|
||||
},
|
||||
|
||||
_showTable : function() {
|
||||
this.activity.show(new Backgrid.Grid({
|
||||
collection : this.trackFileCollection,
|
||||
columns : this.columns,
|
||||
className : 'table table-bordered',
|
||||
emptyText : 'Nothing to see here!'
|
||||
}));
|
||||
},
|
||||
|
||||
_showNoFileView : function() {
|
||||
this.activity.show(new NoFileView());
|
||||
},
|
||||
|
||||
_collectionChanged : function() {
|
||||
if (!this.trackFileCollection.any()) {
|
||||
this._showNoFileView();
|
||||
}
|
||||
|
||||
else {
|
||||
this._showTable();
|
||||
}
|
||||
},
|
||||
|
||||
_trackFileDeleted : function() {
|
||||
this.model.set({
|
||||
trackFileId : 0,
|
||||
hasFile : false
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
<div class="album-info">
|
||||
{{profile profileId}}
|
||||
<span class="label label-info">{{label}}</span>
|
||||
<span class="label label-info">{{RelativeDate releaseDate}}</span>
|
||||
</div>
|
||||
|
||||
<div class="album-overview">
|
||||
{{overview}}
|
||||
</div>
|
||||
|
||||
<div class="album-file-info"></div>
|
|
@ -0,0 +1,5 @@
|
|||
var Marionette = require('marionette');
|
||||
|
||||
module.exports = Marionette.ItemView.extend({
|
||||
template : 'Album/Summary/NoFileViewTemplate'
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
<p class="text-warning">
|
||||
No file(s) available for this album.
|
||||
</p>
|
|
@ -24,6 +24,7 @@ module.exports = Marionette.Layout.extend({
|
|||
albumSearch : '.x-album-search',
|
||||
albumMonitored : '.x-album-monitored',
|
||||
albumRename : '.x-album-rename',
|
||||
albumDetails : '.x-album-details',
|
||||
cover : '.x-album-cover'
|
||||
},
|
||||
|
||||
|
@ -32,6 +33,7 @@ module.exports = Marionette.Layout.extend({
|
|||
'click .x-album-monitored' : '_albumMonitored',
|
||||
'click .x-album-search' : '_albumSearch',
|
||||
'click .x-album-rename' : '_albumRename',
|
||||
'click .x-album-details' : '_albumDetails',
|
||||
'click .x-show-hide-tracks' : '_showHideTracks',
|
||||
'dblclick .artist-album h2' : '_showHideTracks'
|
||||
},
|
||||
|
@ -193,6 +195,10 @@ module.exports = Marionette.Layout.extend({
|
|||
});
|
||||
},
|
||||
|
||||
_albumDetails : function() {
|
||||
vent.trigger(vent.Commands.ShowAlbumDetails, { album : this.model });
|
||||
},
|
||||
|
||||
_albumMonitored : function() {
|
||||
if (!this.artist.get('monitored')) {
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
<i class="x-album-monitored album-monitored clickable" title="Toggle album monitored status"/>
|
||||
|
||||
{{#if title}}
|
||||
<div class="x-album-details album-details">
|
||||
{{title}} ({{albumYear}})
|
||||
|
||||
</div>
|
||||
{{else}}
|
||||
Specials
|
||||
{{/if}}
|
||||
|
|
|
@ -7,7 +7,7 @@ module.exports = NzbDroneCell.extend({
|
|||
|
||||
render : function() {
|
||||
this.$el.empty();
|
||||
var ratings = this.model.get('ratings')
|
||||
var ratings = this.model.get('ratings');
|
||||
|
||||
if (ratings) {
|
||||
this.$el.html(ratings.value + ' (' + ratings.votes + ' votes)');
|
||||
|
|
|
@ -11,6 +11,19 @@
|
|||
.album-cover {
|
||||
min-width: 56px;
|
||||
max-width: 100%;
|
||||
|
||||
}
|
||||
|
||||
.album-details {
|
||||
display: inline;
|
||||
color: #428bca;
|
||||
text-decoration: none;
|
||||
|
||||
&:focus, &:hover {
|
||||
color: #2a6496;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.truncate {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
var Backbone = require('backbone');
|
||||
var EpisodeModel = require('../Series/EpisodeModel');
|
||||
var AlbumModel = require('../Artist/AlbumModel');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/calendar',
|
||||
model : EpisodeModel,
|
||||
model : AlbumModel,
|
||||
tableName : 'calendar',
|
||||
|
||||
comparator : function(model) {
|
||||
var date = new Date(model.get('airDateUtc'));
|
||||
var date = new Date(model.get('releaseDate'));
|
||||
var time = date.getTime();
|
||||
return time;
|
||||
}
|
||||
|
|
|
@ -23,42 +23,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Season Premiers Only</label>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="premiersOnly" class="form-control x-premiersOnly"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Show as All-Day Events</label>
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="asAllDay" class="form-control x-asAllDay"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Tags</label>
|
||||
|
||||
|
|
|
@ -10,13 +10,11 @@
|
|||
<div id="x-calendar" class="calendar"/>
|
||||
<div class="legend calendar">
|
||||
<ul class='legend-labels'>
|
||||
<li class="legend-label"><span class="premiere" title="Premiere episode hasn't aired yet"></span>Unaired Premiere</li>
|
||||
<li class="legend-label"><span class="primary" title="Episode hasn't aired yet"></span>Unaired</li>
|
||||
<li class="legend-label"><span class="warning" title="Episode is currently airing"></span>On Air</li>
|
||||
<li class="legend-label"><span class="purple" title="Episode is currently downloading"></span>Downloading</li>
|
||||
<li class="legend-label"><span class="danger" title="Episode file has not been found"></span>Missing</li>
|
||||
<li class="legend-label"><span class="success" title="Episode was downloaded and sorted"></span>Downloaded</li>
|
||||
<li class="legend-label"><span class="unmonitored" title="Episode is unmonitored"></span>Unmonitored</li>
|
||||
<li class="legend-label"><span class="primary" title="Album hasn't aired yet"></span>Unreleased</li>
|
||||
<li class="legend-label"><span class="purple" title="Album is currently downloading"></span>Downloading</li>
|
||||
<li class="legend-label"><span class="danger" title="Album file has not been found"></span>Missing</li>
|
||||
<li class="legend-label"><span class="success" title="Album was downloaded and sorted"></span>Downloaded</li>
|
||||
<li class="legend-label"><span class="unmonitored" title="Album is unmonitored"></span>Unmonitored</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -89,7 +89,7 @@ module.exports = Marionette.ItemView.extend({
|
|||
});
|
||||
|
||||
element.find('.chart').tooltip({
|
||||
title : 'Episode is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle),
|
||||
title : 'Album is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle),
|
||||
container : '.fc'
|
||||
});
|
||||
}
|
||||
|
@ -99,9 +99,6 @@ module.exports = Marionette.ItemView.extend({
|
|||
this._addStatusIcon(element, 'icon-lidarr-form-warning', 'Scene number hasn\'t been verified yet.');
|
||||
}
|
||||
|
||||
else if (event.model.get('series').seriesType === 'anime' && event.model.get('seasonNumber') > 0 && !event.model.has('absoluteEpisodeNumber')) {
|
||||
this._addStatusIcon(element, 'icon-lidarr-form-warning', 'Episode does not have an absolute episode number');
|
||||
}
|
||||
},
|
||||
|
||||
_eventAfterAllRender : function () {
|
||||
|
@ -148,20 +145,21 @@ module.exports = Marionette.ItemView.extend({
|
|||
var self = this;
|
||||
|
||||
collection.each(function(model) {
|
||||
var seriesTitle = model.get('series').title;
|
||||
var start = model.get('airDateUtc');
|
||||
var runtime = model.get('series').runtime;
|
||||
var albumTitle = model.get('title');
|
||||
var artistName = model.get('artist').name;
|
||||
var start = model.get('releaseDate');
|
||||
var runtime = '30';
|
||||
var end = moment(start).add('minutes', runtime).toISOString();
|
||||
|
||||
var event = {
|
||||
title : seriesTitle,
|
||||
title : artistName + " - " + albumTitle,
|
||||
start : moment(start),
|
||||
end : moment(end),
|
||||
allDay : false,
|
||||
allDay : true,
|
||||
statusLevel : self._getStatusLevel(model, end),
|
||||
downloading : QueueCollection.findEpisode(model.get('id')),
|
||||
model : model,
|
||||
sortOrder : (model.get('seasonNumber') === 0 ? 1000000 : model.get('seasonNumber') * 10000) + model.get('episodeNumber')
|
||||
sortOrder : 0
|
||||
};
|
||||
|
||||
events.push(event);
|
||||
|
@ -174,9 +172,9 @@ module.exports = Marionette.ItemView.extend({
|
|||
var hasFile = element.get('hasFile');
|
||||
var downloading = QueueCollection.findEpisode(element.get('id')) || element.get('grabbed');
|
||||
var currentTime = moment();
|
||||
var start = moment(element.get('airDateUtc'));
|
||||
var start = moment(element.get('releaseDate'));
|
||||
var end = moment(endTime);
|
||||
var monitored = element.get('series').monitored && element.get('monitored');
|
||||
var monitored = element.get('artist').monitored && element.get('monitored');
|
||||
|
||||
var statusLevel = 'primary';
|
||||
|
||||
|
@ -218,7 +216,7 @@ module.exports = Marionette.ItemView.extend({
|
|||
|
||||
_getOptions : function() {
|
||||
var options = {
|
||||
allDayDefault : false,
|
||||
allDayDefault : true,
|
||||
weekMode : 'variable',
|
||||
firstDay : UiSettings.get('firstDayOfWeek'),
|
||||
timeFormat : 'h(:mm)t',
|
||||
|
@ -227,7 +225,7 @@ module.exports = Marionette.ItemView.extend({
|
|||
eventAfterAllRender : this._eventAfterAllRender.bind(this),
|
||||
windowResize : this._windowResize.bind(this),
|
||||
eventClick : function(event) {
|
||||
vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : event.model });
|
||||
vent.trigger(vent.Commands.ShowAlbumDetails, { album : event.model });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -237,7 +235,7 @@ module.exports = Marionette.ItemView.extend({
|
|||
options.header = {
|
||||
left : 'prev,next today',
|
||||
center : 'title',
|
||||
right : 'basicWeek,basicDay'
|
||||
right : 'basicWeek,listYear'
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -247,20 +245,21 @@ module.exports = Marionette.ItemView.extend({
|
|||
options.header = {
|
||||
left : 'prev,next today',
|
||||
center : 'title',
|
||||
right : 'month,basicWeek,basicDay'
|
||||
right : 'month,basicWeek,listYear'
|
||||
};
|
||||
}
|
||||
|
||||
options.titleFormat = {
|
||||
month : 'MMMM YYYY',
|
||||
week : UiSettings.get('shortDateFormat'),
|
||||
day : UiSettings.get('longDateFormat')
|
||||
};
|
||||
options.views = {
|
||||
month: {
|
||||
titleFormat: 'MMMM YYYY',
|
||||
columnFormat: 'ddd'
|
||||
},
|
||||
week: {
|
||||
titleFormat: UiSettings.get('shortDateFormat'),
|
||||
columnFormat: UiSettings.get('calendarWeekColumnHeader')
|
||||
}
|
||||
|
||||
|
||||
options.columnFormat = {
|
||||
month : 'ddd',
|
||||
week : UiSettings.get('calendarWeekColumnHeader'),
|
||||
day : 'dddd'
|
||||
};
|
||||
|
||||
options.timeFormat = UiSettings.get('timeFormat');
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
var Backbone = require('backbone');
|
||||
var moment = require('moment');
|
||||
var EpisodeModel = require('../Series/EpisodeModel');
|
||||
var AlbumModel = require('../Artist/AlbumModel');
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/calendar',
|
||||
model : EpisodeModel,
|
||||
model : AlbumModel,
|
||||
|
||||
comparator : function(model1, model2) {
|
||||
var airDate1 = model1.get('airDateUtc');
|
||||
var airDate1 = model1.get('releaseDate');
|
||||
var date1 = moment(airDate1);
|
||||
var time1 = date1.unix();
|
||||
|
||||
var airDate2 = model2.get('airDateUtc');
|
||||
var airDate2 = model2.get('releaseDate');
|
||||
var date2 = moment(airDate2);
|
||||
var time2 = date2.unix();
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@ module.exports = Marionette.CollectionView.extend({
|
|||
itemView : UpcomingItemView,
|
||||
|
||||
initialize : function() {
|
||||
|
||||
this.showUnmonitored = Config.getValue('calendar.show', 'monitored') === 'all';
|
||||
|
||||
this.collection = new UpcomingCollection().bindSignalR({ updateOnly : true });
|
||||
this._fetchCollection();
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@ module.exports = Marionette.ItemView.extend({
|
|||
tagName : 'div',
|
||||
|
||||
events : {
|
||||
'click .x-episode-title' : '_showEpisodeDetails'
|
||||
'click .x-album-title' : '_showAlbumDetails'
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
var start = this.model.get('airDateUtc');
|
||||
var runtime = this.model.get('series').runtime;
|
||||
var start = this.model.get('releaseDate');
|
||||
var runtime = '30';
|
||||
var end = moment(start).add('minutes', runtime);
|
||||
|
||||
this.model.set({
|
||||
|
@ -22,7 +22,7 @@ module.exports = Marionette.ItemView.extend({
|
|||
this.listenTo(this.model, 'change', this.render);
|
||||
},
|
||||
|
||||
_showEpisodeDetails : function() {
|
||||
vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : this.model });
|
||||
_showAlbumDetails : function() {
|
||||
vent.trigger(vent.Commands.ShowAlbumDetails, { album : this.model });
|
||||
}
|
||||
});
|
|
@ -1,18 +1,18 @@
|
|||
<div class="event">
|
||||
<div class="date {{StatusLevel}}">
|
||||
<h1>{{Day airDateUtc}}</h1>
|
||||
<h4>{{Month airDateUtc}}</h4>
|
||||
<h1>{{Day releaseDate}}</h1>
|
||||
<h4>{{Month releaseDate}}</h4>
|
||||
</div>
|
||||
{{#with series}}
|
||||
{{#with artist}}
|
||||
<a href="{{route}}">
|
||||
<h4>{{title}}</h4>
|
||||
<h4>{{name}}</h4>
|
||||
</a>
|
||||
{{/with}}
|
||||
<p>{{StartTime airDateUtc}} {{#unless_today airDateUtc}}{{ShortDate airDateUtc}}{{/unless_today}}</p>
|
||||
<p>{{#unless_today releaseDate}}{{ShortDate releaseDate}}{{/unless_today}}</p>
|
||||
<p>
|
||||
<span class="episode-title x-episode-title">
|
||||
<span class="album-title x-album-title">
|
||||
{{title}}
|
||||
</span>
|
||||
<span class="pull-right">{{seasonNumber}}x{{Pad2 episodeNumber}}</span>
|
||||
<span class="pull-right">x</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -43,6 +43,10 @@
|
|||
.past {
|
||||
opacity : 0.8;
|
||||
}
|
||||
|
||||
.fc-title {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.event {
|
||||
|
@ -124,7 +128,7 @@
|
|||
border-color : grey;
|
||||
}
|
||||
|
||||
.episode-title {
|
||||
.album-title {
|
||||
.btn-link;
|
||||
.text-overflow;
|
||||
color : @link-color;
|
||||
|
|
|
@ -5,7 +5,7 @@ module.exports = NzbDroneCell.extend({
|
|||
className : 'track-title-cell',
|
||||
|
||||
events : {
|
||||
'click' : '_showDetails'
|
||||
//'click' : '_showDetails'
|
||||
},
|
||||
|
||||
render : function() {
|
||||
|
@ -21,9 +21,9 @@ module.exports = NzbDroneCell.extend({
|
|||
|
||||
_showDetails : function() {
|
||||
var hideArtistLink = this.column.get('hideArtistLink');
|
||||
vent.trigger(vent.Commands.ShowTrackDetails, {
|
||||
track : this.cellValue,
|
||||
hideArtistLink : hideArtistLink
|
||||
});
|
||||
//vent.trigger(vent.Commands.ShowTrackDetails, { //TODO Impelement Track search and screen as well as album?
|
||||
// track : this.cellValue,
|
||||
// hideArtistLink : hideArtistLink
|
||||
//});
|
||||
}
|
||||
});
|
|
@ -40,6 +40,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
.album-title-cell {
|
||||
.text-overflow();
|
||||
|
||||
color: #428bca;
|
||||
text-decoration: none;
|
||||
|
||||
&:focus, &:hover {
|
||||
color: #2a6496;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media @lg {
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
@media @md {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
@media @sm {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.air-date-cell {
|
||||
width : 120px;
|
||||
cursor: default;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*!
|
||||
* FullCalendar v2.3.2 Stylesheet
|
||||
* Docs & License: http://fullcalendar.io/
|
||||
* (c) 2015 Adam Shaw
|
||||
* FullCalendar v3.4.0 Stylesheet
|
||||
* Docs & License: https://fullcalendar.io/
|
||||
* (c) 2017 Adam Shaw
|
||||
*/
|
||||
|
||||
|
||||
|
@ -28,7 +28,10 @@ body .fc { /* extra precedence to overcome jqui */
|
|||
.fc-unthemed tbody,
|
||||
.fc-unthemed .fc-divider,
|
||||
.fc-unthemed .fc-row,
|
||||
.fc-unthemed .fc-popover {
|
||||
.fc-unthemed .fc-content, /* for gutter border */
|
||||
.fc-unthemed .fc-popover,
|
||||
.fc-unthemed .fc-list-view,
|
||||
.fc-unthemed .fc-list-heading td {
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
|
@ -37,7 +40,8 @@ body .fc { /* extra precedence to overcome jqui */
|
|||
}
|
||||
|
||||
.fc-unthemed .fc-divider,
|
||||
.fc-unthemed .fc-popover .fc-header {
|
||||
.fc-unthemed .fc-popover .fc-header,
|
||||
.fc-unthemed .fc-list-heading td {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
|
@ -45,20 +49,18 @@ body .fc { /* extra precedence to overcome jqui */
|
|||
color: #666;
|
||||
}
|
||||
|
||||
.fc-unthemed .fc-today {
|
||||
.fc-unthemed td.fc-today {
|
||||
background: #fcf8e3;
|
||||
}
|
||||
|
||||
.fc-highlight { /* when user is selecting cells */
|
||||
background: #bce8f1;
|
||||
opacity: .3;
|
||||
filter: alpha(opacity=30); /* for IE */
|
||||
}
|
||||
|
||||
.fc-bgevent { /* default look for background events */
|
||||
background: rgb(143, 223, 130);
|
||||
opacity: .3;
|
||||
filter: alpha(opacity=30); /* for IE */
|
||||
}
|
||||
|
||||
.fc-nonbusiness { /* default look for non-business-hours areas */
|
||||
|
@ -66,13 +68,21 @@ body .fc { /* extra precedence to overcome jqui */
|
|||
background: #d7d7d7;
|
||||
}
|
||||
|
||||
.fc-unthemed .fc-disabled-day {
|
||||
background: #d7d7d7;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.ui-widget .fc-disabled-day { /* themed */
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
|
||||
/* Icons (inline elements with styled text that mock arrow icons)
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-icon {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1em;
|
||||
font-size: 1em;
|
||||
|
@ -99,7 +109,6 @@ NOTE: use percentage font sizes or else old IE chokes
|
|||
|
||||
.fc-icon:after {
|
||||
position: relative;
|
||||
margin: 0 -1em; /* ensures character will be centered, regardless of width */
|
||||
}
|
||||
|
||||
.fc-icon-left-single-arrow:after {
|
||||
|
@ -107,7 +116,6 @@ NOTE: use percentage font sizes or else old IE chokes
|
|||
font-weight: bold;
|
||||
font-size: 200%;
|
||||
top: -7%;
|
||||
left: 3%;
|
||||
}
|
||||
|
||||
.fc-icon-right-single-arrow:after {
|
||||
|
@ -115,7 +123,6 @@ NOTE: use percentage font sizes or else old IE chokes
|
|||
font-weight: bold;
|
||||
font-size: 200%;
|
||||
top: -7%;
|
||||
left: -3%;
|
||||
}
|
||||
|
||||
.fc-icon-left-double-arrow:after {
|
||||
|
@ -134,14 +141,12 @@ NOTE: use percentage font sizes or else old IE chokes
|
|||
content: "\25C4";
|
||||
font-size: 125%;
|
||||
top: 3%;
|
||||
left: -2%;
|
||||
}
|
||||
|
||||
.fc-icon-right-triangle:after {
|
||||
content: "\25BA";
|
||||
font-size: 125%;
|
||||
top: 3%;
|
||||
left: 2%;
|
||||
}
|
||||
|
||||
.fc-icon-down-triangle:after {
|
||||
|
@ -252,7 +257,6 @@ NOTE: use percentage font sizes or else old IE chokes
|
|||
cursor: default;
|
||||
background-image: none;
|
||||
opacity: 0.65;
|
||||
filter: alpha(opacity=65);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
@ -372,6 +376,7 @@ hr.fc-divider {
|
|||
|
||||
.fc table {
|
||||
width: 100%;
|
||||
box-sizing: border-box; /* fix scrollbar issue in firefox */
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
|
@ -395,6 +400,18 @@ hr.fc-divider {
|
|||
}
|
||||
|
||||
|
||||
/* Internal Nav Links
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
a[data-goto] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a[data-goto]:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
/* Fake Table Rows
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -491,15 +508,15 @@ temporary rendered events).
|
|||
/* Scrolling Container
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-scroller { /* this class goes on elements for guaranteed vertical scrollbars */
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
.fc-scroller {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.fc-scroller > * { /* we expect an immediate inner element */
|
||||
/* TODO: move to agenda/basic */
|
||||
.fc-scroller > .fc-day-grid,
|
||||
.fc-scroller > .fc-time-grid {
|
||||
position: relative; /* re-scope all positions */
|
||||
width: 100%; /* hack to force re-sizing this inner element when scrollbars appear/disappear */
|
||||
overflow: hidden; /* don't let negative margins or absolute positioning create further scroll */
|
||||
}
|
||||
|
||||
|
||||
|
@ -513,10 +530,14 @@ temporary rendered events).
|
|||
line-height: 1.3;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #3a87ad; /* default BORDER color */
|
||||
background-color: #3a87ad; /* default BACKGROUND color */
|
||||
font-weight: normal; /* undo jqui's ui-widget-header bold */
|
||||
}
|
||||
|
||||
.fc-event,
|
||||
.fc-event-dot {
|
||||
background-color: #3a87ad; /* default BACKGROUND color */
|
||||
}
|
||||
|
||||
/* overpower some of bootstrap's and jqui's styles on <a> tags */
|
||||
.fc-event,
|
||||
.fc-event:hover,
|
||||
|
@ -539,7 +560,6 @@ temporary rendered events).
|
|||
z-index: 1;
|
||||
background: #fff;
|
||||
opacity: .25;
|
||||
filter: alpha(opacity=25); /* for IE */
|
||||
}
|
||||
|
||||
.fc-event .fc-content {
|
||||
|
@ -547,15 +567,68 @@ temporary rendered events).
|
|||
z-index: 2;
|
||||
}
|
||||
|
||||
/* resizer (cursor AND touch devices) */
|
||||
|
||||
.fc-event .fc-resizer {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
/* resizer (touch devices) */
|
||||
|
||||
.fc-event .fc-resizer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fc-event.fc-allow-mouse-resize .fc-resizer,
|
||||
.fc-event.fc-selected .fc-resizer {
|
||||
/* only show when hovering or selected (with touch) */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* hit area */
|
||||
|
||||
.fc-event.fc-selected .fc-resizer:before {
|
||||
/* 40x40 touch area */
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 9999; /* user of this util can scope within a lower z-index */
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-left: -20px;
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
|
||||
/* Event Selection (only for touch devices)
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-event.fc-selected {
|
||||
z-index: 9999 !important; /* overcomes inline z-index */
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.fc-event.fc-selected.fc-dragging {
|
||||
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
|
||||
/* Horizontal Events
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* bigger touch area when selected */
|
||||
.fc-h-event.fc-selected:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 3; /* below resizers */
|
||||
top: -10px;
|
||||
bottom: -10px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* events that are continuing to/from another week. kill rounded corners and butt up against edge */
|
||||
|
||||
.fc-ltr .fc-h-event.fc-not-start,
|
||||
|
@ -576,36 +649,56 @@ temporary rendered events).
|
|||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
/* resizer */
|
||||
|
||||
.fc-h-event .fc-resizer { /* positioned it to overcome the event's borders */
|
||||
top: -1px;
|
||||
bottom: -1px;
|
||||
left: -1px;
|
||||
right: -1px;
|
||||
width: 5px;
|
||||
}
|
||||
/* resizer (cursor AND touch devices) */
|
||||
|
||||
/* left resizer */
|
||||
.fc-ltr .fc-h-event .fc-start-resizer,
|
||||
.fc-ltr .fc-h-event .fc-start-resizer:before,
|
||||
.fc-ltr .fc-h-event .fc-start-resizer:after,
|
||||
.fc-rtl .fc-h-event .fc-end-resizer,
|
||||
.fc-rtl .fc-h-event .fc-end-resizer:before,
|
||||
.fc-rtl .fc-h-event .fc-end-resizer:after {
|
||||
right: auto; /* ignore the right and only use the left */
|
||||
.fc-rtl .fc-h-event .fc-end-resizer {
|
||||
cursor: w-resize;
|
||||
left: -1px; /* overcome border */
|
||||
}
|
||||
|
||||
/* right resizer */
|
||||
.fc-ltr .fc-h-event .fc-end-resizer,
|
||||
.fc-ltr .fc-h-event .fc-end-resizer:before,
|
||||
.fc-ltr .fc-h-event .fc-end-resizer:after,
|
||||
.fc-rtl .fc-h-event .fc-start-resizer,
|
||||
.fc-rtl .fc-h-event .fc-start-resizer:before,
|
||||
.fc-rtl .fc-h-event .fc-start-resizer:after {
|
||||
left: auto; /* ignore the left and only use the right */
|
||||
.fc-rtl .fc-h-event .fc-start-resizer {
|
||||
cursor: e-resize;
|
||||
right: -1px; /* overcome border */
|
||||
}
|
||||
|
||||
/* resizer (mouse devices) */
|
||||
|
||||
.fc-h-event.fc-allow-mouse-resize .fc-resizer {
|
||||
width: 7px;
|
||||
top: -1px; /* overcome top border */
|
||||
bottom: -1px; /* overcome bottom border */
|
||||
}
|
||||
|
||||
/* resizer (touch devices) */
|
||||
|
||||
.fc-h-event.fc-selected .fc-resizer {
|
||||
/* 8x8 little dot */
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-style: solid;
|
||||
border-color: inherit;
|
||||
background: #fff;
|
||||
/* vertically center */
|
||||
top: 50%;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
/* left resizer */
|
||||
.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,
|
||||
.fc-rtl .fc-h-event.fc-selected .fc-end-resizer {
|
||||
margin-left: -4px; /* centers the 8x8 dot on the left edge */
|
||||
}
|
||||
|
||||
/* right resizer */
|
||||
.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,
|
||||
.fc-rtl .fc-h-event.fc-selected .fc-start-resizer {
|
||||
margin-right: -4px; /* centers the 8x8 dot on the right edge */
|
||||
}
|
||||
|
||||
|
||||
|
@ -620,6 +713,23 @@ be a descendant of the grid when it is being dragged.
|
|||
padding: 0 1px;
|
||||
}
|
||||
|
||||
tr:first-child > td > .fc-day-grid-event {
|
||||
margin-top: 2px; /* a little bit more space before the first event */
|
||||
}
|
||||
|
||||
.fc-day-grid-event.fc-selected:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1; /* same z-index as fc-bg, behind text */
|
||||
/* overcome the borders */
|
||||
top: -1px;
|
||||
right: -1px;
|
||||
bottom: -1px;
|
||||
left: -1px;
|
||||
/* darkening effect */
|
||||
background: #000;
|
||||
opacity: .25;
|
||||
}
|
||||
|
||||
.fc-day-grid-event .fc-content { /* force events to be one-line tall */
|
||||
white-space: nowrap;
|
||||
|
@ -630,10 +740,18 @@ be a descendant of the grid when it is being dragged.
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fc-day-grid-event .fc-resizer { /* enlarge the default hit area */
|
||||
left: -3px;
|
||||
right: -3px;
|
||||
width: 7px;
|
||||
/* resizer (cursor devices) */
|
||||
|
||||
/* left resizer */
|
||||
.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,
|
||||
.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer {
|
||||
margin-left: -2px; /* to the day cell's edge */
|
||||
}
|
||||
|
||||
/* right resizer */
|
||||
.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,
|
||||
.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer {
|
||||
margin-right: -2px; /* to the day cell's edge */
|
||||
}
|
||||
|
||||
|
||||
|
@ -672,14 +790,46 @@ a.fc-more:hover {
|
|||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* Now Indicator
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-now-indicator {
|
||||
position: absolute;
|
||||
border: 0 solid red;
|
||||
}
|
||||
|
||||
|
||||
/* Utilities
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-unselectable {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Toolbar
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-toolbar {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fc-toolbar.fc-header-toolbar {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.fc-toolbar.fc-footer-toolbar {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.fc-toolbar .fc-left {
|
||||
float: left;
|
||||
}
|
||||
|
@ -753,6 +903,8 @@ a.fc-more:hover {
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* BasicView
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -760,8 +912,7 @@ a.fc-more:hover {
|
|||
|
||||
.fc-basicWeek-view .fc-content-skeleton,
|
||||
.fc-basicDay-view .fc-content-skeleton {
|
||||
/* we are sure there are no day numbers in these views, so... */
|
||||
padding-top: 1px; /* add a pixel to make sure there are 2px padding above events */
|
||||
/* there may be week numbers in these views, so no padding-top */
|
||||
padding-bottom: 1em; /* ensure a space at bottom of cell for user selecting/clicking */
|
||||
}
|
||||
|
||||
|
@ -784,42 +935,45 @@ a.fc-more:hover {
|
|||
|
||||
/* week and day number styling */
|
||||
|
||||
.fc-day-top.fc-other-month {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.fc-basic-view .fc-week-number,
|
||||
.fc-basic-view .fc-day-number {
|
||||
padding: 0 2px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.fc-basic-view td.fc-week-number span,
|
||||
.fc-basic-view td.fc-day-number {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
.fc-basic-view th.fc-week-number,
|
||||
.fc-basic-view th.fc-day-number {
|
||||
padding: 0 2px; /* column headers can't have as much v space */
|
||||
}
|
||||
|
||||
.fc-basic-view .fc-week-number {
|
||||
.fc-ltr .fc-basic-view .fc-day-top .fc-day-number { float: right; }
|
||||
.fc-rtl .fc-basic-view .fc-day-top .fc-day-number { float: left; }
|
||||
|
||||
.fc-ltr .fc-basic-view .fc-day-top .fc-week-number { float: left; border-radius: 0 0 3px 0; }
|
||||
.fc-rtl .fc-basic-view .fc-day-top .fc-week-number { float: right; border-radius: 0 0 0 3px; }
|
||||
|
||||
.fc-basic-view .fc-day-top .fc-week-number {
|
||||
min-width: 1.5em;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
/* when week/day number have own column */
|
||||
|
||||
.fc-basic-view td.fc-week-number {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fc-basic-view .fc-week-number span {
|
||||
.fc-basic-view td.fc-week-number > * {
|
||||
/* work around the way we do column resizing and ensure a minimum width */
|
||||
display: inline-block;
|
||||
min-width: 1.25em;
|
||||
}
|
||||
|
||||
.fc-ltr .fc-basic-view .fc-day-number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.fc-rtl .fc-basic-view .fc-day-number {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.fc-day-number.fc-other-month {
|
||||
opacity: 0.3;
|
||||
filter: alpha(opacity=30); /* for IE */
|
||||
/* opacity with small font can sometimes look too faded
|
||||
might want to set the 'color' property instead
|
||||
making day-numbers bold also fixes the problem */
|
||||
}
|
||||
|
||||
/* AgendaView all-day area
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
@ -834,7 +988,6 @@ a.fc-more:hover {
|
|||
}
|
||||
|
||||
.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton {
|
||||
padding-top: 1px; /* add a pixel to make sure there are 2px padding above events */
|
||||
padding-bottom: 1em; /* give space underneath events for clicking/selecting days */
|
||||
}
|
||||
|
||||
|
@ -888,27 +1041,46 @@ a.fc-more:hover {
|
|||
z-index: 2;
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-bgevent-skeleton,
|
||||
.fc-time-grid .fc-content-col {
|
||||
position: relative; /* because now-indicator lives directly inside */
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-content-skeleton {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-bgevent-skeleton {
|
||||
/* divs within a cell within the fc-content-skeleton */
|
||||
|
||||
.fc-time-grid .fc-business-container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-bgevent-container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-highlight-container {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-highlight-skeleton {
|
||||
.fc-time-grid .fc-event-container {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-content-skeleton {
|
||||
.fc-time-grid .fc-now-indicator-line {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.fc-time-grid .fc-helper-skeleton {
|
||||
.fc-time-grid .fc-helper-container { /* also is fc-event-container */
|
||||
position: relative;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
|
@ -948,11 +1120,6 @@ a.fc-more:hover {
|
|||
/* TimeGrid Event Containment
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-time-grid .fc-event-container, /* a div within a cell within the fc-content-skeleton */
|
||||
.fc-time-grid .fc-bgevent-container { /* a div within a cell within the fc-bgevent-skeleton */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fc-ltr .fc-time-grid .fc-event-container { /* space on the sides of events for LTR (default) */
|
||||
margin: 0 2.5% 0 2px;
|
||||
}
|
||||
|
@ -1008,6 +1175,20 @@ be a descendant of the grid when it is being dragged.
|
|||
overflow: hidden; /* don't let the bg flow over rounded corners */
|
||||
}
|
||||
|
||||
.fc-time-grid-event.fc-selected {
|
||||
/* need to allow touch resizers to extend outside event's bounding box */
|
||||
/* common fc-selected styles hide the fc-bg, so don't need this anyway */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.fc-time-grid-event.fc-selected .fc-bg {
|
||||
display: none; /* hide semi-white background, to appear darker */
|
||||
}
|
||||
|
||||
.fc-time-grid-event .fc-content {
|
||||
overflow: hidden; /* for when .fc-selected */
|
||||
}
|
||||
|
||||
.fc-time-grid-event .fc-time,
|
||||
.fc-time-grid-event .fc-title {
|
||||
padding: 0 1px;
|
||||
|
@ -1049,9 +1230,9 @@ be a descendant of the grid when it is being dragged.
|
|||
padding: 0; /* undo padding from above */
|
||||
}
|
||||
|
||||
/* resizer */
|
||||
/* resizer (cursor device) */
|
||||
|
||||
.fc-time-grid-event .fc-resizer {
|
||||
.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
@ -1064,6 +1245,169 @@ be a descendant of the grid when it is being dragged.
|
|||
cursor: s-resize;
|
||||
}
|
||||
|
||||
.fc-time-grid-event .fc-resizer:after {
|
||||
.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after {
|
||||
content: "=";
|
||||
}
|
||||
|
||||
/* resizer (touch device) */
|
||||
|
||||
.fc-time-grid-event.fc-selected .fc-resizer {
|
||||
/* 10x10 dot */
|
||||
border-radius: 5px;
|
||||
border-width: 1px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-style: solid;
|
||||
border-color: inherit;
|
||||
background: #fff;
|
||||
/* horizontally center */
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
/* center on the bottom edge */
|
||||
bottom: -5px;
|
||||
}
|
||||
|
||||
|
||||
/* Now Indicator
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-time-grid .fc-now-indicator-line {
|
||||
border-top-width: 1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* arrow on axis */
|
||||
|
||||
.fc-time-grid .fc-now-indicator-arrow {
|
||||
margin-top: -5px; /* vertically center on top coordinate */
|
||||
}
|
||||
|
||||
.fc-ltr .fc-time-grid .fc-now-indicator-arrow {
|
||||
left: 0;
|
||||
/* triangle pointing right... */
|
||||
border-width: 5px 0 5px 6px;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.fc-rtl .fc-time-grid .fc-now-indicator-arrow {
|
||||
right: 0;
|
||||
/* triangle pointing left... */
|
||||
border-width: 5px 6px 5px 0;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* List View
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* possibly reusable */
|
||||
|
||||
.fc-event-dot {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* view wrapper */
|
||||
|
||||
.fc-rtl .fc-list-view {
|
||||
direction: rtl; /* unlike core views, leverage browser RTL */
|
||||
}
|
||||
|
||||
.fc-list-view {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
/* table resets */
|
||||
|
||||
.fc .fc-list-table {
|
||||
table-layout: auto; /* for shrinkwrapping cell content */
|
||||
}
|
||||
|
||||
.fc-list-table td {
|
||||
border-width: 1px 0 0;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.fc-list-table tr:first-child td {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
/* day headings with the list */
|
||||
|
||||
.fc-list-heading {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.fc-list-heading td {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fc-ltr .fc-list-heading-main { float: left; }
|
||||
.fc-ltr .fc-list-heading-alt { float: right; }
|
||||
|
||||
.fc-rtl .fc-list-heading-main { float: right; }
|
||||
.fc-rtl .fc-list-heading-alt { float: left; }
|
||||
|
||||
/* event list items */
|
||||
|
||||
.fc-list-item.fc-has-url {
|
||||
cursor: pointer; /* whole row will be clickable */
|
||||
}
|
||||
|
||||
.fc-list-item:hover td {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.fc-list-item-marker,
|
||||
.fc-list-item-time {
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* make the dot closer to the event title */
|
||||
.fc-ltr .fc-list-item-marker { padding-right: 0; }
|
||||
.fc-rtl .fc-list-item-marker { padding-left: 0; }
|
||||
|
||||
.fc-list-item-title a {
|
||||
/* every event title cell has an <a> tag */
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.fc-list-item-title a[href]:hover {
|
||||
/* hover effect only on titles with hrefs */
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* message when no events */
|
||||
|
||||
.fc-list-empty-wrap2 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.fc-list-empty-wrap1 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.fc-list-empty {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fc-unthemed .fc-list-empty { /* theme will provide own background */
|
||||
background-color: #eee;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,36 @@ Handlebars.registerHelper('cover', function() {
|
|||
return new Handlebars.SafeString('<img class="album-cover placeholder-image" src="{0}">'.format(placeholder));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('StatusLevel', function() {
|
||||
var hasFile = false; //this.hasFile; #TODO Refactor for Albums
|
||||
var downloading = false; //require('../../Activity/Queue/QueueCollection').findEpisode(this.id) || this.downloading; #TODO Queue Refactor for Albums
|
||||
var currentTime = moment();
|
||||
var start = moment(this.releaseDate);
|
||||
var end = moment(this.end);
|
||||
var monitored = this.artist.monitored && this.monitored;
|
||||
|
||||
if (hasFile) {
|
||||
return 'success';
|
||||
}
|
||||
|
||||
if (downloading) {
|
||||
return 'purple';
|
||||
}
|
||||
|
||||
else if (!monitored) {
|
||||
return 'unmonitored';
|
||||
}
|
||||
|
||||
if (currentTime.isAfter(start) && currentTime.isBefore(end)) {
|
||||
return 'warning';
|
||||
}
|
||||
|
||||
if (start.isBefore(currentTime) && !hasFile) {
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
return 'primary';
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('MBAlbumUrl', function() {
|
||||
return 'https://musicbrainz.org/release-group/' + this.mbId;
|
||||
|
|
|
@ -14,40 +14,7 @@ Handlebars.registerHelper('EpisodeNumber', function() {
|
|||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,7 @@ var Marionette = require('marionette');
|
|||
var EditArtistView = require('../../Artist/Edit/EditArtistView');
|
||||
var DeleteArtistView = require('../../Artist/Delete/DeleteArtistView');
|
||||
var EpisodeDetailsLayout = require('../../Episode/EpisodeDetailsLayout');
|
||||
var AlbumDetailsLayout = require('../../Album/AlbumDetailsLayout');
|
||||
var HistoryDetailsLayout = require('../../Activity/History/Details/HistoryDetailsLayout');
|
||||
var LogDetailsView = require('../../System/Logs/Table/Details/LogDetailsView');
|
||||
var RenamePreviewLayout = require('../../Rename/RenamePreviewLayout');
|
||||
|
@ -19,6 +20,7 @@ module.exports = Marionette.AppRouter.extend({
|
|||
vent.on(vent.Commands.EditArtistCommand, this._editArtist, this);
|
||||
vent.on(vent.Commands.DeleteArtistCommand, this._deleteArtist, this);
|
||||
vent.on(vent.Commands.ShowEpisodeDetails, this._showEpisode, this);
|
||||
vent.on(vent.Commands.ShowAlbumDetails, this._showAlbum, this);
|
||||
vent.on(vent.Commands.ShowHistoryDetails, this._showHistory, this);
|
||||
vent.on(vent.Commands.ShowLogDetails, this._showLogDetails, this);
|
||||
vent.on(vent.Commands.ShowRenamePreview, this._showRenamePreview, this);
|
||||
|
@ -62,6 +64,13 @@ module.exports = Marionette.AppRouter.extend({
|
|||
AppLayout.modalRegion.show(view);
|
||||
},
|
||||
|
||||
_showAlbum : function(options) {
|
||||
var view = new AlbumDetailsLayout({
|
||||
model : options.album
|
||||
});
|
||||
AppLayout.modalRegion.show(view);
|
||||
},
|
||||
|
||||
_showHistory : function(options) {
|
||||
var view = new HistoryDetailsLayout({ model : options.model });
|
||||
AppLayout.modalRegion.show(view);
|
||||
|
|
|
@ -20,6 +20,7 @@ vent.Commands = {
|
|||
OpenModal2Command : 'OpenModal2Command',
|
||||
CloseModal2Command : 'CloseModal2Command',
|
||||
ShowEpisodeDetails : 'ShowEpisodeDetails',
|
||||
ShowAlbumDetails : 'ShowAlbumDetails',
|
||||
ShowHistoryDetails : 'ShowHistoryDetails',
|
||||
ShowLogDetails : 'ShowLogDetails',
|
||||
SaveSettings : 'saveSettings',
|
||||
|
|
Loading…
Reference in New Issue