Merge conflicts

pull/7/head
Joseph Milazzo 7 years ago
commit ee90d8021a

@ -20,6 +20,10 @@ namespace NzbDrone.Api.Music
//View Only
public string Name { get; set; }
public string ForeignArtistId { get; set; }
public string MBId { get; set; }
public int TADBId { get; set; }
public int DiscogsId { get; set; }
public string AMId { get; set; }
public string Overview { get; set; }
public int AlbumCount
@ -53,7 +57,7 @@ namespace NzbDrone.Api.Music
public bool Monitored { get; set; }
public string RootFolderPath { get; set; }
public string Certification { get; set; }
//public string Certification { get; set; }
public List<string> Genres { get; set; }
public HashSet<int> Tags { get; set; }
public DateTime Added { get; set; }
@ -71,7 +75,10 @@ namespace NzbDrone.Api.Music
return new ArtistResource
{
Id = model.Id,
MBId = model.MBId,
TADBId = model.TADBId,
DiscogsId = model.DiscogsId,
AMId = model.AMId,
Name = model.Name,
//AlternateTitles
//SortTitle = resource.SortTitle,
@ -127,7 +134,10 @@ namespace NzbDrone.Api.Music
Name = resource.Name,
//AlternateTitles
//SortTitle = resource.SortTitle,
MBId = resource.MBId,
TADBId = resource.TADBId,
DiscogsId = resource.DiscogsId,
AMId = resource.AMId,
//TotalEpisodeCount
//EpisodeCount
//EpisodeFileCount

@ -104,6 +104,13 @@
<Compile Include="ClientSchema\SelectOption.cs" />
<Compile Include="Commands\CommandModule.cs" />
<Compile Include="Commands\CommandResource.cs" />
<Compile Include="TrackFiles\TrackFileModule.cs" />
<Compile Include="TrackFiles\TrackFileResource.cs" />
<Compile Include="Tracks\TrackModule.cs" />
<Compile Include="Tracks\TrackModuleWithSignalR.cs" />
<Compile Include="Tracks\TrackResource.cs" />
<Compile Include="Tracks\RenameTrackModule.cs" />
<Compile Include="Tracks\RenameTrackResource.cs" />
<Compile Include="Extensions\AccessControlHeaders.cs" />
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />

@ -0,0 +1,101 @@
using System.Collections.Generic;
using System.IO;
using NLog;
using NzbDrone.Api.REST;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Music;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.SignalR;
using System;
namespace NzbDrone.Api.TrackFiles
{
public class TrackFileModule : NzbDroneRestModuleWithSignalR<TrackFileResource, TrackFile>,
IHandle<TrackFileAddedEvent>
{
private readonly IMediaFileService _mediaFileService;
private readonly IDiskProvider _diskProvider;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly ISeriesService _seriesService;
private readonly IArtistService _artistService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
private readonly Logger _logger;
public TrackFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService,
IDiskProvider diskProvider,
IRecycleBinProvider recycleBinProvider,
ISeriesService seriesService,
IArtistService artistService,
IQualityUpgradableSpecification qualityUpgradableSpecification,
Logger logger)
: base(signalRBroadcaster)
{
_mediaFileService = mediaFileService;
_diskProvider = diskProvider;
_recycleBinProvider = recycleBinProvider;
_seriesService = seriesService;
_artistService = artistService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
_logger = logger;
GetResourceById = GetTrackFile;
GetResourceAll = GetTrackFiles;
UpdateResource = SetQuality;
DeleteResource = DeleteTrackFile;
}
private TrackFileResource GetTrackFile(int id)
{
throw new NotImplementedException();
//var episodeFile = _mediaFileService.Get(id);
//var series = _seriesService.GetSeries(episodeFile.SeriesId);
//return episodeFile.ToResource(series, _qualityUpgradableSpecification);
}
private List<TrackFileResource> GetTrackFiles()
{
if (!Request.Query.ArtistId.HasValue)
{
throw new BadRequestException("artistId is missing");
}
var artistId = (int)Request.Query.ArtistId;
var artist = _artistService.GetArtist(artistId);
return _mediaFileService.GetFilesByArtist(artistId).ConvertAll(f => f.ToResource(artist, _qualityUpgradableSpecification));
}
private void SetQuality(TrackFileResource trackFileResource)
{
var trackFile = _mediaFileService.Get(trackFileResource.Id);
trackFile.Quality = trackFileResource.Quality;
_mediaFileService.Update(trackFile);
}
private void DeleteTrackFile(int id)
{
throw new NotImplementedException();
//var episodeFile = _mediaFileService.Get(id);
//var series = _seriesService.GetSeries(episodeFile.SeriesId);
//var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
//var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath));
//_logger.Info("Deleting episode file: {0}", fullPath);
//_recycleBinProvider.DeleteFile(fullPath, subfolder);
//_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
}
public void Handle(TrackFileAddedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.TrackFile.Id);
}
}
}

@ -0,0 +1,64 @@
using System;
using System.IO;
using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Api.TrackFiles
{
public class TrackFileResource : RestResource
{
public int ArtistId { get; set; }
public int AlbumId { get; set; }
public string RelativePath { get; set; }
public string Path { get; set; }
public long Size { get; set; }
public DateTime DateAdded { get; set; }
//public string SceneName { get; set; }
public QualityModel Quality { get; set; }
public bool QualityCutoffNotMet { get; set; }
}
public static class TrackFileResourceMapper
{
private static TrackFileResource ToResource(this Core.MediaFiles.TrackFile model)
{
if (model == null) return null;
return new TrackFileResource
{
Id = model.Id,
ArtistId = model.ArtistId,
AlbumId = model.AlbumId,
RelativePath = model.RelativePath,
//Path
Size = model.Size,
DateAdded = model.DateAdded,
//SceneName = model.SceneName,
Quality = model.Quality,
//QualityCutoffNotMet
};
}
public static TrackFileResource ToResource(this Core.MediaFiles.TrackFile model, Core.Music.Artist artist, Core.DecisionEngine.IQualityUpgradableSpecification qualityUpgradableSpecification)
{
if (model == null) return null;
return new TrackFileResource
{
Id = model.Id,
ArtistId = model.ArtistId,
AlbumId = model.AlbumId,
RelativePath = model.RelativePath,
Path = Path.Combine(artist.Path, model.RelativePath),
Size = model.Size,
DateAdded = model.DateAdded,
//SceneName = model.SceneName,
Quality = model.Quality,
QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(artist.Profile.Value, model.Quality)
};
}
}
}

@ -0,0 +1,37 @@
using System.Collections.Generic;
using NzbDrone.Api.REST;
using NzbDrone.Core.MediaFiles;
namespace NzbDrone.Api.Tracks
{
public class RenameTrackModule : NzbDroneRestModule<RenameTrackResource>
{
private readonly IRenameTrackFileService _renameTrackFileService;
public RenameTrackModule(IRenameTrackFileService renameTrackFileService)
: base("rename")
{
_renameTrackFileService = renameTrackFileService;
GetResourceAll = GetTracks;
}
private List<RenameTrackResource> GetTracks()
{
if (!Request.Query.ArtistId.HasValue)
{
throw new BadRequestException("artistId is missing");
}
var artistId = (int)Request.Query.ArtistId;
if (Request.Query.AlbumId.HasValue)
{
var albumId = (int)Request.Query.AlbumId;
return _renameTrackFileService.GetRenamePreviews(artistId, albumId).ToResource();
}
return _renameTrackFileService.GetRenamePreviews(artistId).ToResource();
}
}
}

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.REST;
namespace NzbDrone.Api.Tracks
{
public class RenameTrackResource : RestResource
{
public int ArtistId { get; set; }
public int AlbumId { get; set; }
public List<int> TrackNumbers { get; set; }
public int TrackFileId { get; set; }
public string ExistingPath { get; set; }
public string NewPath { get; set; }
}
public static class RenameTrackResourceMapper
{
public static RenameTrackResource ToResource(this Core.MediaFiles.RenameTrackFilePreview model)
{
if (model == null) return null;
return new RenameTrackResource
{
ArtistId = model.ArtistId,
AlbumId = model.AlbumId,
TrackNumbers = model.TrackNumbers.ToList(),
TrackFileId = model.TrackFileId,
ExistingPath = model.ExistingPath,
NewPath = model.NewPath
};
}
public static List<RenameTrackResource> ToResource(this IEnumerable<Core.MediaFiles.RenameTrackFilePreview> models)
{
return models.Select(ToResource).ToList();
}
}
}

@ -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.Tracks
{
public class TrackModule : TrackModuleWithSignalR
{
public TrackModule(IArtistService artistService,
ITrackService trackService,
IQualityUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(trackService, artistService, qualityUpgradableSpecification, signalRBroadcaster)
{
GetResourceAll = GetTracks;
UpdateResource = SetMonitored;
}
private List<TrackResource> GetTracks()
{
if (!Request.Query.ArtistId.HasValue)
{
throw new BadRequestException("artistId is missing");
}
var artistId = (int)Request.Query.ArtistId;
var resources = MapToResource(_trackService.GetTracksByArtist(artistId), false, true);
return resources;
}
private void SetMonitored(TrackResource trackResource)
{
_trackService.SetTrackMonitored(trackResource.Id, trackResource.Monitored);
}
}
}

@ -0,0 +1,126 @@
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.Tv;
using NzbDrone.Core.Music;
using NzbDrone.SignalR;
namespace NzbDrone.Api.Tracks
{
public abstract class TrackModuleWithSignalR : NzbDroneRestModuleWithSignalR<TrackResource, Track>,
IHandle<TrackDownloadedEvent>
{
protected readonly ITrackService _trackService;
protected readonly IArtistService _artistService;
protected readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
protected TrackModuleWithSignalR(ITrackService trackService,
IArtistService artistService,
IQualityUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(signalRBroadcaster)
{
_trackService = trackService;
_artistService = artistService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetTrack;
}
protected TrackModuleWithSignalR(ITrackService trackService,
IArtistService artistService,
IQualityUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster,
string resource)
: base(signalRBroadcaster, resource)
{
_trackService = trackService;
_artistService = artistService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetTrack;
}
protected TrackResource GetTrack(int id)
{
var track = _trackService.GetTrack(id);
var resource = MapToResource(track, true, true);
return resource;
}
protected TrackResource MapToResource(Track track, bool includeArtist, bool includeTrackFile)
{
var resource = track.ToResource();
if (includeArtist || includeTrackFile)
{
var artist = track.Artist ?? _artistService.GetArtist(track.ArtistId);
if (includeArtist)
{
resource.Artist = artist.ToResource();
}
if (includeTrackFile && track.TrackFileId != 0)
{
resource.TrackFile = track.TrackFile.Value.ToResource(artist, _qualityUpgradableSpecification);
}
}
return resource;
}
protected List<TrackResource> MapToResource(List<Track> tracks, bool includeArtist, bool includeTrackFile)
{
var result = tracks.ToResource();
if (includeArtist || includeTrackFile)
{
var artistDict = new Dictionary<int, Core.Music.Artist>();
for (var i = 0; i < tracks.Count; i++)
{
var track = tracks[i];
var resource = result[i];
var artist = track.Artist ?? artistDict.GetValueOrDefault(tracks[i].ArtistId) ?? _artistService.GetArtist(tracks[i].ArtistId);
artistDict[artist.Id] = artist;
if (includeArtist)
{
resource.Artist = artist.ToResource();
}
if (includeTrackFile && tracks[i].TrackFileId != 0)
{
resource.TrackFile = tracks[i].TrackFile.Value.ToResource(artist, _qualityUpgradableSpecification);
}
}
}
return result;
}
//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 track in message.Track.Tracks)
{
BroadcastResourceChange(ModelAction.Updated, track.Id);
}
}
}
}

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NzbDrone.Api.TrackFiles;
using NzbDrone.Api.REST;
using NzbDrone.Api.Music;
using NzbDrone.Core.Music;
namespace NzbDrone.Api.Tracks
{
public class TrackResource : RestResource
{
public int ArtistId { get; set; }
public int TrackFileId { get; set; }
public int AlbumId { get; set; }
//public int EpisodeNumber { get; set; }
public string Title { get; set; }
//public string AirDate { get; set; }
//public DateTime? AirDateUtc { get; set; }
//public string Overview { get; set; }
public TrackFileResource TrackFile { get; set; }
public bool HasFile { get; set; }
public bool Monitored { get; set; }
//public int? AbsoluteEpisodeNumber { get; set; }
//public int? SceneAbsoluteEpisodeNumber { get; set; }
//public int? SceneEpisodeNumber { get; set; }
//public int? SceneSeasonNumber { get; set; }
//public bool UnverifiedSceneNumbering { get; set; }
//public string SeriesTitle { get; set; }
public ArtistResource Artist { get; set; }
//Hiding this so people don't think its usable (only used to set the initial state)
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool Grabbed { get; set; }
}
public static class TrackResourceMapper
{
public static TrackResource ToResource(this Track model)
{
if (model == null) return null;
return new TrackResource
{
Id = model.Id,
ArtistId = model.ArtistId,
TrackFileId = model.TrackFileId,
AlbumId = model.AlbumId,
//EpisodeNumber = model.EpisodeNumber,
Title = model.Title,
//AirDate = model.AirDate,
//AirDateUtc = model.AirDateUtc,
//Overview = model.Overview,
//EpisodeFile
HasFile = model.HasFile,
Monitored = model.Monitored,
//AbsoluteEpisodeNumber = model.AbsoluteEpisodeNumber,
//SceneAbsoluteEpisodeNumber = model.SceneAbsoluteEpisodeNumber,
//SceneEpisodeNumber = model.SceneEpisodeNumber,
//SceneSeasonNumber = model.SceneSeasonNumber,
//UnverifiedSceneNumbering = model.UnverifiedSceneNumbering,
//SeriesTitle = model.SeriesTitle,
//Series = model.Series.MapToResource(),
};
}
public static List<TrackResource> ToResource(this IEnumerable<Track> models)
{
if (models == null) return null;
return models.Select(ToResource).ToList();
}
}
}

@ -0,0 +1,16 @@
using System.Collections.Generic;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.MediaFiles.Commands
{
public class RenameArtistCommand : Command
{
public List<int> ArtistIds { get; set; }
public override bool SendUpdatesToClient => true;
public RenameArtistCommand()
{
}
}
}

@ -19,6 +19,7 @@ namespace NzbDrone.Core.MediaFiles
void Update(TrackFile trackFile);
void Delete(TrackFile trackFile, DeleteMediaFileReason reason);
List<TrackFile> GetFilesByArtist(int artistId);
List<TrackFile> GetFilesByAlbum(int artistId, int albumId);
List<TrackFile> GetFilesWithoutMediaInfo();
List<string> FilterExistingFiles(List<string> files, Artist artist);
TrackFile Get(int id);
@ -98,5 +99,10 @@ namespace NzbDrone.Core.MediaFiles
{
return _mediaFileRepository.GetFilesByArtist(artistId);
}
public List<TrackFile> GetFilesByAlbum(int artistId, int albumId)
{
return _mediaFileRepository.GetFilesByArtist(artistId);
}
}
}

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace NzbDrone.Core.MediaFiles
{
public class RenameTrackFilePreview
{
public int ArtistId { get; set; }
public int AlbumId { get; set; }
public List<int> TrackNumbers { get; set; }
public int TrackFileId { get; set; }
public string ExistingPath { get; set; }
public string NewPath { get; set; }
}
}

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Music;
namespace NzbDrone.Core.MediaFiles
{
public interface IRenameTrackFileService
{
List<RenameTrackFilePreview> GetRenamePreviews(int artistId);
List<RenameTrackFilePreview> GetRenamePreviews(int artistId, int albumId);
}
public class RenameTrackFileService : IRenameTrackFileService,
IExecute<RenameFilesCommand>,
IExecute<RenameArtistCommand>
{
private readonly IArtistService _artistService;
private readonly IAlbumService _albumService;
private readonly IMediaFileService _mediaFileService;
private readonly IMoveTrackFiles _trackFileMover;
private readonly IEventAggregator _eventAggregator;
private readonly ITrackService _trackService;
private readonly IBuildFileNames _filenameBuilder;
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
public RenameTrackFileService(IArtistService artistService,
IAlbumService albumService,
IMediaFileService mediaFileService,
IMoveTrackFiles trackFileMover,
IEventAggregator eventAggregator,
ITrackService trackService,
IBuildFileNames filenameBuilder,
IDiskProvider diskProvider,
Logger logger)
{
_artistService = artistService;
_albumService = albumService;
_mediaFileService = mediaFileService;
_trackFileMover = trackFileMover;
_eventAggregator = eventAggregator;
_trackService = trackService;
_filenameBuilder = filenameBuilder;
_diskProvider = diskProvider;
_logger = logger;
}
public List<RenameTrackFilePreview> GetRenamePreviews(int artistId)
{
// TODO
throw new NotImplementedException();
//var artist = _artistService.GetArtist(artistId);
//var tracks = _trackService.GetTracksByArtist(artistId);
//var files = _mediaFileService.GetFilesByArtist(artistId);
//return GetPreviews(artist, tracks, files)
// .OrderByDescending(e => e.SeasonNumber)
// .ThenByDescending(e => e.TrackNumbers.First())
// .ToList();
}
public List<RenameTrackFilePreview> GetRenamePreviews(int artistId, int albumId)
{
// TODO
//throw new NotImplementedException();
var artist = _artistService.GetArtist(artistId);
var album = _albumService.GetAlbum(albumId);
var tracks = _trackService.GetTracksByAlbum(artistId, albumId);
var files = _mediaFileService.GetFilesByAlbum(artistId, albumId);
return GetPreviews(artist, album, tracks, files)
.OrderByDescending(e => e.TrackNumbers.First()).ToList();
}
private IEnumerable<RenameTrackFilePreview> GetPreviews(Artist artist, Album album, List<Track> tracks, List<TrackFile> files)
{
foreach (var f in files)
{
var file = f;
var tracksInFile = tracks.Where(e => e.TrackFileId == file.Id).ToList();
var trackFilePath = Path.Combine(artist.Path, file.RelativePath);
if (!tracksInFile.Any())
{
_logger.Warn("File ({0}) is not linked to any tracks", trackFilePath);
continue;
}
var albumId = tracksInFile.First().AlbumId;
var newName = _filenameBuilder.BuildTrackFileName(tracksInFile, artist, album, file);
var newPath = _filenameBuilder.BuildTrackFilePath(artist, album, newName, Path.GetExtension(trackFilePath));
if (!trackFilePath.PathEquals(newPath, StringComparison.Ordinal))
{
yield return new RenameTrackFilePreview
{
ArtistId = artist.Id,
AlbumId = albumId,
TrackNumbers = tracksInFile.Select(e => e.TrackNumber).ToList(),
TrackFileId = file.Id,
ExistingPath = file.RelativePath,
NewPath = artist.Path.GetRelativePath(newPath)
};
}
}
}
private void RenameFiles(List<TrackFile> trackFiles, Artist artist)
{
// TODO
throw new NotImplementedException();
//var renamed = new List<TrackFile>();
//foreach (var trackFile in trackFiles)
//{
// var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath);
// try
// {
// _logger.Debug("Renaming track file: {0}", trackFile);
// _trackFileMover.MoveTrackFile(trackFile, artist);
// _mediaFileService.Update(trackFile);
// renamed.Add(trackFile);
// _logger.Debug("Renamed track file: {0}", trackFile);
// }
// catch (SameFilenameException ex)
// {
// _logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);
// }
// catch (Exception ex)
// {
// _logger.Error(ex, "Failed to rename file {0}", trackFilePath);
// }
//}
//if (renamed.Any())
//{
// _diskProvider.RemoveEmptySubfolders(artist.Path);
// _eventAggregator.PublishEvent(new ArtistRenamedEvent(artist));
//}
}
public void Execute(RenameFilesCommand message)
{
// TODO
throw new NotImplementedException();
//var artist = _artistService.GetArtist(message.ArtistId);
//var trackFiles = _mediaFileService.Get(message.Files);
//_logger.ProgressInfo("Renaming {0} files for {1}", trackFiles.Count, artist.Title);
//RenameFiles(trackFiles, artist);
//_logger.ProgressInfo("Selected track files renamed for {0}", artist.Title);
}
public void Execute(RenameArtistCommand message)
{
// TODO
throw new NotImplementedException();
//_logger.Debug("Renaming all files for selected artist");
//var artistToRename = _artistService.GetArtist(message.ArtistIds);
//foreach (var artist in artistToRename)
//{
// var trackFiles = _mediaFileService.GetFilesByArtist(artist.Id);
// _logger.ProgressInfo("Renaming all files in artist: {0}", artist.Title);
// RenameFiles(trackFiles, artist);
// _logger.ProgressInfo("All track files renamed for {0}", artist.Title);
//}
}
}
}

@ -23,6 +23,10 @@ namespace NzbDrone.Core.Music
}
public string ForeignArtistId { get; set; }
public string MBId { get; set; }
public int TADBId { get; set; }
public int DiscogsId { get; set; }
public string AMId { get; set; }
public string Name { get; set; }
public string NameSlug { get; set; }
public string CleanName { get; set; }
@ -52,6 +56,10 @@ namespace NzbDrone.Core.Music
{
ForeignArtistId = otherArtist.ForeignArtistId;
MBId = otherArtist.MBId;
TADBId = otherArtist.TADBId;
DiscogsId = otherArtist.DiscogsId;
AMId = otherArtist.AMId;
Name = otherArtist.Name;
NameSlug = otherArtist.NameSlug;
CleanName = otherArtist.CleanName;

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Music
public string ForeignTrackId { get; set; }
public int AlbumId { get; set; }
public LazyLoaded<Artist> Artist { get; set; }
public Artist Artist { get; set; }
public int ArtistId { get; set; } // This is the DB Id of the Artist, not the SpotifyId
//public int CompilationId { get; set; }

@ -724,7 +724,10 @@
<Compile Include="MediaFiles\Commands\BackendCommandAttribute.cs" />
<Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" />
<Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" />
<Compile Include="MediaFiles\Commands\RenameArtistCommand.cs" />
<Compile Include="MediaFiles\Events\TrackDownloadedEvent.cs" />
<Compile Include="MediaFiles\RenameTrackFilePreview.cs" />
<Compile Include="MediaFiles\RenameTrackFileService.cs" />
<Compile Include="MediaFiles\TrackFileMovingService.cs" />
<Compile Include="MediaFiles\TrackFileMoveResult.cs" />
<Compile Include="MediaFiles\TrackImport\ImportMode.cs" />

@ -20,7 +20,9 @@ namespace NzbDrone.Core.Organizer
string BuildFileName(List<Episode> episodes, Series series, EpisodeFile episodeFile, NamingConfig namingConfig = null);
string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null);
string BuildFilePath(Series series, int seasonNumber, string fileName, string extension);
string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension);
string BuildSeasonPath(Series series, int seasonNumber);
string BuildAlbumPath(Artist artist, Album album);
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
string GetSeriesFolder(Series series, NamingConfig namingConfig = null);
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
@ -202,6 +204,15 @@ namespace NzbDrone.Core.Organizer
return Path.Combine(path, fileName + extension);
}
public string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension)
{
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
var path = BuildAlbumPath(artist, album);
return Path.Combine(path, fileName + extension);
}
public string BuildSeasonPath(Series series, int seasonNumber)
{
var path = series.Path;
@ -225,6 +236,24 @@ namespace NzbDrone.Core.Organizer
return path;
}
public string BuildAlbumPath(Artist artist, Album album)
{
var path = artist.Path;
if (artist.AlbumFolder)
{
var albumFolder = GetAlbumFolder(artist, album);
albumFolder = CleanFileName(albumFolder);
path = Path.Combine(path, albumFolder);
}
return path;
}
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)
{
var episodeFormat = GetEpisodeFormat(nameSpec.StandardEpisodeFormat).LastOrDefault();

@ -16,11 +16,11 @@
</h2>
</div>
</div>
<div class="row new-artist-overview x-overview">
<!--<div class="row new-artist-overview x-overview">
<div class="col-md-12 overview-internal">
{{overview}}
</div>
</div>
</div>-->
<div class="row">
{{#unless existing}}
{{#unless path}}

@ -29,20 +29,18 @@
</div>
<div class="col-md-3">
<span class="artist-info-links">
<a href="{{traktUrl}}" class="label label-info">Trakt</a>
<a href="{{MBUrl}}" class="label label-info">MusicBrainz</a>
<a href="{{tvdbUrl}}" class="label label-info">The TVDB</a>
{{#if imdbId}}
<a href="{{imdbUrl}}" class="label label-info">IMDB</a>
{{#if tadbId}}
<a href="{{TADBUrl}}" class="label label-info">The AudioDB</a>
{{/if}}
{{#if tvRageId}}
<a href="{{tvRageUrl}}" class="label label-info">TV Rage</a>
{{#if discogsId}}
<a href="{{discogsUrl}}" class="label label-info">Discogs</a>
{{/if}}
{{#if tvMazeId}}
<a href="{{tvMazeUrl}}" class="label label-info">TV Maze</a>
{{#if amId}}
<a href="{{allMusicUrl}}" class="label label-info">AllMusic</a>
{{/if}}
</span>
</div>

@ -321,15 +321,17 @@ module.exports = Marionette.Layout.extend({
_showFooter : function() {
var footerModel = new FooterModel();
var artist = this.artistCollection.models.length;
var episodes = 0;
var episodeFiles = 0;
var albums = 0;
var tracks = 0;
var trackFiles = 0;
var ended = 0;
var continuing = 0;
var monitored = 0;
_.each(this.artistCollection.models, function(model) {
episodes += model.get('episodeCount'); // TODO: Refactor to Seasons and Tracks
episodeFiles += model.get('episodeFileCount');
albums += model.get('albumCount');
tracks += model.get('episodeCount'); // TODO: Refactor to Seasons and Tracks
trackFiles += model.get('episodeFileCount');
/*if (model.get('status').toLowerCase() === 'ended') {
ended++;
@ -348,8 +350,9 @@ module.exports = Marionette.Layout.extend({
continuing : continuing,
monitored : monitored,
unmonitored : artist - monitored,
episodes : episodes,
episodeFiles : episodeFiles
albums : albums,
tracks : tracks,
trackFiles : trackFiles
});
this.footer.show(new FooterView({ model : footerModel }));

@ -34,11 +34,14 @@
<div class="artist-stats col-sm-4">
<dl class="dl-horizontal">
<dt>Albums</dt>
<dd>{{albums}}</dd>
<dt>Tracks</dt>
<dd>{{episodes}}</dd>
<dd>{{tracks}}</dd>
<dt>Files</dt>
<dd>{{episodeFiles}}</dd>
<dd>{{trackFiles}}</dd>
</dl>
</div>
</div>

@ -1,4 +1,4 @@
<div class="progress episode-progress">
<span class="progressbar-back-text">{{episodeFileCount}} / {{episodeCount}}</span>
<div class="progress-bar {{EpisodeProgressClass}} episode-progress" style="width:{{percentOfEpisodes}}%"><span class="progressbar-front-text">{{episodeFileCount}} / {{episodeCount}}</span></div>
<div class="progress track-progress">
<span class="progressbar-back-text">{{trackFileCount}} / {{trackCount}}</span>
<div class="progress-bar {{TrackProgressClass}} track-progress" style="width:{{percentOfTracks}}%"><span class="progressbar-front-text">{{trackFileCount}} / {{trackCount}}</span></div>
</div>

@ -4,11 +4,11 @@ var TrackModel = require('./TrackModel');
require('./TrackCollection');
module.exports = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/episode',
url : window.NzbDrone.ApiRoot + '/track',
model : TrackModel,
state : {
sortKey : 'episodeNumber',
sortKey : 'trackNumber',
order : 1,
pageSize : 100000
},
@ -18,28 +18,28 @@ module.exports = PageableCollection.extend({
originalFetch : Backbone.Collection.prototype.fetch,
initialize : function(options) {
this.seriesId = options.seriesId;
this.artistId = options.artistId;
},
bySeason : function(season) {
var filtered = this.filter(function(episode) {
return episode.get('seasonNumber') === season;
bySeason : function(album) {
var filtered = this.filter(function(track) {
return track.get('albumId') === album;
});
var EpisodeCollection = require('./TrackCollection');
var TrackCollection = require('./TrackCollection');
return new EpisodeCollection(filtered);
return new TrackCollection(filtered);
},
comparator : function(model1, model2) {
var episode1 = model1.get('episodeNumber');
var episode2 = model2.get('episodeNumber');
var track1 = model1.get('trackNumber');
var track2 = model2.get('trackNumber');
if (episode1 < episode2) {
if (track1 < track2) {
return 1;
}
if (episode1 > episode2) {
if (track1 > track2) {
return -1;
}
@ -47,15 +47,15 @@ module.exports = PageableCollection.extend({
},
fetch : function(options) {
if (!this.seriesId) {
throw 'seriesId is required';
if (!this.artistId) {
throw 'artistId is required';
}
if (!options) {
options = {};
}
options.data = { seriesId : this.seriesId };
options.data = { artistId : this.artistId };
return this.originalFetch.call(this, options);
}

@ -2,7 +2,7 @@ var Backbone = require('backbone');
var TrackFileModel = require('./TrackFileModel');
module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/episodefile',
url : window.NzbDrone.ApiRoot + '/trackfile',
model : TrackFileModel,
originalFetch : Backbone.Collection.prototype.fetch,
@ -21,7 +21,7 @@ module.exports = Backbone.Collection.extend({
options = {};
}
options.data = { seriesId : this.seriesId };
options.data = { artistId : this.artistId };
return this.originalFetch.call(this, options);
}

@ -2,12 +2,12 @@ var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
defaults : {
seasonNumber : 0,
albumId : 0,
status : 0
},
methodUrls : {
'update' : window.NzbDrone.ApiRoot + '/episode'
'update' : window.NzbDrone.ApiRoot + '/track'
},
sync : function(method, model, options) {

@ -19,11 +19,6 @@ module.exports = NzbDroneController.extend({
this.showMainRegion(new AddArtistLayout({ action : action }));
},
artistDetails: function(query) {
this.setTitle('Artist Detail');
this.showMainRegion(new SeriesDetailsLayout());
},
calendar : function() {
this.setTitle('Calendar');
this.showMainRegion(new CalendarLayout());

@ -19,24 +19,20 @@ Handlebars.registerHelper('poster', function() {
return new Handlebars.SafeString('<img class="series-poster placeholder-image" src="{0}">'.format(placeholder));
});
Handlebars.registerHelper('traktUrl', function() {
return 'http://trakt.tv/search/tvdb/' + this.tvdbId + '?id_type=show';
Handlebars.registerHelper('MBUrl', function() {
return 'https://musicbrainz.org/artist/' + this.mbId;
});
Handlebars.registerHelper('imdbUrl', function() {
return 'http://imdb.com/title/' + this.imdbId;
Handlebars.registerHelper('TADBUrl', function() {
return 'http://www.theaudiodb.com/artist/' + this.tadbId;
});
Handlebars.registerHelper('tvdbUrl', function() {
return 'http://www.thetvdb.com/?tab=series&id=' + this.tvdbId;
Handlebars.registerHelper('discogsUrl', function() {
return 'https://www.discogs.com/artist/' + this.discogsId;
});
Handlebars.registerHelper('tvRageUrl', function() {
return 'http://www.tvrage.com/shows/id-' + this.tvRageId;
});
Handlebars.registerHelper('tvMazeUrl', function() {
return 'http://www.tvmaze.com/shows/' + this.tvMazeId + '/_';
Handlebars.registerHelper('allMusicUrl', function() {
return 'http://www.allmusic.com/artist/' + this.amId;
});
Handlebars.registerHelper('route', function() {
@ -56,6 +52,19 @@ Handlebars.registerHelper('percentOfEpisodes', function() {
return percent;
});
Handlebars.registerHelper('percentOfTracks', function() {
var trackCount = this.trackCount;
var trackFileCount = this.trackFileCount;
var percent = 100;
if (trackCount > 0) {
percent = trackFileCount / trackCount * 100;
}
return percent;
});
Handlebars.registerHelper('seasonCountHelper', function() {
var seasonCount = this.seasonCount;
var continuing = this.status === 'continuing';
@ -87,7 +96,7 @@ Handlebars.registerHelper('albumCountHelper', function() {
var albumCount = this.albumCount;
if (albumCount === 1) {
return new Handlebars.SafeString('<span class="label label-info">{0} Albums</span>'.format(albumCount));
return new Handlebars.SafeString('<span class="label label-info">{0} Album</span>'.format(albumCount));
}
return new Handlebars.SafeString('<span class="label label-info">{0} Albums</span>'.format(albumCount));

@ -18,7 +18,6 @@ module.exports = Marionette.AppRouter.extend({
'rss' : 'rss',
'system' : 'system',
'system/:action' : 'system',
'artist/:query' : 'artistDetails',
'seasonpass' : 'seasonPass',
'artisteditor' : 'artistEditor',
':whatever' : 'showNotFound'

Loading…
Cancel
Save