mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 13:10:13 -07:00
Many UI and API Improvements (#8)
This fixes and implements many items on the ArtistIndex Page and ArtistDetailPage * Create ArtistStatistics Core Module and tie into API. * Create Members Class and tie into ArtistModel and Artist API resource. * Finish Out Album API resources and pass to ArtistDetailPage. * Finish Out Track and TrackFile API resources and pass to ArtistDetailPage. * Lots of UI work on Artist Detail Page to get Albums and Track list working. * Add Cover and Disc Image Types to MediaCover Class * Remove AddSeries UI Flow, since we have replaced with AddArtist (Cleanup)
This commit is contained in:
parent
2c7398ac66
commit
d8ea0a3243
86 changed files with 1293 additions and 1663 deletions
|
@ -16,10 +16,10 @@ gulp.task('less', function() {
|
||||||
paths.src.content + 'bootstrap.less',
|
paths.src.content + 'bootstrap.less',
|
||||||
paths.src.content + 'theme.less',
|
paths.src.content + 'theme.less',
|
||||||
paths.src.content + 'overrides.less',
|
paths.src.content + 'overrides.less',
|
||||||
paths.src.root + 'Series/series.less',
|
//paths.src.root + 'Series/series.less',
|
||||||
paths.src.root + 'Artist/artist.less',
|
paths.src.root + 'Artist/artist.less',
|
||||||
paths.src.root + 'Activity/activity.less',
|
paths.src.root + 'Activity/activity.less',
|
||||||
paths.src.root + 'AddSeries/addSeries.less',
|
//paths.src.root + 'AddSeries/addSeries.less',
|
||||||
paths.src.root + 'AddArtist/addArtist.less',
|
paths.src.root + 'AddArtist/addArtist.less',
|
||||||
paths.src.root + 'Calendar/calendar.less',
|
paths.src.root + 'Calendar/calendar.less',
|
||||||
paths.src.root + 'Cells/cells.less',
|
paths.src.root + 'Cells/cells.less',
|
||||||
|
|
40
src/NzbDrone.Api/Albums/AlbumModule.cs
Normal file
40
src/NzbDrone.Api/Albums/AlbumModule.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Api.REST;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using NzbDrone.Core.DecisionEngine;
|
||||||
|
using NzbDrone.SignalR;
|
||||||
|
|
||||||
|
namespace NzbDrone.Api.Albums
|
||||||
|
{
|
||||||
|
public class AlbumModule : AlbumModuleWithSignalR
|
||||||
|
{
|
||||||
|
public AlbumModule(IArtistService artistService,
|
||||||
|
IAlbumService albumService,
|
||||||
|
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||||
|
IBroadcastSignalRMessage signalRBroadcaster)
|
||||||
|
: base(albumService, artistService, qualityUpgradableSpecification, signalRBroadcaster)
|
||||||
|
{
|
||||||
|
GetResourceAll = GetAlbums;
|
||||||
|
UpdateResource = SetMonitored;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AlbumResource> GetAlbums()
|
||||||
|
{
|
||||||
|
if (!Request.Query.ArtistId.HasValue)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("artistId is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
var artistId = (int)Request.Query.ArtistId;
|
||||||
|
|
||||||
|
var resources = MapToResource(_albumService.GetAlbumsByArtist(artistId), false);
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetMonitored(AlbumResource albumResource)
|
||||||
|
{
|
||||||
|
_albumService.SetAlbumMonitored(albumResource.Id, albumResource.Monitored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
118
src/NzbDrone.Api/Albums/AlbumModuleWithSignalR.cs
Normal file
118
src/NzbDrone.Api/Albums/AlbumModuleWithSignalR.cs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Api.TrackFiles;
|
||||||
|
using NzbDrone.Api.Music;
|
||||||
|
using NzbDrone.Core.Datastore.Events;
|
||||||
|
using NzbDrone.Core.DecisionEngine;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using NzbDrone.SignalR;
|
||||||
|
|
||||||
|
namespace NzbDrone.Api.Albums
|
||||||
|
{
|
||||||
|
public abstract class AlbumModuleWithSignalR : NzbDroneRestModuleWithSignalR<AlbumResource, Track>
|
||||||
|
{
|
||||||
|
protected readonly IAlbumService _albumService;
|
||||||
|
protected readonly IArtistService _artistService;
|
||||||
|
protected readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
|
||||||
|
|
||||||
|
protected AlbumModuleWithSignalR(IAlbumService albumService,
|
||||||
|
IArtistService artistService,
|
||||||
|
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||||
|
IBroadcastSignalRMessage signalRBroadcaster)
|
||||||
|
: base(signalRBroadcaster)
|
||||||
|
{
|
||||||
|
_albumService = albumService;
|
||||||
|
_artistService = artistService;
|
||||||
|
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||||
|
|
||||||
|
GetResourceById = GetAlbum;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AlbumModuleWithSignalR(IAlbumService albumService,
|
||||||
|
IArtistService artistService,
|
||||||
|
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||||
|
IBroadcastSignalRMessage signalRBroadcaster,
|
||||||
|
string resource)
|
||||||
|
: base(signalRBroadcaster, resource)
|
||||||
|
{
|
||||||
|
_albumService = albumService;
|
||||||
|
_artistService = artistService;
|
||||||
|
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||||
|
|
||||||
|
GetResourceById = GetAlbum;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AlbumResource GetAlbum(int id)
|
||||||
|
{
|
||||||
|
var album = _albumService.GetAlbum(id);
|
||||||
|
var resource = MapToResource(album, true);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AlbumResource MapToResource(Album album, bool includeArtist)
|
||||||
|
{
|
||||||
|
var resource = album.ToResource();
|
||||||
|
|
||||||
|
if (includeArtist)
|
||||||
|
{
|
||||||
|
var artist = album.Artist ?? _artistService.GetArtist(album.ArtistId);
|
||||||
|
|
||||||
|
if (includeArtist)
|
||||||
|
{
|
||||||
|
resource.Artist = artist.ToResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<AlbumResource> MapToResource(List<Album> albums, bool includeArtist)
|
||||||
|
{
|
||||||
|
var result = albums.ToResource();
|
||||||
|
|
||||||
|
if (includeArtist)
|
||||||
|
{
|
||||||
|
var artistDict = new Dictionary<int, Core.Music.Artist>();
|
||||||
|
for (var i = 0; i < albums.Count; i++)
|
||||||
|
{
|
||||||
|
var album = albums[i];
|
||||||
|
var resource = result[i];
|
||||||
|
|
||||||
|
var artist = album.Artist ?? artistDict.GetValueOrDefault(albums[i].ArtistId) ?? _artistService.GetArtist(albums[i].ArtistId);
|
||||||
|
artistDict[artist.Id] = artist;
|
||||||
|
|
||||||
|
if (includeArtist)
|
||||||
|
{
|
||||||
|
resource.Artist = artist.ToResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Implement Track or Album Grabbed/Dowloaded Events
|
||||||
|
|
||||||
|
//public void Handle(TrackGrabbedEvent message)
|
||||||
|
//{
|
||||||
|
// foreach (var track in message.Track.Tracks)
|
||||||
|
// {
|
||||||
|
// var resource = track.ToResource();
|
||||||
|
// resource.Grabbed = true;
|
||||||
|
|
||||||
|
// BroadcastResourceChange(ModelAction.Updated, resource);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public void Handle(TrackDownloadedEvent message)
|
||||||
|
//{
|
||||||
|
// foreach (var album in message.Album.Albums)
|
||||||
|
// {
|
||||||
|
// BroadcastResourceChange(ModelAction.Updated, album.Id);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,33 +3,47 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using NzbDrone.Api.REST;
|
||||||
|
using NzbDrone.Api.Music;
|
||||||
|
using NzbDrone.Core.MediaCover;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Music
|
namespace NzbDrone.Api.Albums
|
||||||
{
|
{
|
||||||
public class AlbumResource
|
public class AlbumResource : RestResource
|
||||||
{
|
{
|
||||||
public string AlbumId { get; set; }
|
|
||||||
public string AlbumName { get; set; }
|
public string Title { get; set; }
|
||||||
|
public string Label { get; set; }
|
||||||
public bool Monitored { 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 List<string> Genres { get; set; }
|
||||||
public string ArtworkUrl { get; set; }
|
public ArtistResource Artist { get; set; }
|
||||||
|
public List<MediaCover> Images { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AlbumResourceMapper
|
public static class AlbumResourceMapper
|
||||||
{
|
{
|
||||||
public static AlbumResource ToResource(this Album model)
|
public static AlbumResource ToResource(this Core.Music.Album model)
|
||||||
{
|
{
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
|
|
||||||
return new AlbumResource
|
return new AlbumResource
|
||||||
{
|
{
|
||||||
AlbumId = model.ForeignAlbumId,
|
Id = model.Id,
|
||||||
|
Label = model.Label,
|
||||||
|
Path = model.Path,
|
||||||
|
ProfileId = model.ProfileId,
|
||||||
Monitored = model.Monitored,
|
Monitored = model.Monitored,
|
||||||
ReleaseDate = model.ReleaseDate,
|
ReleaseDate = model.ReleaseDate,
|
||||||
AlbumName = model.Title,
|
|
||||||
Genres = model.Genres,
|
Genres = model.Genres,
|
||||||
|
Title = model.Title,
|
||||||
|
Images = model.Images,
|
||||||
|
Ratings = model.Ratings,
|
||||||
|
//Genres = model.Genres,
|
||||||
//ArtworkUrl = model.ArtworkUrl
|
//ArtworkUrl = model.ArtworkUrl
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -38,13 +52,19 @@ namespace NzbDrone.Api.Music
|
||||||
{
|
{
|
||||||
if (resource == null) return null;
|
if (resource == null) return null;
|
||||||
|
|
||||||
return new Album
|
return new Core.Music.Album
|
||||||
{
|
{
|
||||||
ForeignAlbumId = resource.AlbumId,
|
Id = resource.Id,
|
||||||
|
Label = resource.Label,
|
||||||
|
Path = resource.Path,
|
||||||
Monitored = resource.Monitored,
|
Monitored = resource.Monitored,
|
||||||
|
ProfileId = resource.ProfileId,
|
||||||
ReleaseDate = resource.ReleaseDate,
|
ReleaseDate = resource.ReleaseDate,
|
||||||
Title = resource.AlbumName,
|
|
||||||
Genres = resource.Genres,
|
Genres = resource.Genres,
|
||||||
|
Title = resource.Title,
|
||||||
|
Images = resource.Images,
|
||||||
|
Ratings = resource.Ratings,
|
||||||
|
//Genres = resource.Genres,
|
||||||
//ArtworkUrl = resource.ArtworkUrl
|
//ArtworkUrl = resource.ArtworkUrl
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
using FluentValidation;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Datastore.Events;
|
using NzbDrone.Core.Datastore.Events;
|
||||||
using NzbDrone.Core.MediaCover;
|
using NzbDrone.Core.MediaCover;
|
||||||
|
@ -7,12 +10,10 @@ using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Core.Music.Events;
|
using NzbDrone.Core.Music.Events;
|
||||||
using NzbDrone.Core.SeriesStats;
|
using NzbDrone.Core.ArtistStats;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
using NzbDrone.SignalR;
|
using NzbDrone.SignalR;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Music
|
namespace NzbDrone.Api.Music
|
||||||
{
|
{
|
||||||
|
@ -26,14 +27,14 @@ namespace NzbDrone.Api.Music
|
||||||
//IHandle<MediaCoversUpdatedEvent>
|
//IHandle<MediaCoversUpdatedEvent>
|
||||||
{
|
{
|
||||||
private readonly IArtistService _artistService;
|
private readonly IArtistService _artistService;
|
||||||
private readonly IAddArtistService _addSeriesService;
|
private readonly IAddArtistService _addArtistService;
|
||||||
private readonly ISeriesStatisticsService _seriesStatisticsService;
|
private readonly IArtistStatisticsService _artistStatisticsService;
|
||||||
private readonly IMapCoversToLocal _coverMapper;
|
private readonly IMapCoversToLocal _coverMapper;
|
||||||
|
|
||||||
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
|
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||||
IArtistService artistService,
|
IArtistService artistService,
|
||||||
IAddArtistService addSeriesService,
|
IAddArtistService addArtistService,
|
||||||
ISeriesStatisticsService seriesStatisticsService,
|
IArtistStatisticsService artistStatisticsService,
|
||||||
IMapCoversToLocal coverMapper,
|
IMapCoversToLocal coverMapper,
|
||||||
RootFolderValidator rootFolderValidator,
|
RootFolderValidator rootFolderValidator,
|
||||||
ArtistPathValidator seriesPathValidator,
|
ArtistPathValidator seriesPathValidator,
|
||||||
|
@ -45,8 +46,8 @@ namespace NzbDrone.Api.Music
|
||||||
: base(signalRBroadcaster)
|
: base(signalRBroadcaster)
|
||||||
{
|
{
|
||||||
_artistService = artistService;
|
_artistService = artistService;
|
||||||
_addSeriesService = addSeriesService;
|
_addArtistService = addArtistService;
|
||||||
_seriesStatisticsService = seriesStatisticsService;
|
_artistStatisticsService = artistStatisticsService;
|
||||||
|
|
||||||
_coverMapper = coverMapper;
|
_coverMapper = coverMapper;
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ namespace NzbDrone.Api.Music
|
||||||
|
|
||||||
var resource = artist.ToResource();
|
var resource = artist.ToResource();
|
||||||
MapCoversToLocal(resource);
|
MapCoversToLocal(resource);
|
||||||
//FetchAndLinkSeriesStatistics(resource);
|
FetchAndLinkArtistStatistics(resource);
|
||||||
//PopulateAlternateTitles(resource);
|
//PopulateAlternateTitles(resource);
|
||||||
|
|
||||||
return resource;
|
return resource;
|
||||||
|
@ -96,10 +97,11 @@ namespace NzbDrone.Api.Music
|
||||||
|
|
||||||
private List<ArtistResource> AllArtist()
|
private List<ArtistResource> AllArtist()
|
||||||
{
|
{
|
||||||
//var seriesStats = _seriesStatisticsService.SeriesStatistics();
|
var artistStats = _artistStatisticsService.ArtistStatistics();
|
||||||
var artistResources = _artistService.GetAllArtists().ToResource();
|
var artistResources = _artistService.GetAllArtists().ToResource();
|
||||||
|
|
||||||
MapCoversToLocal(artistResources.ToArray());
|
MapCoversToLocal(artistResources.ToArray());
|
||||||
//LinkSeriesStatistics(seriesResources, seriesStats);
|
LinkArtistStatistics(artistResources, artistStats);
|
||||||
//PopulateAlternateTitles(seriesResources);
|
//PopulateAlternateTitles(seriesResources);
|
||||||
|
|
||||||
return artistResources;
|
return artistResources;
|
||||||
|
@ -109,7 +111,7 @@ namespace NzbDrone.Api.Music
|
||||||
{
|
{
|
||||||
var model = artistResource.ToModel();
|
var model = artistResource.ToModel();
|
||||||
|
|
||||||
return _addSeriesService.AddArtist(model).Id;
|
return _addArtistService.AddArtist(model).Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatArtist(ArtistResource artistResource)
|
private void UpdatArtist(ArtistResource artistResource)
|
||||||
|
@ -142,6 +144,44 @@ namespace NzbDrone.Api.Music
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FetchAndLinkArtistStatistics(ArtistResource resource)
|
||||||
|
{
|
||||||
|
LinkArtistStatistics(resource, _artistStatisticsService.ArtistStatistics(resource.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void LinkArtistStatistics(List<ArtistResource> resources, List<ArtistStatistics> artistStatistics)
|
||||||
|
{
|
||||||
|
var dictArtistStats = artistStatistics.ToDictionary(v => v.ArtistId);
|
||||||
|
|
||||||
|
foreach (var artist in resources)
|
||||||
|
{
|
||||||
|
var stats = dictArtistStats.GetValueOrDefault(artist.Id);
|
||||||
|
if (stats == null) continue;
|
||||||
|
|
||||||
|
LinkArtistStatistics(artist, stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LinkArtistStatistics(ArtistResource resource, ArtistStatistics artistStatistics)
|
||||||
|
{
|
||||||
|
resource.TotalTrackCount = artistStatistics.TotalTrackCount;
|
||||||
|
resource.TrackCount = artistStatistics.TrackCount;
|
||||||
|
resource.TrackFileCount = artistStatistics.TrackFileCount;
|
||||||
|
resource.SizeOnDisk = artistStatistics.SizeOnDisk;
|
||||||
|
resource.AlbumCount = artistStatistics.AlbumCount;
|
||||||
|
|
||||||
|
//if (artistStatistics.AlbumStatistics != null)
|
||||||
|
//{
|
||||||
|
// var dictSeasonStats = artistStatistics.SeasonStatistics.ToDictionary(v => v.SeasonNumber);
|
||||||
|
|
||||||
|
// foreach (var album in resource.Albums)
|
||||||
|
// {
|
||||||
|
// album.Statistics = dictSeasonStats.GetValueOrDefault(album.Id).ToResource();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
public void Handle(TrackImportedEvent message)
|
public void Handle(TrackImportedEvent message)
|
||||||
{
|
{
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.ImportedTrack.Id); // TODO: Ensure we can pass DB ID instead of Metadata ID (SpotifyID)
|
BroadcastResourceChange(ModelAction.Updated, message.ImportedTrack.Id); // TODO: Ensure we can pass DB ID instead of Metadata ID (SpotifyID)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Api.Series;
|
using NzbDrone.Api.Series;
|
||||||
|
using NzbDrone.Api.Albums;
|
||||||
using NzbDrone.Core.MediaCover;
|
using NzbDrone.Core.MediaCover;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -23,19 +24,10 @@ namespace NzbDrone.Api.Music
|
||||||
public string MBId { get; set; }
|
public string MBId { get; set; }
|
||||||
public int TADBId { get; set; }
|
public int TADBId { get; set; }
|
||||||
public int DiscogsId { get; set; }
|
public int DiscogsId { get; set; }
|
||||||
public string AMId { get; set; }
|
public string AllMusicId { get; set; }
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
|
|
||||||
public int AlbumCount
|
public int? AlbumCount{ get; set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Albums == null) return 0;
|
|
||||||
|
|
||||||
return Albums.Where(s => s.AlbumId != "").Count(); // TODO: CHeck this condition
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? TotalTrackCount { get; set; }
|
public int? TotalTrackCount { get; set; }
|
||||||
public int? TrackCount { get; set; }
|
public int? TrackCount { get; set; }
|
||||||
public int? TrackFileCount { get; set; }
|
public int? TrackFileCount { get; set; }
|
||||||
|
@ -43,6 +35,7 @@ namespace NzbDrone.Api.Music
|
||||||
//public SeriesStatusType Status { get; set; }
|
//public SeriesStatusType Status { get; set; }
|
||||||
|
|
||||||
public List<MediaCover> Images { get; set; }
|
public List<MediaCover> Images { get; set; }
|
||||||
|
public List<Member> Members { get; set; }
|
||||||
|
|
||||||
public string RemotePoster { get; set; }
|
public string RemotePoster { get; set; }
|
||||||
public List<AlbumResource> Albums { get; set; }
|
public List<AlbumResource> Albums { get; set; }
|
||||||
|
@ -61,7 +54,7 @@ namespace NzbDrone.Api.Music
|
||||||
public List<string> Genres { get; set; }
|
public List<string> Genres { get; set; }
|
||||||
public HashSet<int> Tags { get; set; }
|
public HashSet<int> Tags { get; set; }
|
||||||
public DateTime Added { get; set; }
|
public DateTime Added { get; set; }
|
||||||
public AddSeriesOptions AddOptions { get; set; }
|
public AddArtistOptions AddOptions { get; set; }
|
||||||
public Ratings Ratings { get; set; }
|
public Ratings Ratings { get; set; }
|
||||||
public string NameSlug { get; set; }
|
public string NameSlug { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -78,14 +71,14 @@ namespace NzbDrone.Api.Music
|
||||||
MBId = model.MBId,
|
MBId = model.MBId,
|
||||||
TADBId = model.TADBId,
|
TADBId = model.TADBId,
|
||||||
DiscogsId = model.DiscogsId,
|
DiscogsId = model.DiscogsId,
|
||||||
AMId = model.AMId,
|
AllMusicId = model.AMId,
|
||||||
Name = model.Name,
|
Name = model.Name,
|
||||||
//AlternateTitles
|
//AlternateTitles
|
||||||
//SortTitle = resource.SortTitle,
|
//SortTitle = resource.SortTitle,
|
||||||
|
|
||||||
//TotalEpisodeCount
|
//TotalTrackCount
|
||||||
//EpisodeCount
|
//TrackCount
|
||||||
//EpisodeFileCount
|
//TrackFileCount
|
||||||
//SizeOnDisk
|
//SizeOnDisk
|
||||||
//Status = resource.Status,
|
//Status = resource.Status,
|
||||||
Overview = model.Overview,
|
Overview = model.Overview,
|
||||||
|
@ -94,6 +87,7 @@ namespace NzbDrone.Api.Music
|
||||||
//Network = resource.Network,
|
//Network = resource.Network,
|
||||||
//AirTime = resource.AirTime,
|
//AirTime = resource.AirTime,
|
||||||
Images = model.Images,
|
Images = model.Images,
|
||||||
|
Members = model.Members,
|
||||||
|
|
||||||
Albums = model.Albums.ToResource(),
|
Albums = model.Albums.ToResource(),
|
||||||
//Year = resource.Year,
|
//Year = resource.Year,
|
||||||
|
@ -119,7 +113,7 @@ namespace NzbDrone.Api.Music
|
||||||
Tags = model.Tags,
|
Tags = model.Tags,
|
||||||
Added = model.Added,
|
Added = model.Added,
|
||||||
AddOptions = model.AddOptions,
|
AddOptions = model.AddOptions,
|
||||||
//Ratings = resource.Ratings
|
Ratings = model.Ratings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,10 +131,10 @@ namespace NzbDrone.Api.Music
|
||||||
MBId = resource.MBId,
|
MBId = resource.MBId,
|
||||||
TADBId = resource.TADBId,
|
TADBId = resource.TADBId,
|
||||||
DiscogsId = resource.DiscogsId,
|
DiscogsId = resource.DiscogsId,
|
||||||
AMId = resource.AMId,
|
AMId = resource.AllMusicId, //TODO change model and DB to AllMusic instead of AM
|
||||||
//TotalEpisodeCount
|
//TotalEpisodeCount
|
||||||
//EpisodeCount
|
//TrackCount
|
||||||
//EpisodeFileCount
|
//TrackFileCount
|
||||||
//SizeOnDisk
|
//SizeOnDisk
|
||||||
//Status = resource.Status,
|
//Status = resource.Status,
|
||||||
Overview = resource.Overview,
|
Overview = resource.Overview,
|
||||||
|
@ -149,6 +143,7 @@ namespace NzbDrone.Api.Music
|
||||||
//Network = resource.Network,
|
//Network = resource.Network,
|
||||||
//AirTime = resource.AirTime,
|
//AirTime = resource.AirTime,
|
||||||
Images = resource.Images,
|
Images = resource.Images,
|
||||||
|
Members = resource.Members,
|
||||||
|
|
||||||
Albums = resource.Albums.ToModel(),
|
Albums = resource.Albums.ToModel(),
|
||||||
//Year = resource.Year,
|
//Year = resource.Year,
|
||||||
|
@ -166,7 +161,7 @@ namespace NzbDrone.Api.Music
|
||||||
Tags = resource.Tags,
|
Tags = resource.Tags,
|
||||||
Added = resource.Added,
|
Added = resource.Added,
|
||||||
AddOptions = resource.AddOptions,
|
AddOptions = resource.AddOptions,
|
||||||
//Ratings = resource.Ratings
|
Ratings = resource.Ratings
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
<Compile Include="..\NzbDrone.Common\Properties\SharedAssemblyInfo.cs">
|
<Compile Include="..\NzbDrone.Common\Properties\SharedAssemblyInfo.cs">
|
||||||
<Link>Properties\SharedAssemblyInfo.cs</Link>
|
<Link>Properties\SharedAssemblyInfo.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Albums\AlbumModuleWithSignalR.cs" />
|
||||||
<Compile Include="Authentication\AuthenticationService.cs" />
|
<Compile Include="Authentication\AuthenticationService.cs" />
|
||||||
<Compile Include="Authentication\EnableAuthInNancy.cs" />
|
<Compile Include="Authentication\EnableAuthInNancy.cs" />
|
||||||
<Compile Include="Authentication\AuthenticationModule.cs" />
|
<Compile Include="Authentication\AuthenticationModule.cs" />
|
||||||
|
@ -106,6 +107,7 @@
|
||||||
<Compile Include="Commands\CommandResource.cs" />
|
<Compile Include="Commands\CommandResource.cs" />
|
||||||
<Compile Include="TrackFiles\TrackFileModule.cs" />
|
<Compile Include="TrackFiles\TrackFileModule.cs" />
|
||||||
<Compile Include="TrackFiles\TrackFileResource.cs" />
|
<Compile Include="TrackFiles\TrackFileResource.cs" />
|
||||||
|
<Compile Include="Albums\AlbumModule.cs" />
|
||||||
<Compile Include="Tracks\TrackModule.cs" />
|
<Compile Include="Tracks\TrackModule.cs" />
|
||||||
<Compile Include="Tracks\TrackModuleWithSignalR.cs" />
|
<Compile Include="Tracks\TrackModuleWithSignalR.cs" />
|
||||||
<Compile Include="Tracks\TrackResource.cs" />
|
<Compile Include="Tracks\TrackResource.cs" />
|
||||||
|
@ -118,7 +120,7 @@
|
||||||
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
||||||
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
||||||
<Compile Include="Indexers\ReleasePushModule.cs" />
|
<Compile Include="Indexers\ReleasePushModule.cs" />
|
||||||
<Compile Include="Music\AlbumResource.cs" />
|
<Compile Include="Albums\AlbumResource.cs" />
|
||||||
<Compile Include="Music\ArtistLookupModule.cs" />
|
<Compile Include="Music\ArtistLookupModule.cs" />
|
||||||
<Compile Include="Music\ArtistModule.cs" />
|
<Compile Include="Music\ArtistModule.cs" />
|
||||||
<Compile Include="Music\ArtistResource.cs" />
|
<Compile Include="Music\ArtistResource.cs" />
|
||||||
|
|
|
@ -14,7 +14,8 @@ namespace NzbDrone.Api.Tracks
|
||||||
public int ArtistId { get; set; }
|
public int ArtistId { get; set; }
|
||||||
public int TrackFileId { get; set; }
|
public int TrackFileId { get; set; }
|
||||||
public int AlbumId { get; set; }
|
public int AlbumId { get; set; }
|
||||||
//public int EpisodeNumber { get; set; }
|
public bool Explicit { get; set; }
|
||||||
|
public int TrackNumber { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
//public string AirDate { get; set; }
|
//public string AirDate { get; set; }
|
||||||
//public DateTime? AirDateUtc { get; set; }
|
//public DateTime? AirDateUtc { get; set; }
|
||||||
|
@ -30,6 +31,7 @@ namespace NzbDrone.Api.Tracks
|
||||||
//public bool UnverifiedSceneNumbering { get; set; }
|
//public bool UnverifiedSceneNumbering { get; set; }
|
||||||
//public string SeriesTitle { get; set; }
|
//public string SeriesTitle { get; set; }
|
||||||
public ArtistResource Artist { get; set; }
|
public ArtistResource Artist { get; set; }
|
||||||
|
public Ratings Ratings { get; set; }
|
||||||
|
|
||||||
//Hiding this so people don't think its usable (only used to set the initial state)
|
//Hiding this so people don't think its usable (only used to set the initial state)
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||||
|
@ -49,7 +51,8 @@ namespace NzbDrone.Api.Tracks
|
||||||
ArtistId = model.ArtistId,
|
ArtistId = model.ArtistId,
|
||||||
TrackFileId = model.TrackFileId,
|
TrackFileId = model.TrackFileId,
|
||||||
AlbumId = model.AlbumId,
|
AlbumId = model.AlbumId,
|
||||||
//EpisodeNumber = model.EpisodeNumber,
|
Explicit = model.Explicit,
|
||||||
|
TrackNumber = model.TrackNumber,
|
||||||
Title = model.Title,
|
Title = model.Title,
|
||||||
//AirDate = model.AirDate,
|
//AirDate = model.AirDate,
|
||||||
//AirDateUtc = model.AirDateUtc,
|
//AirDateUtc = model.AirDateUtc,
|
||||||
|
@ -58,6 +61,7 @@ namespace NzbDrone.Api.Tracks
|
||||||
|
|
||||||
HasFile = model.HasFile,
|
HasFile = model.HasFile,
|
||||||
Monitored = model.Monitored,
|
Monitored = model.Monitored,
|
||||||
|
Ratings = model.Ratings,
|
||||||
//AbsoluteEpisodeNumber = model.AbsoluteEpisodeNumber,
|
//AbsoluteEpisodeNumber = model.AbsoluteEpisodeNumber,
|
||||||
//SceneAbsoluteEpisodeNumber = model.SceneAbsoluteEpisodeNumber,
|
//SceneAbsoluteEpisodeNumber = model.SceneAbsoluteEpisodeNumber,
|
||||||
//SceneEpisodeNumber = model.SceneEpisodeNumber,
|
//SceneEpisodeNumber = model.SceneEpisodeNumber,
|
||||||
|
|
15
src/NzbDrone.Core/ArtistStats/AlbumStatistics.cs
Normal file
15
src/NzbDrone.Core/ArtistStats/AlbumStatistics.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ArtistStats
|
||||||
|
{
|
||||||
|
public class AlbumStatistics : ResultSet
|
||||||
|
{
|
||||||
|
public int ArtistId { get; set; }
|
||||||
|
public int AlbumId { get; set; }
|
||||||
|
public int TrackFileCount { get; set; }
|
||||||
|
public int TrackCount { get; set; }
|
||||||
|
public int TotalTrackCount { get; set; }
|
||||||
|
public long SizeOnDisk { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
src/NzbDrone.Core/ArtistStats/ArtistStatistics.cs
Normal file
17
src/NzbDrone.Core/ArtistStats/ArtistStatistics.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ArtistStats
|
||||||
|
{
|
||||||
|
public class ArtistStatistics : ResultSet
|
||||||
|
{
|
||||||
|
public int ArtistId { get; set; }
|
||||||
|
public int AlbumCount { get; set; }
|
||||||
|
public int TrackFileCount { get; set; }
|
||||||
|
public int TrackCount { get; set; }
|
||||||
|
public int TotalTrackCount { get; set; }
|
||||||
|
public long SizeOnDisk { get; set; }
|
||||||
|
public List<AlbumStatistics> AlbumStatistics { get; set; }
|
||||||
|
}
|
||||||
|
}
|
80
src/NzbDrone.Core/ArtistStats/ArtistStatisticsRepository.cs
Normal file
80
src/NzbDrone.Core/ArtistStats/ArtistStatisticsRepository.cs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ArtistStats
|
||||||
|
{
|
||||||
|
public interface IArtistStatisticsRepository
|
||||||
|
{
|
||||||
|
List<AlbumStatistics> ArtistStatistics();
|
||||||
|
List<AlbumStatistics> ArtistStatistics(int artistId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ArtistStatisticsRepository : IArtistStatisticsRepository
|
||||||
|
{
|
||||||
|
private readonly IMainDatabase _database;
|
||||||
|
|
||||||
|
public ArtistStatisticsRepository(IMainDatabase database)
|
||||||
|
{
|
||||||
|
_database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AlbumStatistics> ArtistStatistics()
|
||||||
|
{
|
||||||
|
var mapper = _database.GetDataMapper();
|
||||||
|
|
||||||
|
mapper.AddParameter("currentDate", DateTime.UtcNow);
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine(GetSelectClause());
|
||||||
|
sb.AppendLine(GetTrackFilesJoin());
|
||||||
|
sb.AppendLine(GetGroupByClause());
|
||||||
|
var queryText = sb.ToString();
|
||||||
|
|
||||||
|
return mapper.Query<AlbumStatistics>(queryText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AlbumStatistics> ArtistStatistics(int artistId)
|
||||||
|
{
|
||||||
|
var mapper = _database.GetDataMapper();
|
||||||
|
|
||||||
|
mapper.AddParameter("currentDate", DateTime.UtcNow);
|
||||||
|
mapper.AddParameter("artistId", artistId);
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine(GetSelectClause());
|
||||||
|
sb.AppendLine(GetTrackFilesJoin());
|
||||||
|
sb.AppendLine("WHERE Tracks.ArtistId = @artistId");
|
||||||
|
sb.AppendLine(GetGroupByClause());
|
||||||
|
var queryText = sb.ToString();
|
||||||
|
|
||||||
|
return mapper.Query<AlbumStatistics>(queryText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSelectClause()
|
||||||
|
{
|
||||||
|
return @"SELECT Tracks.*, SUM(TrackFiles.Size) as SizeOnDisk FROM
|
||||||
|
(SELECT
|
||||||
|
Tracks.ArtistId,
|
||||||
|
Tracks.AlbumId,
|
||||||
|
SUM(CASE WHEN TrackFileId > 0 THEN 1 ELSE 0 END) AS TotalTrackCount,
|
||||||
|
SUM(CASE WHEN Monitored = 1 OR TrackFileId > 0 THEN 1 ELSE 0 END) AS TrackCount,
|
||||||
|
SUM(CASE WHEN TrackFileId > 0 THEN 1 ELSE 0 END) AS TrackFileCount
|
||||||
|
FROM Tracks
|
||||||
|
GROUP BY Tracks.ArtistId, Tracks.AlbumId) as Tracks";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetGroupByClause()
|
||||||
|
{
|
||||||
|
return "GROUP BY Tracks.ArtistId, Tracks.AlbumId";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTrackFilesJoin()
|
||||||
|
{
|
||||||
|
return @"LEFT OUTER JOIN TrackFiles
|
||||||
|
ON TrackFiles.ArtistId = Tracks.ArtistId
|
||||||
|
AND TrackFiles.AlbumId = Tracks.AlbumId";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
src/NzbDrone.Core/ArtistStats/ArtistStatisticsService.cs
Normal file
53
src/NzbDrone.Core/ArtistStats/ArtistStatisticsService.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ArtistStats
|
||||||
|
{
|
||||||
|
public interface IArtistStatisticsService
|
||||||
|
{
|
||||||
|
List<ArtistStatistics> ArtistStatistics();
|
||||||
|
ArtistStatistics ArtistStatistics(int artistId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ArtistStatisticsService : IArtistStatisticsService
|
||||||
|
{
|
||||||
|
private readonly IArtistStatisticsRepository _artistStatisticsRepository;
|
||||||
|
|
||||||
|
public ArtistStatisticsService(IArtistStatisticsRepository artistStatisticsRepository)
|
||||||
|
{
|
||||||
|
_artistStatisticsRepository = artistStatisticsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ArtistStatistics> ArtistStatistics()
|
||||||
|
{
|
||||||
|
var albumStatistics = _artistStatisticsRepository.ArtistStatistics();
|
||||||
|
|
||||||
|
return albumStatistics.GroupBy(s => s.ArtistId).Select(s => MapArtistStatistics(s.ToList())).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArtistStatistics ArtistStatistics(int artistId)
|
||||||
|
{
|
||||||
|
var stats = _artistStatisticsRepository.ArtistStatistics(artistId);
|
||||||
|
|
||||||
|
if (stats == null || stats.Count == 0) return new ArtistStatistics();
|
||||||
|
|
||||||
|
return MapArtistStatistics(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArtistStatistics MapArtistStatistics(List<AlbumStatistics> albumStatistics)
|
||||||
|
{
|
||||||
|
var artistStatistics = new ArtistStatistics
|
||||||
|
{
|
||||||
|
AlbumStatistics = albumStatistics,
|
||||||
|
AlbumCount = albumStatistics.Count,
|
||||||
|
ArtistId = albumStatistics.First().ArtistId,
|
||||||
|
TrackFileCount = albumStatistics.Sum(s => s.TrackFileCount),
|
||||||
|
TrackCount = albumStatistics.Sum(s => s.TrackCount),
|
||||||
|
TotalTrackCount = albumStatistics.Sum(s => s.TotalTrackCount),
|
||||||
|
SizeOnDisk = albumStatistics.Sum(s => s.SizeOnDisk)
|
||||||
|
};
|
||||||
|
|
||||||
|
return artistStatistics;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ using NzbDrone.Core.Profiles;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Restrictions;
|
using NzbDrone.Core.Restrictions;
|
||||||
using NzbDrone.Core.RootFolders;
|
using NzbDrone.Core.RootFolders;
|
||||||
|
using NzbDrone.Core.ArtistStats;
|
||||||
using NzbDrone.Core.SeriesStats;
|
using NzbDrone.Core.SeriesStats;
|
||||||
using NzbDrone.Core.Tags;
|
using NzbDrone.Core.Tags;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
@ -121,7 +122,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
Mapper.Entity<Profile>().RegisterModel("Profiles");
|
Mapper.Entity<Profile>().RegisterModel("Profiles");
|
||||||
Mapper.Entity<Log>().RegisterModel("Logs");
|
Mapper.Entity<Log>().RegisterModel("Logs");
|
||||||
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
|
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
|
||||||
Mapper.Entity<SeasonStatistics>().MapResultSet();
|
Mapper.Entity<AlbumStatistics>().MapResultSet();
|
||||||
Mapper.Entity<Blacklist>().RegisterModel("Blacklist");
|
Mapper.Entity<Blacklist>().RegisterModel("Blacklist");
|
||||||
Mapper.Entity<MetadataFile>().RegisterModel("MetadataFiles");
|
Mapper.Entity<MetadataFile>().RegisterModel("MetadataFiles");
|
||||||
Mapper.Entity<SubtitleFile>().RegisterModel("SubtitleFiles");
|
Mapper.Entity<SubtitleFile>().RegisterModel("SubtitleFiles");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaCover
|
namespace NzbDrone.Core.MediaCover
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,9 @@ namespace NzbDrone.Core.MediaCover
|
||||||
Banner = 2,
|
Banner = 2,
|
||||||
Fanart = 3,
|
Fanart = 3,
|
||||||
Screenshot = 4,
|
Screenshot = 4,
|
||||||
Headshot = 5
|
Headshot = 5,
|
||||||
|
Cover = 6,
|
||||||
|
Disc = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MediaCover : IEmbeddedDocument
|
public class MediaCover : IEmbeddedDocument
|
||||||
|
|
|
@ -365,7 +365,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
|
|
||||||
series.TitleSlug = show.Slug;
|
series.TitleSlug = show.Slug;
|
||||||
series.Status = MapSeriesStatus(show.Status);
|
series.Status = MapSeriesStatus(show.Status);
|
||||||
series.Ratings = MapRatings(show.Rating);
|
//series.Ratings = MapRatings(show.Rating);
|
||||||
series.Genres = show.Genres;
|
series.Genres = show.Genres;
|
||||||
|
|
||||||
if (show.ContentRating.IsNotNullOrWhiteSpace())
|
if (show.ContentRating.IsNotNullOrWhiteSpace())
|
||||||
|
@ -412,7 +412,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
episode.AirDate = oracleEpisode.AirDate;
|
episode.AirDate = oracleEpisode.AirDate;
|
||||||
episode.AirDateUtc = oracleEpisode.AirDateUtc;
|
episode.AirDateUtc = oracleEpisode.AirDateUtc;
|
||||||
|
|
||||||
episode.Ratings = MapRatings(oracleEpisode.Rating);
|
//episode.Ratings = MapRatings(oracleEpisode.Rating);
|
||||||
|
|
||||||
//Don't include series fanart images as episode screenshot
|
//Don't include series fanart images as episode screenshot
|
||||||
if (oracleEpisode.Image != null)
|
if (oracleEpisode.Image != null)
|
||||||
|
@ -443,14 +443,14 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
return SeriesStatusType.Continuing;
|
return SeriesStatusType.Continuing;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Ratings MapRatings(RatingResource rating)
|
private static Core.Music.Ratings MapRatings(RatingResource rating)
|
||||||
{
|
{
|
||||||
if (rating == null)
|
if (rating == null)
|
||||||
{
|
{
|
||||||
return new Ratings();
|
return new Core.Music.Ratings();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Ratings
|
return new Core.Music.Ratings
|
||||||
{
|
{
|
||||||
Votes = rating.Count,
|
Votes = rating.Count,
|
||||||
Value = rating.Value
|
Value = rating.Value
|
||||||
|
|
|
@ -17,21 +17,25 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
public string ForeignAlbumId { get; set; }
|
public string ForeignAlbumId { get; set; }
|
||||||
public int ArtistId { get; set; }
|
public int ArtistId { get; set; }
|
||||||
public string Title { get; set; } // NOTE: This should be CollectionName in API
|
public string Title { get; set; }
|
||||||
public string CleanTitle { 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 int TrackCount { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
public int ProfileId { get; set; }
|
||||||
public List<Track> Tracks { get; set; }
|
public List<Track> Tracks { get; set; }
|
||||||
//public int DiscCount { get; set; }
|
//public int DiscCount { get; set; }
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public List<MediaCover.MediaCover> Images { get; set; }
|
public List<MediaCover.MediaCover> Images { get; set; }
|
||||||
//public List<Actor> Actors { get; set; } // These are band members. TODO: Refactor
|
//public List<Actor> Actors { get; set; } // TODO: These are band members. TODO: Refactor
|
||||||
public List<string> Genres { get; set; }
|
public List<string> Genres { get; set; }
|
||||||
public String AlbumType { get; set; } //Turn this into a type similar to Series Type in TV
|
public String AlbumType { get; set; } // TODO: Turn this into a type similar to Series Type in TV
|
||||||
//public string ArtworkUrl { get; set; }
|
//public string ArtworkUrl { get; set; }
|
||||||
//public string Explicitness { get; set; }
|
//public string Explicitness { get; set; }
|
||||||
public AddSeriesOptions AddOptions { get; set; }
|
public AddSeriesOptions AddOptions { get; set; }
|
||||||
|
public Artist Artist { get; set; }
|
||||||
|
public Ratings Ratings { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace NzbDrone.Core.Music
|
||||||
List<Album> GetAlbums(int artistId);
|
List<Album> GetAlbums(int artistId);
|
||||||
Album FindByName(string cleanTitle);
|
Album FindByName(string cleanTitle);
|
||||||
Album FindById(string spotifyId);
|
Album FindById(string spotifyId);
|
||||||
|
void SetMonitoredFlat(Album album, bool monitored);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AlbumRepository : BasicRepository<Album>, IAlbumRepository
|
public class AlbumRepository : BasicRepository<Album>, IAlbumRepository
|
||||||
|
@ -35,6 +36,12 @@ namespace NzbDrone.Core.Music
|
||||||
return Query.Where(s => s.ForeignAlbumId == foreignAlbumId).SingleOrDefault();
|
return Query.Where(s => s.ForeignAlbumId == foreignAlbumId).SingleOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetMonitoredFlat(Album album, bool monitored)
|
||||||
|
{
|
||||||
|
album.Monitored = monitored;
|
||||||
|
SetFields(album, p => p.Monitored);
|
||||||
|
}
|
||||||
|
|
||||||
public Album FindByName(string cleanTitle)
|
public Album FindByName(string cleanTitle)
|
||||||
{
|
{
|
||||||
cleanTitle = cleanTitle.ToLowerInvariant();
|
cleanTitle = cleanTitle.ToLowerInvariant();
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace NzbDrone.Core.Music
|
||||||
List<Album> GetAllAlbums();
|
List<Album> GetAllAlbums();
|
||||||
Album UpdateAlbum(Album album);
|
Album UpdateAlbum(Album album);
|
||||||
List<Album> UpdateAlbums(List<Album> album);
|
List<Album> UpdateAlbums(List<Album> album);
|
||||||
|
void SetAlbumMonitored(int albumId, bool monitored);
|
||||||
void InsertMany(List<Album> albums);
|
void InsertMany(List<Album> albums);
|
||||||
void UpdateMany(List<Album> albums);
|
void UpdateMany(List<Album> albums);
|
||||||
void DeleteMany(List<Album> albums);
|
void DeleteMany(List<Album> albums);
|
||||||
|
@ -134,6 +135,14 @@ namespace NzbDrone.Core.Music
|
||||||
return updatedAlbum;
|
return updatedAlbum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAlbumMonitored(int albumId, bool monitored)
|
||||||
|
{
|
||||||
|
var album = _albumRepository.Get(albumId);
|
||||||
|
_albumRepository.SetMonitoredFlat(album, monitored);
|
||||||
|
|
||||||
|
_logger.Debug("Monitored flag for Album:{0} was set to {1}", albumId, monitored);
|
||||||
|
}
|
||||||
|
|
||||||
public List<Album> UpdateAlbums(List<Album> album)
|
public List<Album> UpdateAlbums(List<Album> album)
|
||||||
{
|
{
|
||||||
_logger.Debug("Updating {0} album", album.Count);
|
_logger.Debug("Updating {0} album", album.Count);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Profiles;
|
using NzbDrone.Core.Profiles;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -16,7 +16,7 @@ namespace NzbDrone.Core.Music
|
||||||
{
|
{
|
||||||
Images = new List<MediaCover.MediaCover>();
|
Images = new List<MediaCover.MediaCover>();
|
||||||
Genres = new List<string>();
|
Genres = new List<string>();
|
||||||
//Members = new List<Person>(); // Artist Band Member? (NOTE: This should be per album)
|
Members = new List<Member>();
|
||||||
Albums = new List<Album>();
|
Albums = new List<Album>();
|
||||||
Tags = new HashSet<int>();
|
Tags = new HashSet<int>();
|
||||||
|
|
||||||
|
@ -45,7 +45,9 @@ namespace NzbDrone.Core.Music
|
||||||
public int ProfileId { get; set; }
|
public int ProfileId { get; set; }
|
||||||
public List<Album> Albums { get; set; }
|
public List<Album> Albums { get; set; }
|
||||||
public HashSet<int> Tags { get; set; }
|
public HashSet<int> Tags { get; set; }
|
||||||
public AddSeriesOptions AddOptions { get; set; }
|
public AddArtistOptions AddOptions { get; set; }
|
||||||
|
public Ratings Ratings { get; set; }
|
||||||
|
public List<Member> Members { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
@ -76,6 +78,8 @@ namespace NzbDrone.Core.Music
|
||||||
Albums = otherArtist.Albums;
|
Albums = otherArtist.Albums;
|
||||||
Tags = otherArtist.Tags;
|
Tags = otherArtist.Tags;
|
||||||
AddOptions = otherArtist.AddOptions;
|
AddOptions = otherArtist.AddOptions;
|
||||||
|
Ratings = otherArtist.Ratings;
|
||||||
|
Members = otherArtist.Members;
|
||||||
|
|
||||||
Albums = otherArtist.Albums;
|
Albums = otherArtist.Albums;
|
||||||
Path = otherArtist.Path;
|
Path = otherArtist.Path;
|
||||||
|
@ -86,6 +90,7 @@ namespace NzbDrone.Core.Music
|
||||||
Tags = otherArtist.Tags;
|
Tags = otherArtist.Tags;
|
||||||
AddOptions = otherArtist.AddOptions;
|
AddOptions = otherArtist.AddOptions;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
src/NzbDrone.Core/Music/Member.cs
Normal file
17
src/NzbDrone.Core/Music/Member.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
public class Member : IEmbeddedDocument
|
||||||
|
{
|
||||||
|
public Member()
|
||||||
|
{
|
||||||
|
Images = new List<MediaCover.MediaCover>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Instrument { get; set; }
|
||||||
|
public List<MediaCover.MediaCover> Images { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
src/NzbDrone.Core/Music/Ratings.cs
Normal file
10
src/NzbDrone.Core/Music/Ratings.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
public class Ratings : IEmbeddedDocument
|
||||||
|
{
|
||||||
|
public int Votes { get; set; }
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ namespace NzbDrone.Core.Music
|
||||||
public bool Explicit { get; set; }
|
public bool Explicit { get; set; }
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public int TrackFileId { get; set; }
|
public int TrackFileId { get; set; }
|
||||||
|
public Ratings Ratings { get; set; }
|
||||||
//public DateTime? ReleaseDate { get; set; }
|
//public DateTime? ReleaseDate { get; set; }
|
||||||
|
|
||||||
public LazyLoaded<TrackFile> TrackFile { get; set; }
|
public LazyLoaded<TrackFile> TrackFile { get; set; }
|
||||||
|
|
|
@ -859,6 +859,7 @@
|
||||||
<Compile Include="Extras\Metadata\MetadataType.cs" />
|
<Compile Include="Extras\Metadata\MetadataType.cs" />
|
||||||
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
||||||
<Compile Include="MetadataSource\ISearchForNewSeries.cs" />
|
<Compile Include="MetadataSource\ISearchForNewSeries.cs" />
|
||||||
|
<Compile Include="Music\Member.cs" />
|
||||||
<Compile Include="Music\AddArtistOptions.cs" />
|
<Compile Include="Music\AddArtistOptions.cs" />
|
||||||
<Compile Include="Music\AddArtistService.cs" />
|
<Compile Include="Music\AddArtistService.cs" />
|
||||||
<Compile Include="Music\AddArtistValidator.cs" />
|
<Compile Include="Music\AddArtistValidator.cs" />
|
||||||
|
@ -882,6 +883,7 @@
|
||||||
<Compile Include="Music\Events\ArtistUpdatedEvent.cs" />
|
<Compile Include="Music\Events\ArtistUpdatedEvent.cs" />
|
||||||
<Compile Include="Music\Events\AlbumInfoRefreshedEvent.cs" />
|
<Compile Include="Music\Events\AlbumInfoRefreshedEvent.cs" />
|
||||||
<Compile Include="Music\Events\TrackInfoRefreshedEvent.cs" />
|
<Compile Include="Music\Events\TrackInfoRefreshedEvent.cs" />
|
||||||
|
<Compile Include="Music\Ratings.cs" />
|
||||||
<Compile Include="Music\RefreshArtistService.cs" />
|
<Compile Include="Music\RefreshArtistService.cs" />
|
||||||
<Compile Include="Music\RefreshAlbumService.cs" />
|
<Compile Include="Music\RefreshAlbumService.cs" />
|
||||||
<Compile Include="Music\RefreshTrackService.cs" />
|
<Compile Include="Music\RefreshTrackService.cs" />
|
||||||
|
@ -1104,6 +1106,10 @@
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="RootFolders\UnmappedFolder.cs" />
|
<Compile Include="RootFolders\UnmappedFolder.cs" />
|
||||||
<Compile Include="Security.cs" />
|
<Compile Include="Security.cs" />
|
||||||
|
<Compile Include="ArtistStats\AlbumStatistics.cs" />
|
||||||
|
<Compile Include="ArtistStats\ArtistStatistics.cs" />
|
||||||
|
<Compile Include="ArtistStats\ArtistStatisticsRepository.cs" />
|
||||||
|
<Compile Include="ArtistStats\ArtistStatisticsService.cs" />
|
||||||
<Compile Include="SeriesStats\SeasonStatistics.cs" />
|
<Compile Include="SeriesStats\SeasonStatistics.cs" />
|
||||||
<Compile Include="SeriesStats\SeriesStatistics.cs" />
|
<Compile Include="SeriesStats\SeriesStatistics.cs" />
|
||||||
<Compile Include="SeriesStats\SeriesStatisticsRepository.cs" />
|
<Compile Include="SeriesStats\SeriesStatisticsRepository.cs" />
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
var Backbone = require('backbone');
|
|
||||||
var ArtistModel = require('../Artist/ArtistModel');
|
|
||||||
var _ = require('underscore');
|
|
||||||
|
|
||||||
module.exports = Backbone.Collection.extend({
|
|
||||||
url : window.NzbDrone.ApiRoot + '/artist/lookup',
|
|
||||||
model : ArtistModel,
|
|
||||||
|
|
||||||
parse : function(response) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
_.each(response, function(model) {
|
|
||||||
model.id = undefined;
|
|
||||||
|
|
||||||
if (self.unmappedFolderModel) {
|
|
||||||
model.path = self.unmappedFolderModel.get('folder').path;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log('response: ', response);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,53 +0,0 @@
|
||||||
var vent = require('vent');
|
|
||||||
var AppLayout = require('../AppLayout');
|
|
||||||
var Marionette = require('marionette');
|
|
||||||
var RootFolderLayout = require('./RootFolders/RootFolderLayout');
|
|
||||||
var ExistingSeriesCollectionView = require('./Existing/AddExistingSeriesCollectionView');
|
|
||||||
var AddSeriesView = require('./AddSeriesView');
|
|
||||||
var ProfileCollection = require('../Profile/ProfileCollection');
|
|
||||||
var RootFolderCollection = require('./RootFolders/RootFolderCollection');
|
|
||||||
require('../Artist/ArtistCollection');
|
|
||||||
|
|
||||||
module.exports = Marionette.Layout.extend({
|
|
||||||
template : 'AddSeries/AddSeriesLayoutTemplate',
|
|
||||||
|
|
||||||
regions : {
|
|
||||||
workspace : '#add-series-workspace'
|
|
||||||
},
|
|
||||||
|
|
||||||
events : {
|
|
||||||
'click .x-import' : '_importSeries',
|
|
||||||
'click .x-add-new' : '_addSeries'
|
|
||||||
},
|
|
||||||
|
|
||||||
attributes : {
|
|
||||||
id : 'add-series-screen'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize : function() {
|
|
||||||
ProfileCollection.fetch();
|
|
||||||
RootFolderCollection.fetch().done(function() {
|
|
||||||
RootFolderCollection.synced = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onShow : function() {
|
|
||||||
this.workspace.show(new AddSeriesView());
|
|
||||||
},
|
|
||||||
|
|
||||||
_folderSelected : function(options) {
|
|
||||||
vent.trigger(vent.Commands.CloseModalCommand);
|
|
||||||
|
|
||||||
this.workspace.show(new ExistingSeriesCollectionView({ model : options.model }));
|
|
||||||
},
|
|
||||||
|
|
||||||
_importSeries : function() {
|
|
||||||
this.rootFolderLayout = new RootFolderLayout();
|
|
||||||
this.listenTo(this.rootFolderLayout, 'folderSelected', this._folderSelected);
|
|
||||||
AppLayout.modalRegion.show(this.rootFolderLayout);
|
|
||||||
},
|
|
||||||
|
|
||||||
_addSeries : function() {
|
|
||||||
this.workspace.show(new AddSeriesView());
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="btn-group add-series-btn-group btn-group-lg btn-block">
|
|
||||||
<button type="button" class="btn btn-default col-md-10 col-xs-8 add-series-import-btn x-import">
|
|
||||||
<i class="icon-lidarr-hdd"/>
|
|
||||||
Import existing artists on disk
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-default col-md-2 col-xs-4 x-add-new"><i class="icon-lidarr-active hidden-xs"></i> Add New Artist</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div id="add-series-workspace"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,183 +0,0 @@
|
||||||
var _ = require('underscore');
|
|
||||||
var vent = require('vent');
|
|
||||||
var Marionette = require('marionette');
|
|
||||||
var AddSeriesCollection = require('./AddSeriesCollection');
|
|
||||||
var SearchResultCollectionView = require('./SearchResultCollectionView');
|
|
||||||
var EmptyView = require('./EmptyView');
|
|
||||||
var NotFoundView = require('./NotFoundView');
|
|
||||||
var ErrorView = require('./ErrorView');
|
|
||||||
var LoadingView = require('../Shared/LoadingView');
|
|
||||||
|
|
||||||
module.exports = Marionette.Layout.extend({
|
|
||||||
template : 'AddSeries/AddSeriesViewTemplate',
|
|
||||||
|
|
||||||
regions : {
|
|
||||||
searchResult : '#search-result'
|
|
||||||
},
|
|
||||||
|
|
||||||
ui : {
|
|
||||||
seriesSearch : '.x-series-search',
|
|
||||||
searchBar : '.x-search-bar',
|
|
||||||
loadMore : '.x-load-more'
|
|
||||||
},
|
|
||||||
|
|
||||||
events : {
|
|
||||||
'click .x-load-more' : '_onLoadMore'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize : function(options) {
|
|
||||||
this.isExisting = options.isExisting;
|
|
||||||
this.collection = new AddSeriesCollection();
|
|
||||||
console.log('this.collection:', this.collection);
|
|
||||||
|
|
||||||
if (this.isExisting) {
|
|
||||||
this.collection.unmappedFolderModel = this.model;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isExisting) {
|
|
||||||
this.className = 'existing-series';
|
|
||||||
} else {
|
|
||||||
this.className = 'new-series';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.listenTo(vent, vent.Events.SeriesAdded, this._onSeriesAdded);
|
|
||||||
this.listenTo(this.collection, 'sync', this._showResults);
|
|
||||||
|
|
||||||
this.resultCollectionView = new SearchResultCollectionView({
|
|
||||||
collection : this.collection,
|
|
||||||
isExisting : this.isExisting
|
|
||||||
});
|
|
||||||
|
|
||||||
this.throttledSearch = _.debounce(this.search, 1000, { trailing : true }).bind(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
onRender : function() {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.$el.addClass(this.className);
|
|
||||||
|
|
||||||
this.ui.seriesSearch.keyup(function(e) {
|
|
||||||
|
|
||||||
if (_.contains([
|
|
||||||
9,
|
|
||||||
16,
|
|
||||||
17,
|
|
||||||
18,
|
|
||||||
19,
|
|
||||||
20,
|
|
||||||
33,
|
|
||||||
34,
|
|
||||||
35,
|
|
||||||
36,
|
|
||||||
37,
|
|
||||||
38,
|
|
||||||
39,
|
|
||||||
40,
|
|
||||||
91,
|
|
||||||
92,
|
|
||||||
93
|
|
||||||
], e.keyCode)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self._abortExistingSearch();
|
|
||||||
self.throttledSearch({
|
|
||||||
term : self.ui.seriesSearch.val()
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this._clearResults();
|
|
||||||
|
|
||||||
if (this.isExisting) {
|
|
||||||
this.ui.searchBar.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onShow : function() {
|
|
||||||
this.ui.seriesSearch.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
search : function(options) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.collection.reset();
|
|
||||||
|
|
||||||
if (!options.term || options.term === this.collection.term) {
|
|
||||||
return Marionette.$.Deferred().resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.searchResult.show(new LoadingView());
|
|
||||||
this.collection.term = options.term;
|
|
||||||
this.currentSearchPromise = this.collection.fetch({
|
|
||||||
data : { term : options.term }
|
|
||||||
});
|
|
||||||
|
|
||||||
this.currentSearchPromise.fail(function() {
|
|
||||||
self._showError();
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.currentSearchPromise;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onSeriesAdded : function(options) {
|
|
||||||
if (this.isExisting && options.series.get('path') === this.model.get('folder').path) {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (!this.isExisting) {
|
|
||||||
this.collection.term = '';
|
|
||||||
this.collection.reset();
|
|
||||||
this._clearResults();
|
|
||||||
this.ui.seriesSearch.val('');
|
|
||||||
this.ui.seriesSearch.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onLoadMore : function() {
|
|
||||||
var showingAll = this.resultCollectionView.showMore();
|
|
||||||
this.ui.searchBar.show();
|
|
||||||
|
|
||||||
if (showingAll) {
|
|
||||||
this.ui.loadMore.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_clearResults : function() {
|
|
||||||
if (!this.isExisting) {
|
|
||||||
this.searchResult.show(new EmptyView());
|
|
||||||
} else {
|
|
||||||
this.searchResult.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_showResults : function() {
|
|
||||||
if (!this.isClosed) {
|
|
||||||
if (this.collection.length === 0) {
|
|
||||||
this.ui.searchBar.show();
|
|
||||||
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
|
|
||||||
} else {
|
|
||||||
this.searchResult.show(this.resultCollectionView);
|
|
||||||
if (!this.showingAll && this.isExisting) {
|
|
||||||
this.ui.loadMore.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_abortExistingSearch : function() {
|
|
||||||
if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) {
|
|
||||||
console.log('aborting previous pending search request.');
|
|
||||||
this.currentSearchPromise.abort();
|
|
||||||
} else {
|
|
||||||
this._clearResults();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_showError : function() {
|
|
||||||
if (!this.isClosed) {
|
|
||||||
this.ui.searchBar.show();
|
|
||||||
this.searchResult.show(new ErrorView({ term : this.collection.term }));
|
|
||||||
this.collection.term = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,24 +0,0 @@
|
||||||
{{#if folder.path}}
|
|
||||||
<div class="unmapped-folder-path">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{{folder.path}}
|
|
||||||
</div>
|
|
||||||
</div>{{/if}}
|
|
||||||
<div class="x-search-bar">
|
|
||||||
<div class="input-group input-group-lg add-series-search">
|
|
||||||
<span class="input-group-addon"><i class="icon-lidarr-search"/></span>
|
|
||||||
|
|
||||||
{{#if folder}}
|
|
||||||
<input type="text" class="form-control x-series-search" value="{{folder.name}}">
|
|
||||||
{{else}}
|
|
||||||
<input type="text" class="form-control x-series-search" placeholder="Start typing the name of an artist or album...">
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div id="search-result" class="result-list col-md-12"/>
|
|
||||||
</div>
|
|
||||||
<div class="btn btn-block text-center new-series-loadmore x-load-more" style="display: none;">
|
|
||||||
<i class="icon-lidarr-load-more"/>
|
|
||||||
more
|
|
||||||
</div>
|
|
|
@ -1,5 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
|
|
||||||
module.exports = Marionette.CompositeView.extend({
|
|
||||||
template : 'AddSeries/EmptyViewTemplate'
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
<div class="text-center hint col-md-12">
|
|
||||||
<span>You can also search by iTunes using the itunes: prefixes.</span>
|
|
||||||
</div>
|
|
|
@ -1,13 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
|
|
||||||
module.exports = Marionette.CompositeView.extend({
|
|
||||||
template : 'AddSeries/ErrorViewTemplate',
|
|
||||||
|
|
||||||
initialize : function(options) {
|
|
||||||
this.options = options;
|
|
||||||
},
|
|
||||||
|
|
||||||
templateHelpers : function() {
|
|
||||||
return this.options;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
<div class="text-center col-md-12">
|
|
||||||
<h3>
|
|
||||||
There was an error searching for '{{term}}'.
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
If the artist name contains non-alphanumeric characters try removing them, otherwise try your search again later.
|
|
||||||
</div>
|
|
|
@ -1,51 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
var AddSeriesView = require('../AddSeriesView');
|
|
||||||
var UnmappedFolderCollection = require('./UnmappedFolderCollection');
|
|
||||||
|
|
||||||
module.exports = Marionette.CompositeView.extend({
|
|
||||||
itemView : AddSeriesView,
|
|
||||||
itemViewContainer : '.x-loading-folders',
|
|
||||||
template : 'AddSeries/Existing/AddExistingSeriesCollectionViewTemplate',
|
|
||||||
|
|
||||||
ui : {
|
|
||||||
loadingFolders : '.x-loading-folders'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize : function() {
|
|
||||||
this.collection = new UnmappedFolderCollection();
|
|
||||||
this.collection.importItems(this.model);
|
|
||||||
},
|
|
||||||
|
|
||||||
showCollection : function() {
|
|
||||||
this._showAndSearch(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
appendHtml : function(collectionView, itemView, index) {
|
|
||||||
collectionView.ui.loadingFolders.before(itemView.el);
|
|
||||||
},
|
|
||||||
|
|
||||||
_showAndSearch : function(index) {
|
|
||||||
var self = this;
|
|
||||||
var model = this.collection.at(index);
|
|
||||||
|
|
||||||
if (model) {
|
|
||||||
var currentIndex = index;
|
|
||||||
var folderName = model.get('folder').name;
|
|
||||||
this.addItemView(model, this.getItemView(), index);
|
|
||||||
this.children.findByModel(model).search({ term : folderName }).always(function() {
|
|
||||||
if (!self.isClosed) {
|
|
||||||
self._showAndSearch(currentIndex + 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
this.ui.loadingFolders.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
itemViewOptions : {
|
|
||||||
isExisting : true
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
<div class="x-existing-folders">
|
|
||||||
<div class="loading-folders x-loading-folders">
|
|
||||||
Loading search results from iTunes for your artists, this may take a few minutes.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,20 +0,0 @@
|
||||||
var Backbone = require('backbone');
|
|
||||||
var UnmappedFolderModel = require('./UnmappedFolderModel');
|
|
||||||
var _ = require('underscore');
|
|
||||||
|
|
||||||
module.exports = Backbone.Collection.extend({
|
|
||||||
model : UnmappedFolderModel,
|
|
||||||
|
|
||||||
importItems : function(rootFolderModel) {
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
var rootFolder = rootFolderModel;
|
|
||||||
|
|
||||||
_.each(rootFolderModel.get('unmappedFolders'), function(folder) {
|
|
||||||
this.push(new UnmappedFolderModel({
|
|
||||||
rootFolder : rootFolder,
|
|
||||||
folder : folder
|
|
||||||
}));
|
|
||||||
}, this);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
var Backbone = require('backbone');
|
|
||||||
|
|
||||||
module.exports = Backbone.Model.extend({});
|
|
|
@ -1,18 +0,0 @@
|
||||||
<dl class="monitor-tooltip-contents">
|
|
||||||
<dt>All</dt>
|
|
||||||
<dd>Monitor all episodes except specials</dd>
|
|
||||||
<dt>Future</dt>
|
|
||||||
<dd>Monitor episodes that have not aired yet</dd>
|
|
||||||
<dt>Missing</dt>
|
|
||||||
<dd>Monitor episodes that do not have files or have not aired yet</dd>
|
|
||||||
<dt>Existing</dt>
|
|
||||||
<dd>Monitor episodes that have files or have not aired yet</dd>
|
|
||||||
<dt>First Season</dt>
|
|
||||||
<dd>Monitor all episodes of the first season. All other seasons will be ignored</dd>
|
|
||||||
<dt>Latest Season</dt>
|
|
||||||
<dd>Monitor all episodes of the latest season and future seasons</dd>
|
|
||||||
<dt>None</dt>
|
|
||||||
<dd>No episodes will be monitored.</dd>
|
|
||||||
<!--<dt>Latest Season</dt>-->
|
|
||||||
<!--<dd>Monitor all episodes the latest season only, previous seasons will be ignored</dd>-->
|
|
||||||
</dl>
|
|
|
@ -1,13 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
|
|
||||||
module.exports = Marionette.CompositeView.extend({
|
|
||||||
template : 'AddSeries/NotFoundViewTemplate',
|
|
||||||
|
|
||||||
initialize : function(options) {
|
|
||||||
this.options = options;
|
|
||||||
},
|
|
||||||
|
|
||||||
templateHelpers : function() {
|
|
||||||
return this.options;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
<div class="text-center col-md-12">
|
|
||||||
<h3>
|
|
||||||
Sorry. We couldn't find any series matching '{{term}}'
|
|
||||||
</h3>
|
|
||||||
<a href="https://github.com/NzbDrone/NzbDrone/wiki/FAQ#wiki-why-cant-i-add-a-new-show-to-nzbdrone-its-on-thetvdb">Why can't I find my show?</a>
|
|
||||||
|
|
||||||
</div>
|
|
|
@ -1,10 +0,0 @@
|
||||||
var Backbone = require('backbone');
|
|
||||||
var RootFolderModel = require('./RootFolderModel');
|
|
||||||
require('../../Mixins/backbone.signalr.mixin');
|
|
||||||
|
|
||||||
var RootFolderCollection = Backbone.Collection.extend({
|
|
||||||
url : window.NzbDrone.ApiRoot + '/rootfolder',
|
|
||||||
model : RootFolderModel
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = new RootFolderCollection();
|
|
|
@ -1,8 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
var RootFolderItemView = require('./RootFolderItemView');
|
|
||||||
|
|
||||||
module.exports = Marionette.CompositeView.extend({
|
|
||||||
template : 'AddSeries/RootFolders/RootFolderCollectionViewTemplate',
|
|
||||||
itemViewContainer : '.x-root-folders',
|
|
||||||
itemView : RootFolderItemView
|
|
||||||
});
|
|
|
@ -1,13 +0,0 @@
|
||||||
<table class="table table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="col-md-10 ">
|
|
||||||
Path
|
|
||||||
</th>
|
|
||||||
<th class="col-md-3">
|
|
||||||
Free Space
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="x-root-folders"></tbody>
|
|
||||||
</table>
|
|
|
@ -1,28 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
|
|
||||||
module.exports = Marionette.ItemView.extend({
|
|
||||||
template : 'AddSeries/RootFolders/RootFolderItemViewTemplate',
|
|
||||||
className : 'recent-folder',
|
|
||||||
tagName : 'tr',
|
|
||||||
|
|
||||||
initialize : function() {
|
|
||||||
this.listenTo(this.model, 'change', this.render);
|
|
||||||
},
|
|
||||||
|
|
||||||
events : {
|
|
||||||
'click .x-delete' : 'removeFolder',
|
|
||||||
'click .x-folder' : 'folderSelected'
|
|
||||||
},
|
|
||||||
|
|
||||||
removeFolder : function() {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.model.destroy().success(function() {
|
|
||||||
self.close();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
folderSelected : function() {
|
|
||||||
this.trigger('folderSelected', this.model);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,9 +0,0 @@
|
||||||
<td class="col-md-10 x-folder folder-path">
|
|
||||||
{{path}}
|
|
||||||
</td>
|
|
||||||
<td class="col-md-3 x-folder folder-free-space">
|
|
||||||
<span>{{Bytes freeSpace}}</span>
|
|
||||||
</td>
|
|
||||||
<td class="col-md-1">
|
|
||||||
<i class="icon-lidarr-delete x-delete"></i>
|
|
||||||
</td>
|
|
|
@ -1,80 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
var RootFolderCollectionView = require('./RootFolderCollectionView');
|
|
||||||
var RootFolderCollection = require('./RootFolderCollection');
|
|
||||||
var RootFolderModel = require('./RootFolderModel');
|
|
||||||
var LoadingView = require('../../Shared/LoadingView');
|
|
||||||
var AsValidatedView = require('../../Mixins/AsValidatedView');
|
|
||||||
require('../../Mixins/FileBrowser');
|
|
||||||
|
|
||||||
var Layout = Marionette.Layout.extend({
|
|
||||||
template : 'AddSeries/RootFolders/RootFolderLayoutTemplate',
|
|
||||||
|
|
||||||
ui : {
|
|
||||||
pathInput : '.x-path'
|
|
||||||
},
|
|
||||||
|
|
||||||
regions : {
|
|
||||||
currentDirs : '#current-dirs'
|
|
||||||
},
|
|
||||||
|
|
||||||
events : {
|
|
||||||
'click .x-add' : '_addFolder',
|
|
||||||
'keydown .x-path input' : '_keydown'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize : function() {
|
|
||||||
this.collection = RootFolderCollection;
|
|
||||||
this.rootfolderListView = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
onShow : function() {
|
|
||||||
this.listenTo(RootFolderCollection, 'sync', this._showCurrentDirs);
|
|
||||||
this.currentDirs.show(new LoadingView());
|
|
||||||
|
|
||||||
if (RootFolderCollection.synced) {
|
|
||||||
this._showCurrentDirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ui.pathInput.fileBrowser();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFolderSelected : function(options) {
|
|
||||||
this.trigger('folderSelected', options);
|
|
||||||
},
|
|
||||||
|
|
||||||
_addFolder : function() {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var newDir = new RootFolderModel({
|
|
||||||
Path : this.ui.pathInput.val()
|
|
||||||
});
|
|
||||||
|
|
||||||
this.bindToModelValidation(newDir);
|
|
||||||
|
|
||||||
newDir.save().done(function() {
|
|
||||||
RootFolderCollection.add(newDir);
|
|
||||||
self.trigger('folderSelected', { model : newDir });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_showCurrentDirs : function() {
|
|
||||||
if (!this.rootfolderListView) {
|
|
||||||
this.rootfolderListView = new RootFolderCollectionView({ collection : RootFolderCollection });
|
|
||||||
this.currentDirs.show(this.rootfolderListView);
|
|
||||||
|
|
||||||
this.listenTo(this.rootfolderListView, 'itemview:folderSelected', this._onFolderSelected);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_keydown : function(e) {
|
|
||||||
if (e.keyCode !== 13) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._addFolder();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var Layout = AsValidatedView.apply(Layout);
|
|
||||||
|
|
||||||
module.exports = Layout;
|
|
|
@ -1,36 +0,0 @@
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
|
||||||
<h3>Select Folder</h3>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body root-folders-modal">
|
|
||||||
<div class="validation-errors"></div>
|
|
||||||
<div class="alert alert-info">Enter the path that contains some or all of your TV series, you will be able to choose which series you want to import<button type="button" class="close" data-dismiss="alert">×</button></div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group">
|
|
||||||
|
|
||||||
<div class="col-md-12">
|
|
||||||
|
|
||||||
<div class="input-group">
|
|
||||||
<span class="input-group-addon"> <i class="icon-lidarr-folder-open"></i></span>
|
|
||||||
<input class="form-control x-path" type="text" validation-name="path" placeholder="Enter path to folder that contains your shows">
|
|
||||||
<span class="input-group-btn"><button class="btn btn-success x-add"><i class="icon-lidarr-ok"/></button></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row root-folders">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{{#if items}}
|
|
||||||
<h4>Recent Folders</h4>
|
|
||||||
{{/if}}
|
|
||||||
<div id="current-dirs" class="root-folders-list"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="btn" data-dismiss="modal">Close</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,8 +0,0 @@
|
||||||
var Backbone = require('backbone');
|
|
||||||
|
|
||||||
module.exports = Backbone.Model.extend({
|
|
||||||
urlRoot : window.NzbDrone.ApiRoot + '/rootfolder',
|
|
||||||
defaults : {
|
|
||||||
freeSpace : 0
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,11 +0,0 @@
|
||||||
<select class="col-md-4 form-control x-root-folder" validation-name="RootFolderPath">
|
|
||||||
{{#if this}}
|
|
||||||
{{#each this}}
|
|
||||||
<option value="{{id}}">{{path}}</option>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<option value="">Select Path</option>
|
|
||||||
{{/if}}
|
|
||||||
<option value="addNew">Add a different path</option>
|
|
||||||
</select>
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
var Marionette = require('marionette');
|
|
||||||
var SearchResultView = require('./SearchResultView');
|
|
||||||
|
|
||||||
module.exports = Marionette.CollectionView.extend({
|
|
||||||
itemView : SearchResultView,
|
|
||||||
|
|
||||||
initialize : function(options) {
|
|
||||||
this.isExisting = options.isExisting;
|
|
||||||
this.showing = 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
showAll : function() {
|
|
||||||
this.showingAll = true;
|
|
||||||
this.render();
|
|
||||||
},
|
|
||||||
|
|
||||||
showMore : function() {
|
|
||||||
this.showing += 5;
|
|
||||||
this.render();
|
|
||||||
|
|
||||||
return this.showing >= this.collection.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
appendHtml : function(collectionView, itemView, index) {
|
|
||||||
if (!this.isExisting || index < this.showing || index === 0) {
|
|
||||||
collectionView.$el.append(itemView.el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,297 +0,0 @@
|
||||||
var _ = require('underscore');
|
|
||||||
var vent = require('vent');
|
|
||||||
var AppLayout = require('../AppLayout');
|
|
||||||
var Backbone = require('backbone');
|
|
||||||
var Marionette = require('marionette');
|
|
||||||
var Profiles = require('../Profile/ProfileCollection');
|
|
||||||
var RootFolders = require('./RootFolders/RootFolderCollection');
|
|
||||||
var RootFolderLayout = require('./RootFolders/RootFolderLayout');
|
|
||||||
var ArtistCollection = require('../Artist/ArtistCollection');
|
|
||||||
var Config = require('../Config');
|
|
||||||
var Messenger = require('../Shared/Messenger');
|
|
||||||
var AsValidatedView = require('../Mixins/AsValidatedView');
|
|
||||||
|
|
||||||
require('jquery.dotdotdot');
|
|
||||||
|
|
||||||
var view = Marionette.ItemView.extend({
|
|
||||||
|
|
||||||
template : 'AddSeries/SearchResultViewTemplate',
|
|
||||||
|
|
||||||
ui : {
|
|
||||||
profile : '.x-profile',
|
|
||||||
rootFolder : '.x-root-folder',
|
|
||||||
seasonFolder : '.x-season-folder',
|
|
||||||
seriesType : '.x-series-type',
|
|
||||||
monitor : '.x-monitor',
|
|
||||||
monitorTooltip : '.x-monitor-tooltip',
|
|
||||||
addButton : '.x-add',
|
|
||||||
addAlbumButton : '.x-add-album',
|
|
||||||
addSearchButton : '.x-add-search',
|
|
||||||
addAlbumSearchButton : '.x-add-album-search',
|
|
||||||
overview : '.x-overview'
|
|
||||||
},
|
|
||||||
|
|
||||||
events : {
|
|
||||||
'click .x-add' : '_addWithoutSearch',
|
|
||||||
'click .x-add-album' : '_addWithoutSearch',
|
|
||||||
'click .x-add-search' : '_addAndSearch',
|
|
||||||
'click .x-add-album-search' : '_addAndSearch',
|
|
||||||
'change .x-profile' : '_profileChanged',
|
|
||||||
'change .x-root-folder' : '_rootFolderChanged',
|
|
||||||
'change .x-season-folder' : '_seasonFolderChanged',
|
|
||||||
'change .x-series-type' : '_seriesTypeChanged',
|
|
||||||
'change .x-monitor' : '_monitorChanged'
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize : function() {
|
|
||||||
|
|
||||||
if (!this.model) {
|
|
||||||
throw 'model is required';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.templateHelpers = {};
|
|
||||||
this._configureTemplateHelpers();
|
|
||||||
|
|
||||||
this.listenTo(vent, Config.Events.ConfigUpdatedEvent, this._onConfigUpdated);
|
|
||||||
this.listenTo(this.model, 'change', this.render);
|
|
||||||
this.listenTo(RootFolders, 'all', this._rootFoldersUpdated);
|
|
||||||
},
|
|
||||||
|
|
||||||
onRender : function() {
|
|
||||||
|
|
||||||
var defaultProfile = Config.getValue(Config.Keys.DefaultProfileId);
|
|
||||||
var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId);
|
|
||||||
var useSeasonFolder = Config.getValueBoolean(Config.Keys.UseSeasonFolder, true);
|
|
||||||
var defaultSeriesType = Config.getValue(Config.Keys.DefaultSeriesType, 'standard');
|
|
||||||
var defaultMonitorEpisodes = Config.getValue(Config.Keys.MonitorEpisodes, 'missing');
|
|
||||||
|
|
||||||
if (Profiles.get(defaultProfile)) {
|
|
||||||
this.ui.profile.val(defaultProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RootFolders.get(defaultRoot)) {
|
|
||||||
this.ui.rootFolder.val(defaultRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ui.seasonFolder.prop('checked', useSeasonFolder);
|
|
||||||
this.ui.seriesType.val(defaultSeriesType);
|
|
||||||
this.ui.monitor.val(defaultMonitorEpisodes);
|
|
||||||
|
|
||||||
//TODO: make this work via onRender, FM?
|
|
||||||
//works with onShow, but stops working after the first render
|
|
||||||
this.ui.overview.dotdotdot({
|
|
||||||
height : 120
|
|
||||||
});
|
|
||||||
|
|
||||||
this.templateFunction = Marionette.TemplateCache.get('AddSeries/MonitoringTooltipTemplate');
|
|
||||||
var content = this.templateFunction();
|
|
||||||
|
|
||||||
this.ui.monitorTooltip.popover({
|
|
||||||
content : content,
|
|
||||||
html : true,
|
|
||||||
trigger : 'hover',
|
|
||||||
title : 'Episode Monitoring Options',
|
|
||||||
placement : 'right',
|
|
||||||
container : this.$el
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_configureTemplateHelpers : function() {
|
|
||||||
var existingSeries = ArtistCollection.where({ iTunesId : this.model.get('itunesId') });
|
|
||||||
|
|
||||||
if (existingSeries.length > 0) {
|
|
||||||
this.templateHelpers.existing = existingSeries[0].toJSON();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.templateHelpers.profiles = Profiles.toJSON();
|
|
||||||
|
|
||||||
if (!this.model.get('isExisting')) {
|
|
||||||
this.templateHelpers.rootFolders = RootFolders.toJSON();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onConfigUpdated : function(options) {
|
|
||||||
if (options.key === Config.Keys.DefaultProfileId) {
|
|
||||||
this.ui.profile.val(options.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (options.key === Config.Keys.DefaultRootFolderId) {
|
|
||||||
this.ui.rootFolder.val(options.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (options.key === Config.Keys.UseSeasonFolder) {
|
|
||||||
this.ui.seasonFolder.prop('checked', options.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (options.key === Config.Keys.DefaultSeriesType) {
|
|
||||||
this.ui.seriesType.val(options.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (options.key === Config.Keys.MonitorEpisodes) {
|
|
||||||
this.ui.monitor.val(options.value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_profileChanged : function() {
|
|
||||||
Config.setValue(Config.Keys.DefaultProfileId, this.ui.profile.val());
|
|
||||||
},
|
|
||||||
|
|
||||||
_seasonFolderChanged : function() {
|
|
||||||
Config.setValue(Config.Keys.UseSeasonFolder, this.ui.seasonFolder.prop('checked'));
|
|
||||||
},
|
|
||||||
|
|
||||||
_rootFolderChanged : function() {
|
|
||||||
var rootFolderValue = this.ui.rootFolder.val();
|
|
||||||
if (rootFolderValue === 'addNew') {
|
|
||||||
var rootFolderLayout = new RootFolderLayout();
|
|
||||||
this.listenToOnce(rootFolderLayout, 'folderSelected', this._setRootFolder);
|
|
||||||
AppLayout.modalRegion.show(rootFolderLayout);
|
|
||||||
} else {
|
|
||||||
Config.setValue(Config.Keys.DefaultRootFolderId, rootFolderValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_seriesTypeChanged : function() {
|
|
||||||
Config.setValue(Config.Keys.DefaultSeriesType, this.ui.seriesType.val());
|
|
||||||
},
|
|
||||||
|
|
||||||
_monitorChanged : function() {
|
|
||||||
Config.setValue(Config.Keys.MonitorEpisodes, this.ui.monitor.val());
|
|
||||||
},
|
|
||||||
|
|
||||||
_setRootFolder : function(options) {
|
|
||||||
vent.trigger(vent.Commands.CloseModalCommand);
|
|
||||||
this.ui.rootFolder.val(options.model.id);
|
|
||||||
this._rootFolderChanged();
|
|
||||||
},
|
|
||||||
|
|
||||||
_addWithoutSearch : function(evt) {
|
|
||||||
console.log(evt);
|
|
||||||
this._addSeries(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_addAndSearch : function() {
|
|
||||||
this._addSeries(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
_addSeries : function(searchForMissing) {
|
|
||||||
// TODO: Refactor to handle multiple add buttons/albums
|
|
||||||
var addButton = this.ui.addButton;
|
|
||||||
var addSearchButton = this.ui.addSearchButton;
|
|
||||||
console.log('_addSeries, searchForMissing=', searchForMissing);
|
|
||||||
|
|
||||||
addButton.addClass('disabled');
|
|
||||||
addSearchButton.addClass('disabled');
|
|
||||||
|
|
||||||
var profile = this.ui.profile.val();
|
|
||||||
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
|
||||||
var seriesType = this.ui.seriesType.val(); // Perhaps make this a differnitator between artist or Album?
|
|
||||||
var seasonFolder = this.ui.seasonFolder.prop('checked');
|
|
||||||
|
|
||||||
var options = this._getAddSeriesOptions();
|
|
||||||
options.searchForMissing = searchForMissing;
|
|
||||||
|
|
||||||
this.model.set({
|
|
||||||
profileId : profile,
|
|
||||||
rootFolderPath : rootFolderPath,
|
|
||||||
seasonFolder : seasonFolder,
|
|
||||||
seriesType : seriesType,
|
|
||||||
addOptions : options,
|
|
||||||
monitored : true
|
|
||||||
}, { silent : true });
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var promise = this.model.save();
|
|
||||||
|
|
||||||
if (searchForMissing) {
|
|
||||||
this.ui.addSearchButton.spinForPromise(promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
this.ui.addButton.spinForPromise(promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.always(function() {
|
|
||||||
addButton.removeClass('disabled');
|
|
||||||
addSearchButton.removeClass('disabled');
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.done(function() {
|
|
||||||
console.log('[SearchResultView] _addSeries promise resolve:', self.model);
|
|
||||||
ArtistCollection.add(self.model);
|
|
||||||
|
|
||||||
self.close();
|
|
||||||
|
|
||||||
Messenger.show({
|
|
||||||
message : 'Added: ' + self.model.get('artistName'),
|
|
||||||
actions : {
|
|
||||||
goToSeries : {
|
|
||||||
label : 'Go to Artist',
|
|
||||||
action : function() {
|
|
||||||
Backbone.history.navigate('/artist/' + self.model.get('artistSlug'), { trigger : true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hideAfter : 8,
|
|
||||||
hideOnNavigate : true
|
|
||||||
});
|
|
||||||
|
|
||||||
vent.trigger(vent.Events.SeriesAdded, { series : self.model });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_rootFoldersUpdated : function() {
|
|
||||||
this._configureTemplateHelpers();
|
|
||||||
this.render();
|
|
||||||
},
|
|
||||||
|
|
||||||
_getAddSeriesOptions : function() {
|
|
||||||
var monitor = this.ui.monitor.val();
|
|
||||||
//[TODO]: Refactor for albums
|
|
||||||
var lastSeason = _.max(this.model.get('seasons'), 'seasonNumber');
|
|
||||||
var firstSeason = _.min(_.reject(this.model.get('seasons'), { seasonNumber : 0 }), 'seasonNumber');
|
|
||||||
|
|
||||||
//this.model.setSeasonPass(firstSeason.seasonNumber); // TODO
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
ignoreEpisodesWithFiles : false,
|
|
||||||
ignoreEpisodesWithoutFiles : false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (monitor === 'all') {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (monitor === 'future') {
|
|
||||||
options.ignoreEpisodesWithFiles = true;
|
|
||||||
options.ignoreEpisodesWithoutFiles = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*else if (monitor === 'latest') {
|
|
||||||
this.model.setSeasonPass(lastSeason.seasonNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (monitor === 'first') {
|
|
||||||
this.model.setSeasonPass(lastSeason.seasonNumber + 1);
|
|
||||||
this.model.setSeasonMonitored(firstSeason.seasonNumber);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
else if (monitor === 'missing') {
|
|
||||||
options.ignoreEpisodesWithFiles = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (monitor === 'existing') {
|
|
||||||
options.ignoreEpisodesWithoutFiles = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*else if (monitor === 'none') {
|
|
||||||
this.model.setSeasonPass(lastSeason.seasonNumber + 1);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AsValidatedView.apply(view);
|
|
||||||
|
|
||||||
module.exports = view;
|
|
|
@ -1,146 +0,0 @@
|
||||||
<div class="search-item {{#unless isExisting}}search-item-new{{/unless}}">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-10">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<h2 class="series-title">
|
|
||||||
<!--{{titleWithYear}}-->
|
|
||||||
{{artistName}}
|
|
||||||
|
|
||||||
<!--<span class="labels">
|
|
||||||
<span class="label label-default">{{network}}</span>
|
|
||||||
{{#unless_eq status compare="continuing"}}
|
|
||||||
<span class="label label-danger">Ended</span>
|
|
||||||
{{/unless_eq}}
|
|
||||||
</span>-->
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="row new-series-overview x-overview">
|
|
||||||
<div class="col-md-12 overview-internal">
|
|
||||||
{{overview}}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
<div class="row">
|
|
||||||
{{#unless existing}}
|
|
||||||
{{#unless path}}
|
|
||||||
<div class="form-group col-md-4">
|
|
||||||
<label>Path</label>
|
|
||||||
{{> RootFolderSelectionPartial rootFolders}}
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
<div class="form-group col-md-2">
|
|
||||||
<label>Monitor <i class="icon-lidarr-form-info monitor-tooltip x-monitor-tooltip"></i></label>
|
|
||||||
<select class="form-control col-md-2 x-monitor">
|
|
||||||
<option value="all">All</option>
|
|
||||||
<option value="future">Future</option>
|
|
||||||
<option value="missing">Missing</option>
|
|
||||||
<option value="existing">Existing</option>
|
|
||||||
<option value="none">None</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group col-md-2">
|
|
||||||
<label>Profile</label>
|
|
||||||
{{> ProfileSelectionPartial profiles}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--<div class="form-group col-md-2">
|
|
||||||
|
|
||||||
</div>-->
|
|
||||||
|
|
||||||
<div class="form-group col-md-2">
|
|
||||||
<label>Album Folders</label>
|
|
||||||
|
|
||||||
<div class="input-group">
|
|
||||||
<label class="checkbox toggle well">
|
|
||||||
<input type="checkbox" class="x-season-folder"/>
|
|
||||||
<p>
|
|
||||||
<span>Yes</span>
|
|
||||||
<span>No</span>
|
|
||||||
</p>
|
|
||||||
<div class="btn btn-primary slide-button"/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
{{#unless existing}}
|
|
||||||
{{#if artistName}}
|
|
||||||
<div class="form-group col-md-2 col-md-offset-10">
|
|
||||||
<!--Uncomment if we need to add even more controls to add series-->
|
|
||||||
<!--<label style="visibility: hidden">Add</label>-->
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn btn-success add x-add" title="Add" data-artist="{{artistName}}">
|
|
||||||
<i class="icon-lidarr-add"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-success add x-add-search" title="Add and Search for missing tracks" data-artist="{{artistName}}">
|
|
||||||
<i class="icon-lidarr-search"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="col-md-2 col-md-offset-10">
|
|
||||||
<button class="btn add-series disabled">
|
|
||||||
Add
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<div class="col-md-2 col-md-offset-10">
|
|
||||||
<a class="btn btn-default" href="{{route}}">
|
|
||||||
Already Exists
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
{{#each albums}}
|
|
||||||
<div class="col-md-12" style="border:1px dashed black;">
|
|
||||||
<div class="col-md-2">
|
|
||||||
<a href="{{artworkUrl}}" target="_blank">
|
|
||||||
<!-- {{poster}} -->
|
|
||||||
<img class="album-poster" src="{{artworkUrl}}">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<h2>{{albumName}} ({{year}})</h2>
|
|
||||||
{{#unless existing}}
|
|
||||||
{{#if albumName}}
|
|
||||||
<div class="form-group col-md-offset-10">
|
|
||||||
<!--Uncomment if we need to add even more controls to add series-->
|
|
||||||
<!--<label style="visibility: hidden">Add</label>-->
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn btn-success add x-add-album" title="Add" data-album="{{albumName}}">
|
|
||||||
<i class="icon-lidarr-add"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-success add x-add-album-search" title="Add and Search for missing tracks" data-album="{{albumName}}">
|
|
||||||
<i class="icon-lidarr-search"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="col-md-2 col-md-offset-10">
|
|
||||||
<button class="btn add-series disabled">
|
|
||||||
Add
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<div class="col-md-2 col-md-offset-10">
|
|
||||||
<a class="btn btn-default" href="{{route}}">
|
|
||||||
Already Exists
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<select class="form-control col-md-2 x-series-type" name="seriesType">
|
|
||||||
<option value="standard">Standard</option>
|
|
||||||
<option value="daily">Daily</option>
|
|
||||||
<option value="anime">Anime</option>
|
|
||||||
</select>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<select class="form-control col-md-2 starting-season x-starting-season">
|
|
||||||
|
|
||||||
|
|
||||||
{{#each this}}
|
|
||||||
{{#if_eq seasonNumber compare="0"}}
|
|
||||||
<option value="{{seasonNumber}}">Specials</option>
|
|
||||||
{{else}}
|
|
||||||
<option value="{{seasonNumber}}">Season {{seasonNumber}}</option>
|
|
||||||
{{/if_eq}}
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
<option value="5000000">None</option>
|
|
||||||
</select>
|
|
|
@ -1,181 +0,0 @@
|
||||||
@import "../Shared/Styles/card.less";
|
|
||||||
@import "../Shared/Styles/clickable.less";
|
|
||||||
|
|
||||||
#add-series-screen {
|
|
||||||
.existing-series {
|
|
||||||
|
|
||||||
.card();
|
|
||||||
margin : 30px 0px;
|
|
||||||
|
|
||||||
.unmapped-folder-path {
|
|
||||||
padding: 20px;
|
|
||||||
margin-left : 0px;
|
|
||||||
font-weight : 100;
|
|
||||||
font-size : 25px;
|
|
||||||
text-align : center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-series-loadmore {
|
|
||||||
font-size : 30px;
|
|
||||||
font-weight : 300;
|
|
||||||
padding-top : 10px;
|
|
||||||
padding-bottom : 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-series {
|
|
||||||
.search-item {
|
|
||||||
.card();
|
|
||||||
margin : 40px 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-series-search {
|
|
||||||
margin-top : 20px;
|
|
||||||
margin-bottom : 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-item {
|
|
||||||
|
|
||||||
padding-bottom : 20px;
|
|
||||||
|
|
||||||
.series-title {
|
|
||||||
margin-top : 5px;
|
|
||||||
|
|
||||||
.labels {
|
|
||||||
margin-left : 10px;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size : 12px;
|
|
||||||
vertical-align : middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.year {
|
|
||||||
font-style : italic;
|
|
||||||
color : #aaaaaa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-series-overview {
|
|
||||||
overflow : hidden;
|
|
||||||
height : 103px;
|
|
||||||
|
|
||||||
.overview-internal {
|
|
||||||
overflow : hidden;
|
|
||||||
height : 80px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.series-poster {
|
|
||||||
min-width : 138px;
|
|
||||||
min-height : 203px;
|
|
||||||
max-width : 138px;
|
|
||||||
max-height : 203px;
|
|
||||||
margin : 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-poster {
|
|
||||||
min-width : 100px;
|
|
||||||
min-height : 100px;
|
|
||||||
max-width : 138px;
|
|
||||||
max-height : 203px;
|
|
||||||
margin : 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color : #343434;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration : none;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
font-size : 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox {
|
|
||||||
margin-top : 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add {
|
|
||||||
i {
|
|
||||||
&:before {
|
|
||||||
color : #ffffff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.monitor-tooltip {
|
|
||||||
margin-left : 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-folders {
|
|
||||||
margin : 30px 0px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
|
||||||
color : #999999;
|
|
||||||
font-style : italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monitor-tooltip-contents {
|
|
||||||
padding-bottom : 0px;
|
|
||||||
|
|
||||||
dd {
|
|
||||||
padding-bottom : 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
li.add-new {
|
|
||||||
.clickable;
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
padding: 3px 20px;
|
|
||||||
clear: both;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 20px;
|
|
||||||
color: rgb(51, 51, 51);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.add-new:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
color: rgb(255, 255, 255);
|
|
||||||
background-color: rgb(0, 129, 194);
|
|
||||||
}
|
|
||||||
|
|
||||||
.root-folders-modal {
|
|
||||||
overflow : visible;
|
|
||||||
|
|
||||||
.root-folders-list {
|
|
||||||
overflow-y : auto;
|
|
||||||
max-height : 300px;
|
|
||||||
|
|
||||||
i {
|
|
||||||
.clickable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.validation-errors {
|
|
||||||
display : none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
.form-control {
|
|
||||||
background-color : white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.root-folders {
|
|
||||||
margin-top : 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-folder {
|
|
||||||
.clickable();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,9 +2,27 @@ var Backbone = require('backbone');
|
||||||
var AlbumModel = require('./AlbumModel');
|
var AlbumModel = require('./AlbumModel');
|
||||||
|
|
||||||
module.exports = Backbone.Collection.extend({
|
module.exports = Backbone.Collection.extend({
|
||||||
|
url : window.NzbDrone.ApiRoot + '/album',
|
||||||
model : AlbumModel,
|
model : AlbumModel,
|
||||||
|
|
||||||
comparator : function(season) {
|
originalFetch : Backbone.Collection.prototype.fetch,
|
||||||
return -season.get('seasonNumber');
|
|
||||||
|
initialize : function(options) {
|
||||||
|
this.artistId = options.artistId;
|
||||||
|
this.models = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
fetch : function(options) {
|
||||||
|
if (!this.artistId) {
|
||||||
|
throw 'artistId is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
options.data = { artistId : this.artistId };
|
||||||
|
|
||||||
|
return this.originalFetch.call(this, options);
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -1,11 +1,8 @@
|
||||||
var Backbone = require('backbone');
|
var Backbone = require('backbone');
|
||||||
|
|
||||||
module.exports = Backbone.Model.extend({
|
module.exports = Backbone.Model.extend({
|
||||||
defaults : {
|
|
||||||
seasonNumber : 0
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize : function() {
|
defaults : {
|
||||||
this.set('id', this.get('seasonNumber'));
|
artistId : 0
|
||||||
}
|
},
|
||||||
});
|
});
|
|
@ -67,7 +67,7 @@ var Collection = PageableCollection.extend({
|
||||||
'missing' : [
|
'missing' : [
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
function(model) { return model.get('episodeCount') !== model.get('episodeFileCount'); }
|
function(model) { return model.get('trackCount') !== model.get('trackFileCount'); }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -96,12 +96,12 @@ var Collection = PageableCollection.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
percentOfEpisodes : {
|
percentOfTracks : {
|
||||||
sortValue : function(model, attr) {
|
sortValue : function(model, attr) {
|
||||||
var percentOfEpisodes = model.get(attr);
|
var percentOfTracks = model.get(attr);
|
||||||
var episodeCount = model.get('episodeCount');
|
var trackCount = model.get('trackCount');
|
||||||
|
|
||||||
return percentOfEpisodes + episodeCount / 1000000;
|
return percentOfTracks + trackCount / 1000000;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,15 @@ module.exports = Backbone.Model.extend({
|
||||||
urlRoot : window.NzbDrone.ApiRoot + '/artist',
|
urlRoot : window.NzbDrone.ApiRoot + '/artist',
|
||||||
|
|
||||||
defaults : {
|
defaults : {
|
||||||
episodeFileCount : 0,
|
trackFileCount : 0,
|
||||||
episodeCount : 0,
|
trackCount : 0,
|
||||||
isExisting : false,
|
isExisting : false,
|
||||||
status : 0
|
status : 0
|
||||||
},
|
},
|
||||||
|
|
||||||
setAlbumsMonitored : function(albumName) {
|
setAlbumsMonitored : function(albumName) {
|
||||||
_.each(this.get('albums'), function(album) {
|
_.each(this.get('albums'), function(album) {
|
||||||
|
console.log(album);
|
||||||
if (album.albumName === albumName) {
|
if (album.albumName === albumName) {
|
||||||
album.monitored = !album.monitored;
|
album.monitored = !album.monitored;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var Marionette = require('marionette');
|
var Marionette = require('marionette');
|
||||||
var SeasonLayout = require('./AlbumLayout');
|
var AlbumLayout = require('./AlbumLayout');
|
||||||
var AsSortedCollectionView = require('../../Mixins/AsSortedCollectionView');
|
var AsSortedCollectionView = require('../../Mixins/AsSortedCollectionView');
|
||||||
|
|
||||||
var view = Marionette.CollectionView.extend({
|
var view = Marionette.CollectionView.extend({
|
||||||
|
|
||||||
itemView : SeasonLayout,
|
itemView : AlbumLayout,
|
||||||
|
|
||||||
initialize : function(options) {
|
initialize : function(options) {
|
||||||
if (!options.trackCollection) {
|
if (!options.trackCollection) {
|
||||||
throw 'trackCollection is needed';
|
throw 'trackCollection is needed';
|
||||||
}
|
}
|
||||||
|
console.log(options);
|
||||||
|
this.albumCollection = options.collection;
|
||||||
this.trackCollection = options.trackCollection;
|
this.trackCollection = options.trackCollection;
|
||||||
this.artist = options.artist;
|
this.artist = options.artist;
|
||||||
},
|
},
|
||||||
|
|
||||||
itemViewOptions : function() {
|
itemViewOptions : function() {
|
||||||
return {
|
return {
|
||||||
|
albumCollection : this.albumCollection,
|
||||||
trackCollection : this.trackCollection,
|
trackCollection : this.trackCollection,
|
||||||
artist : this.artist
|
artist : this.artist
|
||||||
};
|
};
|
||||||
|
|
11
src/UI/Artist/Details/AlbumInfoView.js
Normal file
11
src/UI/Artist/Details/AlbumInfoView.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
var Marionette = require('marionette');
|
||||||
|
|
||||||
|
module.exports = Marionette.ItemView.extend({
|
||||||
|
template : 'Artist/Details/AlbumInfoViewTemplate',
|
||||||
|
|
||||||
|
initialize : function(options) {
|
||||||
|
|
||||||
|
this.listenTo(this.model, 'change', this.render);
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
41
src/UI/Artist/Details/AlbumInfoViewTemplate.hbs
Normal file
41
src/UI/Artist/Details/AlbumInfoViewTemplate.hbs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
{{profile profileId}}
|
||||||
|
|
||||||
|
{{#if label}}
|
||||||
|
<span class="label label-info">{{label}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span class="label label-info">{{path}}</span>
|
||||||
|
|
||||||
|
{{#if ratings}}
|
||||||
|
<span class="label label-info" title="{{ratings.votes}} vote{{#if_gt ratings.votes compare="1"}}s{{/if_gt}}">{{ratings.value}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<span class="album-info-links">
|
||||||
|
<a href="{{MBAlbumUrl}}" class="label label-info">MusicBrainz</a>
|
||||||
|
|
||||||
|
{{#if tadbId}}
|
||||||
|
<a href="{{TADBAlbumUrl}}" class="label label-info">The AudioDB</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if discogsId}}
|
||||||
|
<a href="{{discogsAlbumUrl}}" class="label label-info">Discogs</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if amId}}
|
||||||
|
<a href="{{allMusicAlbumUrl}}" class="label label-info">AllMusic</a>
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if tags}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{{tagDisplay tags}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
|
@ -1,39 +1,44 @@
|
||||||
var vent = require('vent');
|
var vent = require('vent');
|
||||||
var Marionette = require('marionette');
|
var Marionette = require('marionette');
|
||||||
var Backgrid = require('backgrid');
|
var Backgrid = require('backgrid');
|
||||||
var ToggleCell = require('../../Cells/EpisodeMonitoredCell');
|
var ToggleCell = require('../../Cells/TrackMonitoredCell');
|
||||||
var EpisodeTitleCell = require('../../Cells/EpisodeTitleCell');
|
var TrackTitleCell = require('../../Cells/TrackTitleCell');
|
||||||
|
var TrackExplicitCell = require('../../Cells/TrackExplicitCell');
|
||||||
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||||
var EpisodeStatusCell = require('../../Cells/EpisodeStatusCell');
|
var TrackStatusCell = require('../../Cells/TrackStatusCell');
|
||||||
var EpisodeActionsCell = require('../../Cells/EpisodeActionsCell');
|
var TrackActionsCell = require('../../Cells/TrackActionsCell');
|
||||||
var TrackNumberCell = require('./TrackNumberCell');
|
var TrackNumberCell = require('./TrackNumberCell');
|
||||||
var TrackWarningCell = require('./TrackWarningCell');
|
var TrackWarningCell = require('./TrackWarningCell');
|
||||||
|
var TrackRatingCell = require('./TrackRatingCell');
|
||||||
|
var AlbumInfoView = require('./AlbumInfoView');
|
||||||
var CommandController = require('../../Commands/CommandController');
|
var CommandController = require('../../Commands/CommandController');
|
||||||
var EpisodeFileEditorLayout = require('../../EpisodeFile/Editor/EpisodeFileEditorLayout');
|
//var TrackFileEditorLayout = require('../../TrackFile/Editor/TrackFileEditorLayout');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var Messenger = require('../../Shared/Messenger');
|
var Messenger = require('../../Shared/Messenger');
|
||||||
|
|
||||||
module.exports = Marionette.Layout.extend({
|
module.exports = Marionette.Layout.extend({
|
||||||
template : 'Artist/Details/ArtistLayoutTemplate',
|
template : 'Artist/Details/AlbumLayoutTemplate',
|
||||||
|
|
||||||
ui : {
|
ui : {
|
||||||
seasonSearch : '.x-album-search',
|
albumSearch : '.x-album-search',
|
||||||
seasonMonitored : '.x-album-monitored',
|
albumMonitored : '.x-album-monitored',
|
||||||
seasonRename : '.x-album-rename'
|
albumRename : '.x-album-rename',
|
||||||
|
cover : '.x-album-cover'
|
||||||
},
|
},
|
||||||
|
|
||||||
events : {
|
events : {
|
||||||
'click .x-album-episode-file-editor' : '_openEpisodeFileEditor',
|
'click .x-track-file-editor' : '_openTrackFileEditor',
|
||||||
'click .x-album-monitored' : '_albumMonitored',
|
'click .x-album-monitored' : '_albumMonitored',
|
||||||
'click .x-album-search' : '_albumSearch',
|
'click .x-album-search' : '_albumSearch',
|
||||||
'click .x-album-rename' : '_albumRename',
|
'click .x-album-rename' : '_albumRename',
|
||||||
'click .x-show-hide-episodes' : '_showHideEpisodes',
|
'click .x-show-hide-tracks' : '_showHideTracks',
|
||||||
'dblclick .artist-album h2' : '_showHideEpisodes'
|
'dblclick .artist-album h2' : '_showHideTracks'
|
||||||
},
|
},
|
||||||
|
|
||||||
regions : {
|
regions : {
|
||||||
episodeGrid : '.x-episode-grid'
|
trackGrid : '.x-track-grid',
|
||||||
|
albumInfo : '#album-info'
|
||||||
},
|
},
|
||||||
|
|
||||||
columns : [
|
columns : [
|
||||||
|
@ -61,110 +66,135 @@ module.exports = Marionette.Layout.extend({
|
||||||
{
|
{
|
||||||
name : 'this',
|
name : 'this',
|
||||||
label : 'Title',
|
label : 'Title',
|
||||||
hideSeriesLink : true,
|
hideArtistLink : true,
|
||||||
cell : EpisodeTitleCell,
|
cell : TrackTitleCell,
|
||||||
sortable : false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name : 'airDateUtc',
|
|
||||||
label : 'Air Date',
|
|
||||||
cell : RelativeDateCell
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name : 'status',
|
|
||||||
label : 'Status',
|
|
||||||
cell : EpisodeStatusCell,
|
|
||||||
sortable : false
|
sortable : false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'this',
|
name : 'this',
|
||||||
label : '',
|
label : 'Rating',
|
||||||
cell : EpisodeActionsCell,
|
cell : TrackRatingCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'this',
|
||||||
|
label : 'Content',
|
||||||
|
cell : TrackExplicitCell
|
||||||
|
},
|
||||||
|
//{
|
||||||
|
// name : 'airDateUtc',
|
||||||
|
// label : 'Air Date',
|
||||||
|
// cell : RelativeDateCell
|
||||||
|
//},
|
||||||
|
{
|
||||||
|
name : 'status',
|
||||||
|
label : 'Status',
|
||||||
|
cell : TrackStatusCell,
|
||||||
sortable : false
|
sortable : false
|
||||||
}
|
},
|
||||||
|
//{
|
||||||
|
// name : 'this',
|
||||||
|
// label : '',
|
||||||
|
// cell : TrackActionsCell,
|
||||||
|
// sortable : false
|
||||||
|
//}
|
||||||
],
|
],
|
||||||
|
|
||||||
templateHelpers : function() {
|
templateHelpers : function() {
|
||||||
var episodeCount = this.episodeCollection.filter(function(episode) {
|
var trackCount = this.trackCollection.filter(function(track) {
|
||||||
return episode.get('hasFile') || episode.get('monitored') && moment(episode.get('airDateUtc')).isBefore(moment());
|
return track.get('hasFile') || track.get('monitored');
|
||||||
}).length;
|
}).length;
|
||||||
|
|
||||||
var episodeFileCount = this.episodeCollection.where({ hasFile : true }).length;
|
var trackFileCount = this.trackCollection.where({ hasFile : true }).length;
|
||||||
var percentOfEpisodes = 100;
|
var percentOfTracks = 100;
|
||||||
|
|
||||||
if (episodeCount > 0) {
|
if (trackCount > 0) {
|
||||||
percentOfEpisodes = episodeFileCount / episodeCount * 100;
|
percentOfTracks = trackFileCount / trackCount * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
showingEpisodes : this.showingEpisodes,
|
showingTracks : this.showingTracks,
|
||||||
episodeCount : episodeCount,
|
trackCount : trackCount,
|
||||||
episodeFileCount : episodeFileCount,
|
trackFileCount : trackFileCount,
|
||||||
percentOfEpisodes : percentOfEpisodes
|
percentOfTracks : percentOfTracks
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize : function(options) {
|
initialize : function(options) {
|
||||||
if (!options.episodeCollection) {
|
if (!options.trackCollection) {
|
||||||
throw 'episodeCollection is required';
|
throw 'trackCollection is required';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.series = options.series;
|
this.artist = options.artist;
|
||||||
this.fullEpisodeCollection = options.episodeCollection;
|
this.fullTrackCollection = options.trackCollection;
|
||||||
this.episodeCollection = this.fullEpisodeCollection.bySeason(this.model.get('seasonNumber'));
|
|
||||||
this._updateEpisodeCollection();
|
|
||||||
|
|
||||||
this.showingEpisodes = this._shouldShowEpisodes();
|
this.trackCollection = this.fullTrackCollection.byAlbum(this.model.get('id'));
|
||||||
|
this._updateTrackCollection();
|
||||||
|
|
||||||
|
console.log(options);
|
||||||
|
|
||||||
|
this.showingTracks = this._shouldShowTracks();
|
||||||
|
|
||||||
this.listenTo(this.model, 'sync', this._afterSeasonMonitored);
|
this.listenTo(this.model, 'sync', this._afterSeasonMonitored);
|
||||||
this.listenTo(this.episodeCollection, 'sync', this.render);
|
this.listenTo(this.trackCollection, 'sync', this.render);
|
||||||
|
this.listenTo(this.fullTrackCollection, 'sync', this._refreshTracks);
|
||||||
this.listenTo(this.fullEpisodeCollection, 'sync', this._refreshEpisodes);
|
this.listenTo(this.model, 'change:images', this._updateImages);
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender : function() {
|
onRender : function() {
|
||||||
if (this.showingEpisodes) {
|
if (this.showingTracks) {
|
||||||
this._showEpisodes();
|
this._showTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._setSeasonMonitoredState();
|
this._showAlbumInfo();
|
||||||
|
|
||||||
|
this._setAlbumMonitoredState();
|
||||||
|
|
||||||
CommandController.bindToCommand({
|
CommandController.bindToCommand({
|
||||||
element : this.ui.seasonSearch,
|
element : this.ui.albumSearch,
|
||||||
command : {
|
command : {
|
||||||
name : 'seasonSearch',
|
name : 'albumSearch',
|
||||||
seriesId : this.series.id,
|
artistId : this.artist.id,
|
||||||
seasonNumber : this.model.get('seasonNumber')
|
albumId : this.model.get('albumId')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CommandController.bindToCommand({
|
CommandController.bindToCommand({
|
||||||
element : this.ui.seasonRename,
|
element : this.ui.albumRename,
|
||||||
command : {
|
command : {
|
||||||
name : 'renameFiles',
|
name : 'renameFiles',
|
||||||
seriesId : this.series.id,
|
artistId : this.artist.id,
|
||||||
seasonNumber : this.model.get('seasonNumber')
|
albumId : this.model.get('albumId')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_seasonSearch : function() {
|
_getImage : function(type) {
|
||||||
CommandController.Execute('seasonSearch', {
|
var image = _.where(this.model.get('images'), { coverType : type });
|
||||||
name : 'seasonSearch',
|
|
||||||
seriesId : this.series.id,
|
if (image && image[0]) {
|
||||||
seasonNumber : this.model.get('seasonNumber')
|
return image[0].url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
_albumSearch : function() {
|
||||||
|
CommandController.Execute('albumSearch', {
|
||||||
|
name : 'albumSearch',
|
||||||
|
artistId : this.artist.id,
|
||||||
|
albumId : this.model.get('albumId')
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_seasonRename : function() {
|
_albumRename : function() {
|
||||||
vent.trigger(vent.Commands.ShowRenamePreview, {
|
vent.trigger(vent.Commands.ShowRenamePreview, {
|
||||||
series : this.series,
|
artist : this.artist,
|
||||||
seasonNumber : this.model.get('seasonNumber')
|
albumId : this.model.get('albumId')
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_seasonMonitored : function() {
|
_albumMonitored : function() {
|
||||||
if (!this.series.get('monitored')) {
|
if (!this.artist.get('monitored')) {
|
||||||
|
|
||||||
Messenger.show({
|
Messenger.show({
|
||||||
message : 'Unable to change monitored state when series is not monitored',
|
message : 'Unable to change monitored state when series is not monitored',
|
||||||
|
@ -176,80 +206,86 @@ module.exports = Marionette.Layout.extend({
|
||||||
|
|
||||||
var name = 'monitored';
|
var name = 'monitored';
|
||||||
this.model.set(name, !this.model.get(name));
|
this.model.set(name, !this.model.get(name));
|
||||||
this.series.setSeasonMonitored(this.model.get('seasonNumber'));
|
this.artist.setSeasonMonitored(this.model.get('albumId'));
|
||||||
|
|
||||||
var savePromise = this.series.save().always(this._afterSeasonMonitored.bind(this));
|
var savePromise = this.artist.save().always(this._afterSeasonMonitored.bind(this));
|
||||||
|
|
||||||
this.ui.seasonMonitored.spinForPromise(savePromise);
|
this.ui.albumMonitored.spinForPromise(savePromise);
|
||||||
},
|
},
|
||||||
|
|
||||||
_afterSeasonMonitored : function() {
|
_afterSeasonMonitored : function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
_.each(this.episodeCollection.models, function(episode) {
|
_.each(this.trackCollection.models, function(track) {
|
||||||
episode.set({ monitored : self.model.get('monitored') });
|
track.set({ monitored : self.model.get('monitored') });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
_setSeasonMonitoredState : function() {
|
_setAlbumMonitoredState : function() {
|
||||||
this.ui.seasonMonitored.removeClass('icon-lidarr-spinner fa-spin');
|
this.ui.albumMonitored.removeClass('icon-lidarr-spinner fa-spin');
|
||||||
|
|
||||||
if (this.model.get('monitored')) {
|
if (this.model.get('monitored')) {
|
||||||
this.ui.seasonMonitored.addClass('icon-lidarr-monitored');
|
this.ui.albumMonitored.addClass('icon-lidarr-monitored');
|
||||||
this.ui.seasonMonitored.removeClass('icon-lidarr-unmonitored');
|
this.ui.albumMonitored.removeClass('icon-lidarr-unmonitored');
|
||||||
} else {
|
} else {
|
||||||
this.ui.seasonMonitored.addClass('icon-lidarr-unmonitored');
|
this.ui.albumMonitored.addClass('icon-lidarr-unmonitored');
|
||||||
this.ui.seasonMonitored.removeClass('icon-lidarr-monitored');
|
this.ui.albumMonitored.removeClass('icon-lidarr-monitored');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_showEpisodes : function() {
|
_showTracks : function() {
|
||||||
this.episodeGrid.show(new Backgrid.Grid({
|
this.trackGrid.show(new Backgrid.Grid({
|
||||||
columns : this.columns,
|
columns : this.columns,
|
||||||
collection : this.episodeCollection,
|
collection : this.trackCollection,
|
||||||
className : 'table table-hover season-grid'
|
className : 'table table-hover track-grid'
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_shouldShowEpisodes : function() {
|
_showAlbumInfo : function() {
|
||||||
var startDate = moment().add('month', -1);
|
this.albumInfo.show(new AlbumInfoView({
|
||||||
var endDate = moment().add('year', 1);
|
model : this.model
|
||||||
|
}));
|
||||||
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() {
|
_shouldShowTracks : function() {
|
||||||
if (this.showingEpisodes) {
|
var startDate = moment().add('month', -1);
|
||||||
this.showingEpisodes = false;
|
var endDate = moment().add('year', 1);
|
||||||
this.episodeGrid.close();
|
return true;
|
||||||
|
//return this.trackCollection.some(function(track) {
|
||||||
|
// var airDate = track.get('releasedDate');
|
||||||
|
|
||||||
|
// if (airDate) {
|
||||||
|
// var airDateMoment = moment(airDate);
|
||||||
|
|
||||||
|
// if (airDateMoment.isAfter(startDate) && airDateMoment.isBefore(endDate)) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
//});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showHideTracks : function() {
|
||||||
|
if (this.showingTracks) {
|
||||||
|
this.showingTracks = false;
|
||||||
|
this.trackGrid.close();
|
||||||
} else {
|
} else {
|
||||||
this.showingEpisodes = true;
|
this.showingTracks = true;
|
||||||
this._showEpisodes();
|
this._showTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.templateHelpers.showingEpisodes = this.showingEpisodes;
|
this.templateHelpers.showingTracks = this.showingTracks;
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
_episodeMonitoredToggled : function(options) {
|
_trackMonitoredToggled : function(options) {
|
||||||
var model = options.model;
|
var model = options.model;
|
||||||
var shiftKey = options.shiftKey;
|
var shiftKey = options.shiftKey;
|
||||||
|
|
||||||
if (!this.episodeCollection.get(model.get('id'))) {
|
if (!this.trackCollection.get(model.get('id'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,45 +293,53 @@ module.exports = Marionette.Layout.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastToggled = this.episodeCollection.lastToggled;
|
var lastToggled = this.trackCollection.lastToggled;
|
||||||
|
|
||||||
if (!lastToggled) {
|
if (!lastToggled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentIndex = this.episodeCollection.indexOf(model);
|
var currentIndex = this.trackCollection.indexOf(model);
|
||||||
var lastIndex = this.episodeCollection.indexOf(lastToggled);
|
var lastIndex = this.trackCollection.indexOf(lastToggled);
|
||||||
|
|
||||||
var low = Math.min(currentIndex, lastIndex);
|
var low = Math.min(currentIndex, lastIndex);
|
||||||
var high = Math.max(currentIndex, lastIndex);
|
var high = Math.max(currentIndex, lastIndex);
|
||||||
var range = _.range(low + 1, high);
|
var range = _.range(low + 1, high);
|
||||||
|
|
||||||
this.episodeCollection.lastToggled = model;
|
this.trackCollection.lastToggled = model;
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateEpisodeCollection : function() {
|
_updateTrackCollection : function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.episodeCollection.add(this.fullEpisodeCollection.bySeason(this.model.get('seasonNumber')).models, { merge : true });
|
this.trackCollection.add(this.fullTrackCollection.byAlbum(this.model.get('albumId')).models, { merge : true });
|
||||||
|
|
||||||
this.episodeCollection.each(function(model) {
|
this.trackCollection.each(function(model) {
|
||||||
model.episodeCollection = self.episodeCollection;
|
model.trackCollection = self.trackCollection;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_refreshEpisodes : function() {
|
_updateImages : function () {
|
||||||
this._updateEpisodeCollection();
|
var cover = this._getImage('cover');
|
||||||
this.episodeCollection.fullCollection.sort();
|
|
||||||
|
if (cover) {
|
||||||
|
this.ui.poster.attr('src', cover);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_refreshTracks : function() {
|
||||||
|
this._updateTrackCollection();
|
||||||
|
this.trackCollection.fullCollection.sort();
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
_openEpisodeFileEditor : function() {
|
_openTrackFileEditor : function() {
|
||||||
var view = new EpisodeFileEditorLayout({
|
//var view = new TrackFileEditorLayout({
|
||||||
model : this.model,
|
// model : this.model,
|
||||||
series : this.series,
|
// artist : this.artist,
|
||||||
episodeCollection : this.episodeCollection
|
// trackCollection : this.trackCollection
|
||||||
});
|
//});
|
||||||
|
|
||||||
vent.trigger(vent.Commands.OpenModalCommand, view);
|
//vent.trigger(vent.Commands.OpenModalCommand, view);
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -1,50 +1,68 @@
|
||||||
<div class="artist-album" id="season-{{seasonNumber}}">
|
<div class="artist-album" id="album-{{title}}">
|
||||||
<h2>
|
<div class="visible-lg col-lg-2 poster">
|
||||||
|
{{cover}}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 col-lg-10">
|
||||||
|
<h2 class="header-text">
|
||||||
<i class="x-album-monitored album-monitored clickable" title="Toggle album monitored status"/>
|
<i class="x-album-monitored album-monitored clickable" title="Toggle album monitored status"/>
|
||||||
|
|
||||||
{{#if seasonNumber}}
|
{{#if title}}
|
||||||
Season {{seasonNumber}}
|
{{title}} ({{albumYear}})
|
||||||
|
|
||||||
{{else}}
|
{{else}}
|
||||||
Specials
|
Specials
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
||||||
{{#if_eq episodeCount compare=0}}
|
{{#if_eq trackCount compare=0}}
|
||||||
{{#if monitored}}
|
{{#if monitored}}
|
||||||
<span class="badge badge-primary album-status" title="No aired episodes"> </span>
|
<span class="badge badge-primary album-status" title="No aired tracks"> </span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="badge badge-warning album-status" title="Album is not monitored"> </span>
|
<span class="badge badge-warning album-status" title="Album is not monitored"> </span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if_eq percentOfEpisodes compare=100}}
|
{{#if_eq percentOfTracks compare=100}}
|
||||||
<span class="badge badge-success album-status" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded">{{episodeFileCount}} / {{episodeCount}}</span>
|
<span class="badge badge-success album-status" title="{{trackFileCount}}/{{trackCount}} tracks downloaded">{{trackFileCount}} / {{trackCount}} Tracks</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="badge badge-danger album-status" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded">{{episodeFileCount}} / {{episodeCount}}</span>
|
<span class="badge badge-danger album-status" title="{{trackFileCount}}/{{trackCount}} tracks downloaded">{{trackFileCount}} / {{trackCount}} Tracks</span>
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
{{/if_eq}}
|
{{/if_eq}}
|
||||||
|
|
||||||
<span class="album-actions pull-right">
|
<span class="album-actions pull-right">
|
||||||
<div class="x-album-episode-file-editor">
|
<div class="x-track-file-editor">
|
||||||
<i class="icon-lidarr-episode-file" title="Modify episode files for album"/>
|
<i class="icon-lidarr-track-file" title="Modify track files for album"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="x-album-rename">
|
<div class="x-album-rename">
|
||||||
<i class="icon-lidarr-rename" title="Preview rename for album {{seasonNumber}}"/>
|
<i class="icon-lidarr-rename" title="Preview rename for album {{albumId}}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="x-album-search">
|
<div class="x-album-search">
|
||||||
<i class="icon-lidarr-search" title="Search for monitored episodes in album {{seasonNumber}}"/>
|
<i class="icon-lidarr-search" title="Search for monitored tracks in album {{albumId}}"/>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</h2>
|
</h2>
|
||||||
<div class="show-hide-episodes x-show-hide-episodes">
|
|
||||||
|
<div class="album-detail-release">
|
||||||
|
Release Date: {{albumReleaseDate}}
|
||||||
|
</div>
|
||||||
|
<div class="album-detail-type">
|
||||||
|
Type: Album
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="album-info" class="album-info"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="show-hide-tracks x-show-hide-tracks">
|
||||||
<h4>
|
<h4>
|
||||||
{{#if showingEpisodes}}
|
{{#if showingTracks}}
|
||||||
<i class="icon-lidarr-panel-hide"/>
|
<i class="icon-lidarr-panel-hide"/>
|
||||||
Hide Episodes
|
Hide Tracks
|
||||||
{{else}}
|
{{else}}
|
||||||
<i class="icon-lidarr-panel-show"/>
|
<i class="icon-lidarr-panel-show"/>
|
||||||
Show Episodes
|
Show Tracks
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="x-episode-grid table-responsive"></div>
|
|
||||||
|
<div class="x-track-grid table-responsive"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -36,7 +36,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
events : {
|
events : {
|
||||||
'click .x-episode-file-editor' : '_openEpisodeFileEditor',
|
'click .x-track-file-editor' : '_openTrackFileEditor',
|
||||||
'click .x-monitored' : '_toggleMonitored',
|
'click .x-monitored' : '_toggleMonitored',
|
||||||
'click .x-edit' : '_editArtist',
|
'click .x-edit' : '_editArtist',
|
||||||
'click .x-refresh' : '_refreshArtist',
|
'click .x-refresh' : '_refreshArtist',
|
||||||
|
@ -142,7 +142,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
_refreshArtist : function() {
|
_refreshArtist : function() {
|
||||||
CommandController.Execute('refreshArtist', {
|
CommandController.Execute('refreshArtist', {
|
||||||
name : 'refreshArtist',
|
name : 'refreshArtist',
|
||||||
seriesId : this.model.id
|
artistId : this.model.id
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -167,30 +167,19 @@ module.exports = Marionette.Layout.extend({
|
||||||
|
|
||||||
this.albums.show(new LoadingView());
|
this.albums.show(new LoadingView());
|
||||||
|
|
||||||
this.albumCollection = new AlbumCollection(this.model.get('albums'));
|
this.albumCollection = new AlbumCollection({ artistId : this.model.id }).bindSignalR();
|
||||||
|
|
||||||
this.trackCollection = new TrackCollection({ artistId : this.model.id }).bindSignalR();
|
this.trackCollection = new TrackCollection({ artistId : this.model.id }).bindSignalR();
|
||||||
this.trackFileCollection = new TrackFileCollection({ artistId : this.model.id }).bindSignalR();
|
this.trackFileCollection = new TrackFileCollection({ artistId : this.model.id }).bindSignalR();
|
||||||
|
|
||||||
|
console.log (this.trackCollection);
|
||||||
|
|
||||||
reqres.setHandler(reqres.Requests.GetEpisodeFileById, function(trackFileId) {
|
reqres.setHandler(reqres.Requests.GetEpisodeFileById, function(trackFileId) {
|
||||||
return self.trackFileCollection.get(trackFileId);
|
return self.trackFileCollection.get(trackFileId);
|
||||||
});
|
});
|
||||||
|
|
||||||
reqres.setHandler(reqres.Requests.GetAlternateNameBySeasonNumber, function(artistId, seasonNumber, sceneSeasonNumber) {
|
|
||||||
if (self.model.get('id') !== artistId) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sceneSeasonNumber === undefined) {
|
$.when(this.albumCollection.fetch(), this.trackCollection.fetch(), this.trackFileCollection.fetch()).done(function() {
|
||||||
sceneSeasonNumber = seasonNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _.where(self.model.get('alternateTitles'),
|
|
||||||
function(alt) {
|
|
||||||
return alt.sceneSeasonNumber === sceneSeasonNumber || alt.seasonNumber === seasonNumber;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$.when(this.trackCollection.fetch(), this.trackFileCollection.fetch()).done(function() {
|
|
||||||
var albumCollectionView = new AlbumCollectionView({
|
var albumCollectionView = new AlbumCollectionView({
|
||||||
collection : self.albumCollection,
|
collection : self.albumCollection,
|
||||||
trackCollection : self.trackCollection,
|
trackCollection : self.trackCollection,
|
||||||
|
@ -219,7 +208,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
_refresh : function() {
|
_refresh : function() {
|
||||||
this.albumCollection.add(this.model.get('albums'), { merge : true });
|
this.albumCollection.fetch();
|
||||||
this.trackCollection.fetch();
|
this.trackCollection.fetch();
|
||||||
this.trackFileCollection.fetch();
|
this.trackFileCollection.fetch();
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
{{name}}
|
{{name}}
|
||||||
<div class="artist-actions pull-right">
|
<div class="artist-actions pull-right">
|
||||||
<div class="x-episode-file-editor">
|
<div class="x-episode-file-editor">
|
||||||
<i class="icon-lidarr-episode-file" title="Modify episode files for artist"/>
|
<i class="icon-lidarr-track-file" title="Modify episode files for artist"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="x-refresh">
|
<div class="x-refresh">
|
||||||
<i class="icon-lidarr-refresh icon-can-spin" title="Update artist info and scan disk"/>
|
<i class="icon-lidarr-refresh icon-can-spin" title="Update artist info and scan disk"/>
|
||||||
|
@ -32,4 +32,4 @@
|
||||||
<div id="info" class="artist-info"></div>
|
<div id="info" class="artist-info"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="seasons"></div>
|
<div id="albums"></div>
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<a href="{{discogsUrl}}" class="label label-info">Discogs</a>
|
<a href="{{discogsUrl}}" class="label label-info">Discogs</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if amId}}
|
{{#if allMusicId}}
|
||||||
<a href="{{allMusicUrl}}" class="label label-info">AllMusic</a>
|
<a href="{{allMusicUrl}}" class="label label-info">AllMusic</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -4,18 +4,14 @@ var reqres = require('../../reqres');
|
||||||
var ArtistCollection = require('../ArtistCollection');
|
var ArtistCollection = require('../ArtistCollection');
|
||||||
|
|
||||||
module.exports = NzbDroneCell.extend({
|
module.exports = NzbDroneCell.extend({
|
||||||
className : 'episode-number-cell',
|
className : 'track-number-cell',
|
||||||
template : 'Artist/Details/TrackNumberCellTemplate',
|
template : 'Artist/Details/TrackNumberCellTemplate',
|
||||||
|
|
||||||
render : function() {
|
render : function() {
|
||||||
this.$el.empty();
|
this.$el.empty();
|
||||||
this.$el.html(this.model.get('trackNumber'));
|
this.$el.html(this.model.get('trackNumber'));
|
||||||
|
|
||||||
var series = ArtistCollection.get(this.model.get('seriesId'));
|
var artist = ArtistCollection.get(this.model.get('artistId'));
|
||||||
|
|
||||||
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 = [];
|
var alternateTitles = [];
|
||||||
|
|
||||||
|
|
19
src/UI/Artist/Details/TrackRatingCell.js
Normal file
19
src/UI/Artist/Details/TrackRatingCell.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
var NzbDroneCell = require('../../Cells/NzbDroneCell');
|
||||||
|
var ArtistCollection = require('../ArtistCollection');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'track-rating-cell',
|
||||||
|
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
var ratings = this.model.get('ratings')
|
||||||
|
|
||||||
|
if (ratings) {
|
||||||
|
this.$el.html(ratings.value + ' (' + ratings.votes + ' votes)');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.delegateEvents();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
|
@ -9,7 +9,7 @@ var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||||
var ArtistTitleCell = require('../../Cells/ArtistTitleCell');
|
var ArtistTitleCell = require('../../Cells/ArtistTitleCell');
|
||||||
var TemplatedCell = require('../../Cells/TemplatedCell');
|
var TemplatedCell = require('../../Cells/TemplatedCell');
|
||||||
var ProfileCell = require('../../Cells/ProfileCell');
|
var ProfileCell = require('../../Cells/ProfileCell');
|
||||||
var EpisodeProgressCell = require('../../Cells/EpisodeProgressCell');
|
var TrackProgressCell = require('../../Cells/TrackProgressCell');
|
||||||
var ArtistActionsCell = require('../../Cells/ArtistActionsCell');
|
var ArtistActionsCell = require('../../Cells/ArtistActionsCell');
|
||||||
var ArtistStatusCell = require('../../Cells/ArtistStatusCell');
|
var ArtistStatusCell = require('../../Cells/ArtistStatusCell');
|
||||||
var FooterView = require('./FooterView');
|
var FooterView = require('./FooterView');
|
||||||
|
@ -62,10 +62,10 @@ module.exports = Marionette.Layout.extend({
|
||||||
cell : RelativeDateCell
|
cell : RelativeDateCell
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'percentOfEpisodes',
|
name : 'percentOfTracks',
|
||||||
label : 'Tracks',
|
label : 'Tracks',
|
||||||
cell : EpisodeProgressCell,
|
cell : TrackProgressCell,
|
||||||
className : 'episode-progress-cell'
|
className : 'track-progress-cell'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name : 'this',
|
name : 'this',
|
||||||
|
@ -157,7 +157,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title : 'Tracks',
|
title : 'Tracks',
|
||||||
name : 'percentOfEpisodes'
|
name : 'percentOfTracks'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -330,8 +330,8 @@ module.exports = Marionette.Layout.extend({
|
||||||
|
|
||||||
_.each(this.artistCollection.models, function(model) {
|
_.each(this.artistCollection.models, function(model) {
|
||||||
albums += model.get('albumCount');
|
albums += model.get('albumCount');
|
||||||
tracks += model.get('episodeCount'); // TODO: Refactor to Seasons and Tracks
|
tracks += model.get('trackCount'); // TODO: Refactor to Seasons and Tracks
|
||||||
trackFiles += model.get('episodeFileCount');
|
trackFiles += model.get('trackFileCount');
|
||||||
|
|
||||||
/*if (model.get('status').toLowerCase() === 'ended') {
|
/*if (model.get('status').toLowerCase() === 'ended') {
|
||||||
ended++;
|
ended++;
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
{{profile profileId}}
|
{{profile profileId}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2 col-xs-4">
|
<div class="col-md-2 col-xs-4">
|
||||||
{{> EpisodeProgressPartial }}
|
{{> TrackProgressPartial }}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8 col-xs-10">
|
<div class="col-md-8 col-xs-10">
|
||||||
Path {{path}}
|
Path {{path}}
|
||||||
|
|
|
@ -21,7 +21,7 @@ module.exports = PageableCollection.extend({
|
||||||
this.artistId = options.artistId;
|
this.artistId = options.artistId;
|
||||||
},
|
},
|
||||||
|
|
||||||
bySeason : function(album) {
|
byAlbum : function(album) {
|
||||||
var filtered = this.filter(function(track) {
|
var filtered = this.filter(function(track) {
|
||||||
return track.get('albumId') === album;
|
return track.get('albumId') === album;
|
||||||
});
|
});
|
||||||
|
@ -36,11 +36,11 @@ module.exports = PageableCollection.extend({
|
||||||
var track2 = model2.get('trackNumber');
|
var track2 = model2.get('trackNumber');
|
||||||
|
|
||||||
if (track1 < track2) {
|
if (track1 < track2) {
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (track1 > track2) {
|
if (track1 > track2) {
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.album-cover {
|
||||||
|
min-width: 56px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.truncate {
|
.truncate {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -22,6 +27,11 @@
|
||||||
width : 168px;
|
width : 168px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.album-cover {
|
||||||
|
padding-left : 20px;
|
||||||
|
width : 168px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-horizontal {
|
.form-horizontal {
|
||||||
margin-top : 10px;
|
margin-top : 10px;
|
||||||
}
|
}
|
||||||
|
@ -81,9 +91,9 @@
|
||||||
.card;
|
.card;
|
||||||
.opacity(0.9);
|
.opacity(0.9);
|
||||||
margin : 30px 10px;
|
margin : 30px 10px;
|
||||||
padding : 10px 25px;
|
padding : 30px 25px;
|
||||||
|
|
||||||
.show-hide-episodes {
|
.show-hide-tracks {
|
||||||
.clickable();
|
.clickable();
|
||||||
text-align : center;
|
text-align : center;
|
||||||
|
|
||||||
|
@ -91,6 +101,9 @@
|
||||||
.clickable();
|
.clickable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.header-text {
|
||||||
|
margin-top : 0px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.artist-posters {
|
.artist-posters {
|
||||||
|
@ -261,39 +274,45 @@
|
||||||
|
|
||||||
.artist-album {
|
.artist-album {
|
||||||
|
|
||||||
.episode-number-cell {
|
.track-number-cell {
|
||||||
width : 40px;
|
width : 40px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.episode-air-date-cell {
|
.track-air-date-cell {
|
||||||
width : 150px;
|
width : 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-status-cell {
|
.track-status-cell {
|
||||||
|
width : 100px;
|
||||||
|
}
|
||||||
|
.track-rating-cell {
|
||||||
|
width : 150px;
|
||||||
|
}
|
||||||
|
.track-explicit-cell {
|
||||||
width : 100px;
|
width : 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-title-cell {
|
.track-title-cell {
|
||||||
cursor : pointer;
|
cursor : pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-detail-modal {
|
.track-detail-modal {
|
||||||
|
|
||||||
.episode-info {
|
.track-info {
|
||||||
margin-bottom : 10px;
|
margin-bottom : 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-overview {
|
.track-overview {
|
||||||
font-style : italic;
|
font-style : italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-file-info {
|
.track-file-info {
|
||||||
margin-top : 30px;
|
margin-top : 30px;
|
||||||
font-size : 12px;
|
font-size : 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-history-details-cell .popover {
|
.track-history-details-cell .popover {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +321,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-grid {
|
.track-grid {
|
||||||
.toggle-cell {
|
.toggle-cell {
|
||||||
width : 28px;
|
width : 28px;
|
||||||
text-align : center;
|
text-align : center;
|
||||||
|
@ -422,7 +441,7 @@
|
||||||
//artist Details
|
//artist Details
|
||||||
|
|
||||||
.artist-not-monitored {
|
.artist-not-monitored {
|
||||||
.album-monitored, .episode-monitored {
|
.album-monitored, .track-monitored {
|
||||||
color: #888888;
|
color: #888888;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
@ -454,6 +473,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.album-info {
|
||||||
|
.row {
|
||||||
|
margin-bottom : 3px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display : inline-block;
|
||||||
|
margin-bottom : 2px;
|
||||||
|
padding : 4px 6px 3px 6px;
|
||||||
|
max-width : 100%;
|
||||||
|
white-space : normal;
|
||||||
|
word-wrap : break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-info-links {
|
||||||
|
@media (max-width: @screen-sm-max) {
|
||||||
|
display : inline-block;
|
||||||
|
margin-top : 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.scene-info {
|
.scene-info {
|
||||||
.key, .value {
|
.key, .value {
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{{> EpisodeProgressPartial }}
|
|
44
src/UI/Cells/TrackActionsCell.js
Normal file
44
src/UI/Cells/TrackActionsCell.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
var vent = require('vent');
|
||||||
|
var NzbDroneCell = require('./NzbDroneCell');
|
||||||
|
var CommandController = require('../Commands/CommandController');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'track-actions-cell',
|
||||||
|
|
||||||
|
events : {
|
||||||
|
'click .x-automatic-search' : '_automaticSearch',
|
||||||
|
'click .x-manual-search' : '_manualSearch'
|
||||||
|
},
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.$el.empty();
|
||||||
|
|
||||||
|
this.$el.html('<i class="icon-lidarr-search x-automatic-search" title="Automatic Search"></i>' + '<i class="icon-lidarr-search-manual x-manual-search" title="Manual Search"></i>');
|
||||||
|
|
||||||
|
CommandController.bindToCommand({
|
||||||
|
element : this.$el.find('.x-automatic-search'),
|
||||||
|
command : {
|
||||||
|
name : 'trackSearch',
|
||||||
|
trackIds : [this.model.get('id')]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.delegateEvents();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_automaticSearch : function() {
|
||||||
|
CommandController.Execute('trackSearch', {
|
||||||
|
name : 'trackSearch',
|
||||||
|
trackIds : [this.model.get('id')]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_manualSearch : function() {
|
||||||
|
vent.trigger(vent.Commands.ShowTrackDetails, {
|
||||||
|
track : this.cellValue,
|
||||||
|
hideSeriesLink : true,
|
||||||
|
openingTab : 'search'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
20
src/UI/Cells/TrackExplicitCell.js
Normal file
20
src/UI/Cells/TrackExplicitCell.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
var vent = require('vent');
|
||||||
|
var NzbDroneCell = require('./NzbDroneCell');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'track-explicit-cell',
|
||||||
|
template : 'Cells/TrackExplicitCellTemplate',
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
var explicit = this.cellValue.get('explicit');
|
||||||
|
var print = '';
|
||||||
|
|
||||||
|
if (explicit === true) {
|
||||||
|
print = 'Explicit';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$el.html(print);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
57
src/UI/Cells/TrackMonitoredCell.js
Normal file
57
src/UI/Cells/TrackMonitoredCell.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
var _ = require('underscore');
|
||||||
|
var ToggleCell = require('./ToggleCell');
|
||||||
|
var ArtistCollection = require('../Artist/ArtistCollection');
|
||||||
|
var Messenger = require('../Shared/Messenger');
|
||||||
|
|
||||||
|
module.exports = ToggleCell.extend({
|
||||||
|
className : 'toggle-cell track-monitored',
|
||||||
|
|
||||||
|
_originalOnClick : ToggleCell.prototype._onClick,
|
||||||
|
|
||||||
|
_onClick : function(e) {
|
||||||
|
|
||||||
|
var artist = ArtistCollection.get(this.model.get('artistId'));
|
||||||
|
|
||||||
|
if (!artist.get('monitored')) {
|
||||||
|
|
||||||
|
Messenger.show({
|
||||||
|
message : 'Unable to change monitored state when series is not monitored',
|
||||||
|
type : 'error'
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.shiftKey && this.model.trackCollection.lastToggled) {
|
||||||
|
this._selectRange();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._originalOnClick.apply(this, arguments);
|
||||||
|
this.model.trackCollection.lastToggled = this.model;
|
||||||
|
},
|
||||||
|
|
||||||
|
_selectRange : function() {
|
||||||
|
var trackCollection = this.model.trackCollection;
|
||||||
|
var lastToggled = trackCollection.lastToggled;
|
||||||
|
|
||||||
|
var currentIndex = trackCollection.indexOf(this.model);
|
||||||
|
var lastIndex = trackCollection.indexOf(lastToggled);
|
||||||
|
|
||||||
|
var low = Math.min(currentIndex, lastIndex);
|
||||||
|
var high = Math.max(currentIndex, lastIndex);
|
||||||
|
var range = _.range(low + 1, high);
|
||||||
|
|
||||||
|
_.each(range, function(index) {
|
||||||
|
var model = trackCollection.at(index);
|
||||||
|
|
||||||
|
model.set('monitored', lastToggled.get('monitored'));
|
||||||
|
model.save();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.model.set('monitored', lastToggled.get('monitored'));
|
||||||
|
this.model.save();
|
||||||
|
this.model.trackCollection.lastToggled = undefined;
|
||||||
|
}
|
||||||
|
});
|
|
@ -2,21 +2,21 @@ var Marionette = require('marionette');
|
||||||
var NzbDroneCell = require('./NzbDroneCell');
|
var NzbDroneCell = require('./NzbDroneCell');
|
||||||
|
|
||||||
module.exports = NzbDroneCell.extend({
|
module.exports = NzbDroneCell.extend({
|
||||||
className : 'episode-progress-cell',
|
className : 'track-progress-cell',
|
||||||
template : 'Cells/EpisodeProgressCellTemplate',
|
template : 'Cells/TrackProgressCellTemplate',
|
||||||
|
|
||||||
render : function() {
|
render : function() {
|
||||||
|
|
||||||
var episodeCount = this.model.get('episodeCount');
|
var trackCount = this.model.get('trackCount');
|
||||||
var episodeFileCount = this.model.get('episodeFileCount');
|
var trackFileCount = this.model.get('trackFileCount');
|
||||||
|
|
||||||
var percent = 100;
|
var percent = 100;
|
||||||
|
|
||||||
if (episodeCount > 0) {
|
if (trackCount > 0) {
|
||||||
percent = episodeFileCount / episodeCount * 100;
|
percent = trackFileCount / trackCount * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.model.set('percentOfEpisodes', percent);
|
this.model.set('percentOfTracks', percent);
|
||||||
|
|
||||||
this.templateFunction = Marionette.TemplateCache.get(this.template);
|
this.templateFunction = Marionette.TemplateCache.get(this.template);
|
||||||
var data = this.model.toJSON();
|
var data = this.model.toJSON();
|
1
src/UI/Cells/TrackProgressCellTemplate.hbs
Normal file
1
src/UI/Cells/TrackProgressCellTemplate.hbs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{> TrackProgressPartial }}
|
127
src/UI/Cells/TrackStatusCell.js
Normal file
127
src/UI/Cells/TrackStatusCell.js
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
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 : 'track-status-cell',
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
this.listenTo(QueueCollection, 'sync', this._renderCell);
|
||||||
|
|
||||||
|
this._renderCell();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderCell : function() {
|
||||||
|
|
||||||
|
if (this.trackFile) {
|
||||||
|
this.stopListening(this.trackFile, 'change', this._refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$el.empty();
|
||||||
|
|
||||||
|
if (this.model) {
|
||||||
|
|
||||||
|
var icon;
|
||||||
|
var tooltip;
|
||||||
|
|
||||||
|
var hasAired = moment(this.model.get('airDateUtc')).isBefore(moment());
|
||||||
|
this.trackFile = this._getFile();
|
||||||
|
|
||||||
|
if (this.trackFile) {
|
||||||
|
this.listenTo(this.trackFile, 'change', this._refresh);
|
||||||
|
|
||||||
|
var quality = this.trackFile.get('quality');
|
||||||
|
var revision = quality.revision;
|
||||||
|
var size = FormatHelpers.bytes(this.trackFile.get('size'));
|
||||||
|
var title = 'Track 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.trackFile.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 = false; //TODO Fix this by adding to QueueCollection
|
||||||
|
|
||||||
|
if (downloading) {
|
||||||
|
var progress = 100 - (downloading.get('sizeleft') / downloading.get('size') * 100);
|
||||||
|
|
||||||
|
if (progress === 0) {
|
||||||
|
icon = 'icon-lidarr-downloading';
|
||||||
|
tooltip = 'Track is downloading';
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this.$el.html('<div class="progress" title="Track 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-lidarr-downloading';
|
||||||
|
tooltip = 'Track is downloading';
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!this.model.get('airDateUtc')) {
|
||||||
|
icon = 'icon-lidarr-tba';
|
||||||
|
tooltip = 'TBA';
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (hasAired) {
|
||||||
|
icon = 'icon-lidarr-missing';
|
||||||
|
tooltip = 'Track missing from disk';
|
||||||
|
} else {
|
||||||
|
icon = 'icon-lidarr-not-aired';
|
||||||
|
tooltip = 'Track 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 trackFile;
|
||||||
|
|
||||||
|
if (reqres.hasHandler(reqres.Requests.GetTrackFileById)) {
|
||||||
|
trackFile = reqres.request(reqres.Requests.GetTrackFileById, this.model.get('trackFileId'));
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (this.model.has('trackFile')) {
|
||||||
|
trackFile = new Backbone.Model(this.model.get('trackFile'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackFile) {
|
||||||
|
return trackFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
29
src/UI/Cells/TrackTitleCell.js
Normal file
29
src/UI/Cells/TrackTitleCell.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
var vent = require('vent');
|
||||||
|
var NzbDroneCell = require('./NzbDroneCell');
|
||||||
|
|
||||||
|
module.exports = NzbDroneCell.extend({
|
||||||
|
className : 'track-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 hideArtistLink = this.column.get('hideArtistLink');
|
||||||
|
vent.trigger(vent.Commands.ShowTrackDetails, {
|
||||||
|
track : this.cellValue,
|
||||||
|
hideArtistLink : hideArtistLink
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -15,7 +15,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-title-cell {
|
.track-title-cell {
|
||||||
.text-overflow();
|
.text-overflow();
|
||||||
|
|
||||||
color: #428bca;
|
color: #428bca;
|
||||||
|
@ -89,7 +89,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td.episode-status-cell, td.quality-cell, td.history-quality-cell, td.progress-cell {
|
td.track-status-cell, td.quality-cell, td.history-quality-cell, td.progress-cell {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ td.episode-status-cell, td.quality-cell, td.history-quality-cell, td.progress-ce
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-actions-cell {
|
.track-actions-cell {
|
||||||
width: 55px;
|
width: 55px;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
|
@ -131,12 +131,12 @@ td.episode-status-cell, td.quality-cell, td.history-quality-cell, td.progress-ce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-history-details-cell {
|
.track-history-details-cell {
|
||||||
width : 18px;
|
width : 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-detail-modal {
|
.track-detail-modal {
|
||||||
.episode-actions-cell {
|
.track-actions-cell {
|
||||||
width : 18px;
|
width : 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ td.episode-status-cell, td.quality-cell, td.history-quality-cell, td.progress-ce
|
||||||
width : 80px;
|
width : 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.delete-episode-file-cell {
|
td.delete-track-file-cell {
|
||||||
.clickable();
|
.clickable();
|
||||||
|
|
||||||
text-align : center;
|
text-align : center;
|
||||||
|
@ -188,7 +188,7 @@ td.delete-episode-file-cell {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episode-number-cell {
|
.track-number-cell {
|
||||||
cursor : default;
|
cursor : default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ td.delete-episode-file-cell {
|
||||||
|
|
||||||
.table>tbody>tr>td, .table>thead>tr>th {
|
.table>tbody>tr>td, .table>thead>tr>th {
|
||||||
|
|
||||||
&.episode-warning-cell {
|
&.track-warning-cell {
|
||||||
width : 1px;
|
width : 1px;
|
||||||
padding-left : 0px;
|
padding-left : 0px;
|
||||||
padding-right : 0px;
|
padding-right : 0px;
|
||||||
|
|
|
@ -496,7 +496,7 @@
|
||||||
.fa-icon-content(@fa-var-retweet);
|
.fa-icon-content(@fa-var-retweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-lidarr-episode-file {
|
.icon-lidarr-track-file {
|
||||||
.fa-icon-content(@fa-var-file-video-o);
|
.fa-icon-content(@fa-var-file-video-o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
@import "Bootstrap/variables";
|
@import "Bootstrap/variables";
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
.progress.episode-progress {
|
.progress.track-progress {
|
||||||
position : relative;
|
position : relative;
|
||||||
margin-bottom : 2px;
|
margin-bottom : 2px;
|
||||||
|
|
||||||
|
|
46
src/UI/Handlebars/Helpers/Album.js
Normal file
46
src/UI/Handlebars/Helpers/Album.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
var Handlebars = require('handlebars');
|
||||||
|
var StatusModel = require('../../System/StatusModel');
|
||||||
|
var moment = require('moment');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
Handlebars.registerHelper('cover', function() {
|
||||||
|
|
||||||
|
var placeholder = StatusModel.get('urlBase') + '/Content/Images/poster-dark.png';
|
||||||
|
var cover = _.where(this.images, { coverType : 'cover' });
|
||||||
|
|
||||||
|
if (cover[0]) {
|
||||||
|
if (!cover[0].url.match(/^https?:\/\//)) {
|
||||||
|
return new Handlebars.SafeString('<img class="album-cover x-album-cover" {0}>'.format(Handlebars.helpers.defaultImg.call(null, cover[0].url, 250)));
|
||||||
|
} else {
|
||||||
|
var url = cover[0].url.replace(/^https?\:/, '');
|
||||||
|
return new Handlebars.SafeString('<img class="album-cover x-album-cover" {0}>'.format(Handlebars.helpers.defaultImg.call(null, url)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Handlebars.SafeString('<img class="album-cover placeholder-image" src="{0}">'.format(placeholder));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Handlebars.registerHelper('MBAlbumUrl', function() {
|
||||||
|
return 'https://musicbrainz.org/release-group/' + this.mbId;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('TADBAlbumUrl', function() {
|
||||||
|
return 'http://www.theaudiodb.com/album/' + this.tadbId;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('discogsAlbumUrl', function() {
|
||||||
|
return 'https://www.discogs.com/master/' + this.discogsId;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('allMusicAlbumUrl', function() {
|
||||||
|
return 'http://www.allmusic.com/album/' + this.allMusicId;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('albumYear', function() {
|
||||||
|
return new Handlebars.SafeString('<span class="year">{0}</span>'.format(moment(this.releaseDate).format('YYYY')));
|
||||||
|
});
|
||||||
|
Handlebars.registerHelper('albumReleaseDate', function() {
|
||||||
|
return new Handlebars.SafeString('<span class="release">{0}</span>'.format(moment(this.releaseDate).format('L')));
|
||||||
|
});
|
|
@ -9,16 +9,18 @@ Handlebars.registerHelper('poster', function() {
|
||||||
|
|
||||||
if (poster[0]) {
|
if (poster[0]) {
|
||||||
if (!poster[0].url.match(/^https?:\/\//)) {
|
if (!poster[0].url.match(/^https?:\/\//)) {
|
||||||
return new Handlebars.SafeString('<img class="series-poster x-series-poster" {0}>'.format(Handlebars.helpers.defaultImg.call(null, poster[0].url, 250)));
|
return new Handlebars.SafeString('<img class="artist-poster x-artist-poster" {0}>'.format(Handlebars.helpers.defaultImg.call(null, poster[0].url, 250)));
|
||||||
} else {
|
} else {
|
||||||
var url = poster[0].url.replace(/^https?\:/, '');
|
var url = poster[0].url.replace(/^https?\:/, '');
|
||||||
return new Handlebars.SafeString('<img class="series-poster x-series-poster" {0}>'.format(Handlebars.helpers.defaultImg.call(null, url)));
|
return new Handlebars.SafeString('<img class="artist-poster x-artist-poster" {0}>'.format(Handlebars.helpers.defaultImg.call(null, url)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Handlebars.SafeString('<img class="series-poster placeholder-image" src="{0}">'.format(placeholder));
|
return new Handlebars.SafeString('<img class="artist-poster placeholder-image" src="{0}">'.format(placeholder));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Handlebars.registerHelper('MBUrl', function() {
|
Handlebars.registerHelper('MBUrl', function() {
|
||||||
return 'https://musicbrainz.org/artist/' + this.mbId;
|
return 'https://musicbrainz.org/artist/' + this.mbId;
|
||||||
});
|
});
|
||||||
|
@ -32,25 +34,25 @@ Handlebars.registerHelper('discogsUrl', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('allMusicUrl', function() {
|
Handlebars.registerHelper('allMusicUrl', function() {
|
||||||
return 'http://www.allmusic.com/artist/' + this.amId;
|
return 'http://www.allmusic.com/artist/' + this.allMusicId;
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('route', function() {
|
Handlebars.registerHelper('route', function() {
|
||||||
return StatusModel.get('urlBase') + '/artist/' + this.nameSlug;
|
return StatusModel.get('urlBase') + '/artist/' + this.nameSlug;
|
||||||
});
|
});
|
||||||
|
|
||||||
Handlebars.registerHelper('percentOfEpisodes', function() {
|
// Handlebars.registerHelper('percentOfEpisodes', function() {
|
||||||
var episodeCount = this.episodeCount;
|
// var episodeCount = this.episodeCount;
|
||||||
var episodeFileCount = this.episodeFileCount;
|
// var episodeFileCount = this.episodeFileCount;
|
||||||
|
|
||||||
var percent = 100;
|
// var percent = 100;
|
||||||
|
|
||||||
if (episodeCount > 0) {
|
// if (episodeCount > 0) {
|
||||||
percent = episodeFileCount / episodeCount * 100;
|
// percent = episodeFileCount / episodeCount * 100;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return percent;
|
// return percent;
|
||||||
});
|
// });
|
||||||
|
|
||||||
Handlebars.registerHelper('percentOfTracks', function() {
|
Handlebars.registerHelper('percentOfTracks', function() {
|
||||||
var trackCount = this.trackCount;
|
var trackCount = this.trackCount;
|
||||||
|
|
|
@ -6,6 +6,7 @@ require('./Helpers/Numbers');
|
||||||
require('./Helpers/Episode');
|
require('./Helpers/Episode');
|
||||||
//require('./Helpers/Series');
|
//require('./Helpers/Series');
|
||||||
require('./Helpers/Artist');
|
require('./Helpers/Artist');
|
||||||
|
require('./Helpers/Album');
|
||||||
require('./Helpers/Quality');
|
require('./Helpers/Quality');
|
||||||
require('./Helpers/System');
|
require('./Helpers/System');
|
||||||
require('./Helpers/EachReverse');
|
require('./Helpers/EachReverse');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue