mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-16 10:03:51 -07:00
Implemented Tracks and ability to save to the DB. Updated SkyHook to support ArtistSlug.
This commit is contained in:
parent
66445930eb
commit
fbb6691ea1
13 changed files with 404 additions and 87 deletions
|
@ -48,16 +48,16 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||||
.WithColumn("Overview").AsString();
|
.WithColumn("Overview").AsString();
|
||||||
|
|
||||||
Create.TableForModel("Tracks")
|
Create.TableForModel("Tracks")
|
||||||
.WithColumn("ItunesTrackId").AsInt32().Unique()
|
.WithColumn("SpotifyTrackId").AsString().Nullable() // This shouldn't be nullable, but TrackRepository won't behave. Someone please fix this.
|
||||||
.WithColumn("AlbumId").AsString()
|
.WithColumn("AlbumId").AsString()
|
||||||
.WithColumn("ArtistsId").AsString().Nullable()
|
.WithColumn("ArtistId").AsString() // This may be a list of Ids in future for compilations
|
||||||
|
.WithColumn("ArtistSpotifyId").AsString()
|
||||||
|
.WithColumn("Compilation").AsBoolean()
|
||||||
.WithColumn("TrackNumber").AsInt32()
|
.WithColumn("TrackNumber").AsInt32()
|
||||||
.WithColumn("Title").AsString().Nullable()
|
.WithColumn("Title").AsString().Nullable()
|
||||||
.WithColumn("Ignored").AsBoolean().Nullable()
|
.WithColumn("Ignored").AsBoolean().Nullable()
|
||||||
.WithColumn("Explict").AsBoolean()
|
.WithColumn("Explict").AsBoolean()
|
||||||
.WithColumn("Monitored").AsBoolean()
|
.WithColumn("Monitored").AsBoolean()
|
||||||
.WithColumn("TrackExplicitName").AsString().Nullable()
|
|
||||||
.WithColumn("TrackCensoredName").AsString().Nullable()
|
|
||||||
.WithColumn("TrackFileId").AsInt32().Nullable()
|
.WithColumn("TrackFileId").AsInt32().Nullable()
|
||||||
.WithColumn("ReleaseDate").AsDateTime().Nullable();
|
.WithColumn("ReleaseDate").AsDateTime().Nullable();
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
.Relationships.AutoMapICollectionOrComplexProperties()
|
.Relationships.AutoMapICollectionOrComplexProperties()
|
||||||
.For("Tracks")
|
.For("Tracks")
|
||||||
.LazyLoad(condition: parent => parent.Id > 0,
|
.LazyLoad(condition: parent => parent.Id > 0,
|
||||||
query: (db, parent) => db.Query<Track>().Where(c => c.SpotifyTrackId == parent.Id).ToList())
|
query: (db, parent) => db.Query<Track>().Where(c => c.ArtistId == parent.Id).ToList()) // TODO: Figure what the hell to do here
|
||||||
.HasOne(file => file.Artist, file => file.AlbumId);
|
.HasOne(file => file.Artist, file => file.AlbumId);
|
||||||
|
|
||||||
Mapper.Entity<Track>().RegisterModel("Tracks")
|
Mapper.Entity<Track>().RegisterModel("Tracks")
|
||||||
|
@ -110,6 +110,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
.Ignore(e => e.Album)
|
.Ignore(e => e.Album)
|
||||||
.Ignore(e => e.HasFile)
|
.Ignore(e => e.HasFile)
|
||||||
.Relationship()
|
.Relationship()
|
||||||
|
// TODO: Need to implement ArtistId to Artist.Id here
|
||||||
.HasOne(track => track.TrackFile, track => track.TrackFileId); // TODO: Check lazy load for artists
|
.HasOne(track => track.TrackFile, track => track.TrackFileId); // TODO: Check lazy load for artists
|
||||||
|
|
||||||
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions")
|
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions")
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace NzbDrone.Core.Jobs
|
||||||
//new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
//new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 6*60, TypeName = typeof(CheckHealthCommand).FullName},
|
new ScheduledTask{ Interval = 6*60, TypeName = typeof(CheckHealthCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshArtistCommand).FullName},
|
new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshArtistCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshSeriesCommand).FullName}, // TODO: Remove
|
//new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshSeriesCommand).FullName}, // TODO: Remove
|
||||||
new ScheduledTask{ Interval = 24*60, TypeName = typeof(HousekeepingCommand).FullName},
|
new ScheduledTask{ Interval = 24*60, TypeName = typeof(HousekeepingCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 7*24*60, TypeName = typeof(BackupCommand).FullName},
|
new ScheduledTask{ Interval = 7*24*60, TypeName = typeof(BackupCommand).FullName},
|
||||||
|
|
||||||
|
|
18
src/NzbDrone.Core/MediaFiles/Events/TrackFileAddedEvent.cs
Normal file
18
src/NzbDrone.Core/MediaFiles/Events/TrackFileAddedEvent.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using NzbDrone.Common.Messaging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles.Events
|
||||||
|
{
|
||||||
|
public class TrackFileAddedEvent : IEvent
|
||||||
|
{
|
||||||
|
public TrackFile TrackFile { get; private set; }
|
||||||
|
|
||||||
|
public TrackFileAddedEvent(TrackFile trackFile)
|
||||||
|
{
|
||||||
|
TrackFile = trackFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -115,15 +115,15 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
artist.SpotifyId = httpResponse.Resource.Id;
|
artist.SpotifyId = httpResponse.Resource.Id;
|
||||||
artist.Genres = httpResponse.Resource.Genres;
|
artist.Genres = httpResponse.Resource.Genres;
|
||||||
|
|
||||||
|
var albumRet = MapAlbums(artist);
|
||||||
|
artist = albumRet.Item1;
|
||||||
|
|
||||||
artist = MapAlbums(artist);
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: implement tracks api call
|
|
||||||
return new Tuple<Artist, List<Track>>(artist, new List<Track>());
|
return new Tuple<Artist, List<Track>>(albumRet.Item1, albumRet.Item2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Artist MapAlbums(Artist artist)
|
private Tuple<Artist, List<Track>> MapAlbums(Artist artist)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Find all albums for the artist and all tracks for said album
|
// Find all albums for the artist and all tracks for said album
|
||||||
|
@ -141,21 +141,23 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
throw new HttpException(httpRequest, httpResponse);
|
throw new HttpException(httpRequest, httpResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Track> masterTracks = new List<Track>();
|
||||||
List<Album> albums = new List<Album>();
|
List<Album> albums = new List<Album>();
|
||||||
foreach(var albumResource in httpResponse.Resource.Items)
|
foreach(var albumResource in httpResponse.Resource.Items)
|
||||||
{
|
{
|
||||||
Album album = new Album();
|
Album album = new Album();
|
||||||
album.AlbumId = albumResource.Id;
|
album.AlbumId = albumResource.Id;
|
||||||
album.Title = albumResource.Name;
|
album.Title = albumResource.Name;
|
||||||
album.ArtworkUrl = albumResource.Images[0].Url;
|
album.ArtworkUrl = albumResource.Images.Count > 0 ? albumResource.Images[0].Url : "";
|
||||||
album.Tracks = MapTracksToAlbum(album);
|
album.Tracks = MapTracksToAlbum(album);
|
||||||
|
masterTracks.InsertRange(masterTracks.Count, album.Tracks);
|
||||||
albums.Add(album);
|
albums.Add(album);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We now need to get all tracks for each album
|
// TODO: We now need to get all tracks for each album
|
||||||
|
|
||||||
artist.Albums = albums;
|
artist.Albums = albums;
|
||||||
return artist;
|
return new Tuple<Artist, List<Track>>(artist, masterTracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Track> MapTracksToAlbum(Album album)
|
private List<Track> MapTracksToAlbum(Album album)
|
||||||
|
@ -183,11 +185,12 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
// TODO: Implement more track mapping
|
// TODO: Implement more track mapping
|
||||||
//track.Artist = trackResource.Artists
|
//track.Artist = trackResource.Artists
|
||||||
//track.ArtistId = album.
|
//track.ArtistId = album.
|
||||||
|
track.SpotifyTrackId = trackResource.Id;
|
||||||
|
track.ArtistSpotifyId = trackResource.Artists.Count > 0 ? trackResource.Artists[0].Id : null;
|
||||||
track.Explict = trackResource.Explicit;
|
track.Explict = trackResource.Explicit;
|
||||||
track.Compilation = trackResource.Artists.Count > 1;
|
track.Compilation = trackResource.Artists.Count > 1;
|
||||||
track.TrackNumber = trackResource.TrackNumber;
|
track.TrackNumber = trackResource.TrackNumber;
|
||||||
track.TrackExplicitName = trackResource.Name;
|
track.Title = trackResource.Name;
|
||||||
track.TrackCensoredName = trackResource.Name;
|
|
||||||
tracks.Add(track);
|
tracks.Add(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +275,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
artist.ArtistName = artistResource.Name;
|
artist.ArtistName = artistResource.Name;
|
||||||
artist.SpotifyId = artistResource.Id;
|
artist.SpotifyId = artistResource.Id;
|
||||||
artist.Genres = artistResource.Genres;
|
artist.Genres = artistResource.Genres;
|
||||||
//artist.ArtistSlug = a//TODO implement artistSlug mapping;
|
artist.ArtistSlug = Parser.Parser.CleanArtistTitle(artist.ArtistName);
|
||||||
artists.Add(artist);
|
artists.Add(artist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,8 @@ namespace NzbDrone.Core.Music
|
||||||
private readonly IArtistService _artistService;
|
private readonly IArtistService _artistService;
|
||||||
private readonly IRefreshTrackService _refreshTrackService;
|
private readonly IRefreshTrackService _refreshTrackService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
//private readonly IDailySeriesService _dailySeriesService;
|
|
||||||
private readonly IDiskScanService _diskScanService;
|
private readonly IDiskScanService _diskScanService;
|
||||||
//private readonly ICheckIfArtistShouldBeRefreshed _checkIfArtistShouldBeRefreshed;
|
private readonly ICheckIfArtistShouldBeRefreshed _checkIfArtistShouldBeRefreshed;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public RefreshArtistService(IProvideArtistInfo artistInfo,
|
public RefreshArtistService(IProvideArtistInfo artistInfo,
|
||||||
|
@ -32,7 +31,7 @@ namespace NzbDrone.Core.Music
|
||||||
IRefreshTrackService refreshTrackService,
|
IRefreshTrackService refreshTrackService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
IDiskScanService diskScanService,
|
IDiskScanService diskScanService,
|
||||||
//ICheckIfArtistShouldBeRefreshed checkIfArtistShouldBeRefreshed,
|
ICheckIfArtistShouldBeRefreshed checkIfArtistShouldBeRefreshed,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_artistInfo = artistInfo;
|
_artistInfo = artistInfo;
|
||||||
|
@ -40,7 +39,7 @@ namespace NzbDrone.Core.Music
|
||||||
_refreshTrackService = refreshTrackService;
|
_refreshTrackService = refreshTrackService;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_diskScanService = diskScanService;
|
_diskScanService = diskScanService;
|
||||||
//_checkIfArtistShouldBeRefreshed = checkIfArtistShouldBeRefreshed;
|
_checkIfArtistShouldBeRefreshed = checkIfArtistShouldBeRefreshed;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +74,6 @@ namespace NzbDrone.Core.Music
|
||||||
artist.CleanTitle = artistInfo.CleanTitle;
|
artist.CleanTitle = artistInfo.CleanTitle;
|
||||||
artist.LastInfoSync = DateTime.UtcNow;
|
artist.LastInfoSync = DateTime.UtcNow;
|
||||||
artist.Images = artistInfo.Images;
|
artist.Images = artistInfo.Images;
|
||||||
//artist.Actors = artistInfo.Actors;
|
|
||||||
artist.Genres = artistInfo.Genres;
|
artist.Genres = artistInfo.Genres;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -142,7 +140,7 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
foreach (var artist in allArtists)
|
foreach (var artist in allArtists)
|
||||||
{
|
{
|
||||||
if (message.Trigger == CommandTrigger.Manual /*|| _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)*/)
|
if (message.Trigger == CommandTrigger.Manual || _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace NzbDrone.Core.Music
|
||||||
var successCount = 0;
|
var successCount = 0;
|
||||||
var failCount = 0;
|
var failCount = 0;
|
||||||
|
|
||||||
var existingTracks = _trackService.GetTrackByArtist(artist.SpotifyId);
|
var existingTracks = _trackService.GetTracksByArtist(artist.SpotifyId);
|
||||||
var albums = artist.Albums;
|
var albums = artist.Albums;
|
||||||
|
|
||||||
var updateList = new List<Track>();
|
var updateList = new List<Track>();
|
||||||
|
@ -57,13 +57,26 @@ namespace NzbDrone.Core.Music
|
||||||
trackToUpdate.Monitored = GetMonitoredStatus(track, albums);
|
trackToUpdate.Monitored = GetMonitoredStatus(track, albums);
|
||||||
newList.Add(trackToUpdate);
|
newList.Add(trackToUpdate);
|
||||||
}
|
}
|
||||||
trackToUpdate.ArtistId = artist.SpotifyId; // TODO: Ensure LazyLoaded<Artist> field gets updated.
|
|
||||||
|
trackToUpdate.SpotifyTrackId = track.SpotifyTrackId;
|
||||||
trackToUpdate.TrackNumber = track.TrackNumber;
|
trackToUpdate.TrackNumber = track.TrackNumber;
|
||||||
trackToUpdate.Title = track.Title ?? "Unknown";
|
trackToUpdate.Title = track.Title ?? "Unknown";
|
||||||
|
trackToUpdate.AlbumId = track.AlbumId;
|
||||||
|
trackToUpdate.Album = track.Album;
|
||||||
|
trackToUpdate.Explict = track.Explict;
|
||||||
|
if (track.ArtistSpotifyId.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
trackToUpdate.ArtistSpotifyId = artist.SpotifyId;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
trackToUpdate.ArtistSpotifyId = track.ArtistSpotifyId;
|
||||||
|
}
|
||||||
|
trackToUpdate.ArtistId = track.ArtistId;
|
||||||
|
trackToUpdate.Compilation = track.Compilation;
|
||||||
|
|
||||||
// TODO: Implement rest of [RefreshTrackService] fields
|
// TODO: Implement rest of [RefreshTrackService] fields
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
successCount++;
|
successCount++;
|
||||||
}
|
}
|
||||||
|
|
43
src/NzbDrone.Core/Music/ShouldRefreshArtist.cs
Normal file
43
src/NzbDrone.Core/Music/ShouldRefreshArtist.cs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
public interface ICheckIfArtistShouldBeRefreshed
|
||||||
|
{
|
||||||
|
bool ShouldRefresh(Artist artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CheckIfArtistShouldBeRefreshed : ICheckIfArtistShouldBeRefreshed
|
||||||
|
{
|
||||||
|
private readonly ITrackService _trackService;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public CheckIfArtistShouldBeRefreshed(ITrackService trackService, Logger logger)
|
||||||
|
{
|
||||||
|
_trackService = trackService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldRefresh(Artist artist)
|
||||||
|
{
|
||||||
|
if (artist.LastInfoSync < DateTime.UtcNow.AddDays(-30))
|
||||||
|
{
|
||||||
|
_logger.Trace("Artist {0} last updated more than 30 days ago, should refresh.", artist.ArtistName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artist.LastInfoSync >= DateTime.UtcNow.AddHours(-6))
|
||||||
|
{
|
||||||
|
_logger.Trace("Artist {0} last updated less than 6 hours ago, should not be refreshed.", artist.ArtistName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//_logger.Trace("Artist {0} ended long ago, should not be refreshed.", artist.Title);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,18 +17,17 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
public const string RELEASE_DATE_FORMAT = "yyyy-MM-dd";
|
public const string RELEASE_DATE_FORMAT = "yyyy-MM-dd";
|
||||||
|
|
||||||
public int SpotifyTrackId { get; set; }
|
public string SpotifyTrackId { get; set; }
|
||||||
public string AlbumId { get; set; }
|
public string AlbumId { get; set; }
|
||||||
public LazyLoaded<Artist> Artist { get; set; }
|
public LazyLoaded<Artist> Artist { get; set; }
|
||||||
public string ArtistId { get; set; }
|
public string ArtistSpotifyId { get; set; }
|
||||||
public int CompilationId { get; set; }
|
public int ArtistId { get; set; } // This is the DB Id of the Artist, not the SpotifyId
|
||||||
|
//public int CompilationId { get; set; }
|
||||||
public bool Compilation { get; set; }
|
public bool Compilation { get; set; }
|
||||||
public int TrackNumber { get; set; }
|
public int TrackNumber { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public bool Ignored { get; set; }
|
public bool Ignored { get; set; }
|
||||||
public bool Explict { get; set; }
|
public bool Explict { get; set; }
|
||||||
public string TrackExplicitName { get; set; }
|
|
||||||
public string TrackCensoredName { get; set; }
|
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public int TrackFileId { get; set; }
|
public int TrackFileId { get; set; }
|
||||||
public DateTime? ReleaseDate { get; set; }
|
public DateTime? ReleaseDate { get; set; }
|
||||||
|
|
165
src/NzbDrone.Core/Music/TrackRepository.cs
Normal file
165
src/NzbDrone.Core/Music/TrackRepository.cs
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using Marr.Data.QGen;
|
||||||
|
using NzbDrone.Core.Datastore.Extensions;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
public interface ITrackRepository : IBasicRepository<Track>
|
||||||
|
{
|
||||||
|
Track Find(string artistId, string albumId, int trackNumber);
|
||||||
|
List<Track> GetTracks(string artistId);
|
||||||
|
List<Track> GetTracks(string artistId, string albumId);
|
||||||
|
List<Track> GetTracksByFileId(int fileId);
|
||||||
|
List<Track> TracksWithFiles(string artistId);
|
||||||
|
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
||||||
|
PagingSpec<Track> TracksWhereCutoffUnmet(PagingSpec<Track> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff);
|
||||||
|
void SetMonitoredFlat(Track episode, bool monitored);
|
||||||
|
void SetMonitoredByAlbum(string artistId, string albumId, bool monitored);
|
||||||
|
void SetFileId(int trackId, int fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TrackRepository : BasicRepository<Track>, ITrackRepository
|
||||||
|
{
|
||||||
|
private readonly IMainDatabase _database;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public TrackRepository(IMainDatabase database, IEventAggregator eventAggregator, Logger logger)
|
||||||
|
: base(database, eventAggregator)
|
||||||
|
{
|
||||||
|
_database = database;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Track Find(string artistId, string albumId, int trackNumber)
|
||||||
|
{
|
||||||
|
return Query.Where(s => s.ArtistSpotifyId == artistId)
|
||||||
|
.AndWhere(s => s.AlbumId == albumId)
|
||||||
|
.AndWhere(s => s.TrackNumber == trackNumber)
|
||||||
|
.SingleOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Track> GetTracks(string artistId)
|
||||||
|
{
|
||||||
|
return Query.Where(s => s.ArtistSpotifyId == artistId).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Track> GetTracks(string artistId, string albumId)
|
||||||
|
{
|
||||||
|
return Query.Where(s => s.ArtistSpotifyId == artistId)
|
||||||
|
.AndWhere(s => s.AlbumId == albumId)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Track> GetTracksByFileId(int fileId)
|
||||||
|
{
|
||||||
|
return Query.Where(e => e.TrackFileId == fileId).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Track> TracksWithFiles(string artistId)
|
||||||
|
{
|
||||||
|
return Query.Join<Track, TrackFile>(JoinType.Inner, e => e.TrackFile, (e, ef) => e.TrackFileId == ef.Id)
|
||||||
|
.Where(e => e.ArtistSpotifyId == artistId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PagingSpec<Track> TracksWhereCutoffUnmet(PagingSpec<Track> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
||||||
|
{
|
||||||
|
pagingSpec.TotalRecords = EpisodesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).GetRowCount();
|
||||||
|
pagingSpec.Records = EpisodesWhereCutoffUnmetQuery(pagingSpec, qualitiesBelowCutoff).ToList();
|
||||||
|
|
||||||
|
return pagingSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void SetMonitoredFlat(Track track, bool monitored)
|
||||||
|
{
|
||||||
|
track.Monitored = monitored;
|
||||||
|
SetFields(track, p => p.Monitored);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMonitoredByAlbum(string artistId, string albumId, bool monitored)
|
||||||
|
{
|
||||||
|
var mapper = _database.GetDataMapper();
|
||||||
|
|
||||||
|
mapper.AddParameter("artistId", artistId);
|
||||||
|
mapper.AddParameter("albumId", albumId);
|
||||||
|
mapper.AddParameter("monitored", monitored);
|
||||||
|
|
||||||
|
const string sql = "UPDATE Tracks " +
|
||||||
|
"SET Monitored = @monitored " +
|
||||||
|
"WHERE ArtistId = @artistId " +
|
||||||
|
"AND AlbumId = @albumId";
|
||||||
|
|
||||||
|
mapper.ExecuteNonQuery(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFileId(int episodeId, int fileId)
|
||||||
|
{
|
||||||
|
SetFields(new Track { Id = episodeId, TrackFileId = fileId }, track => track.TrackFileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec)
|
||||||
|
{
|
||||||
|
var currentTime = DateTime.UtcNow;
|
||||||
|
|
||||||
|
pagingSpec.TotalRecords = GetMissingEpisodesQuery(pagingSpec, currentTime).GetRowCount();
|
||||||
|
pagingSpec.Records = GetMissingEpisodesQuery(pagingSpec, currentTime).ToList();
|
||||||
|
|
||||||
|
return pagingSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortBuilder<Track> GetMissingEpisodesQuery(PagingSpec<Track> pagingSpec, DateTime currentTime)
|
||||||
|
{
|
||||||
|
return Query.Join<Track, Artist>(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistSpotifyId == s.SpotifyId)
|
||||||
|
.Where(pagingSpec.FilterExpression)
|
||||||
|
.AndWhere(e => e.TrackFileId == 0)
|
||||||
|
.AndWhere(BuildAirDateUtcCutoffWhereClause(currentTime))
|
||||||
|
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||||
|
.Skip(pagingSpec.PagingOffset())
|
||||||
|
.Take(pagingSpec.PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private SortBuilder<Track> EpisodesWhereCutoffUnmetQuery(PagingSpec<Track> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
||||||
|
{
|
||||||
|
return Query.Join<Track, Artist>(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistSpotifyId == s.SpotifyId)
|
||||||
|
.Join<Track, TrackFile>(JoinType.Left, e => e.TrackFile, (e, s) => e.TrackFileId == s.Id)
|
||||||
|
.Where(pagingSpec.FilterExpression)
|
||||||
|
.AndWhere(e => e.TrackFileId != 0)
|
||||||
|
.AndWhere(BuildQualityCutoffWhereClause(qualitiesBelowCutoff))
|
||||||
|
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
|
||||||
|
.Skip(pagingSpec.PagingOffset())
|
||||||
|
.Take(pagingSpec.PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildAirDateUtcCutoffWhereClause(DateTime currentTime)
|
||||||
|
{
|
||||||
|
return string.Format("WHERE datetime(strftime('%s', [t0].[AirDateUtc]) + [t1].[RunTime] * 60, 'unixepoch') <= '{0}'",
|
||||||
|
currentTime.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private string BuildQualityCutoffWhereClause(List<QualitiesBelowCutoff> qualitiesBelowCutoff)
|
||||||
|
{
|
||||||
|
var clauses = new List<string>();
|
||||||
|
|
||||||
|
foreach (var profile in qualitiesBelowCutoff)
|
||||||
|
{
|
||||||
|
foreach (var belowCutoff in profile.QualityIds)
|
||||||
|
{
|
||||||
|
clauses.Add(string.Format("([t1].[ProfileId] = {0} AND [t2].[Quality] LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format("({0})", string.Join(" OR ", clauses));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,9 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using NLog;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
|
using NzbDrone.Core.Music.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -12,12 +17,12 @@ namespace NzbDrone.Core.Music
|
||||||
List<Track> GetTracks(IEnumerable<int> ids);
|
List<Track> GetTracks(IEnumerable<int> ids);
|
||||||
Track FindTrack(string artistId, string albumId, int trackNumber);
|
Track FindTrack(string artistId, string albumId, int trackNumber);
|
||||||
Track FindTrackByTitle(string artistId, string albumId, string releaseTitle);
|
Track FindTrackByTitle(string artistId, string albumId, string releaseTitle);
|
||||||
List<Track> GetTrackByArtist(string artistId);
|
List<Track> GetTracksByArtist(string artistId);
|
||||||
List<Track> GetTracksByAlbum(string artistId, string albumId);
|
//List<Track> GetTracksByAlbum(string artistId, string albumId);
|
||||||
List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle);
|
//List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle);
|
||||||
List<Track> TracksWithFiles(string artistId);
|
List<Track> TracksWithFiles(string artistId);
|
||||||
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
//PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
||||||
List<Track> GeTracksByFileId(int trackFileId);
|
List<Track> GetTracksByFileId(int trackFileId);
|
||||||
void UpdateTrack(Track track);
|
void UpdateTrack(Track track);
|
||||||
void SetTrackMonitored(int trackId, bool monitored);
|
void SetTrackMonitored(int trackId, bool monitored);
|
||||||
void UpdateTracks(List<Track> tracks);
|
void UpdateTracks(List<Track> tracks);
|
||||||
|
@ -29,89 +34,158 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
public class TrackService : ITrackService
|
public class TrackService : ITrackService
|
||||||
{
|
{
|
||||||
public void DeleteMany(List<Track> tracks)
|
private readonly ITrackRepository _trackRepository;
|
||||||
{
|
private readonly IConfigService _configService;
|
||||||
throw new NotImplementedException();
|
private readonly Logger _logger;
|
||||||
}
|
|
||||||
|
|
||||||
public Track FindTrack(string artistId, string albumId, int trackNumber)
|
public TrackService(ITrackRepository trackRepository, IConfigService configService, Logger logger)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
_trackRepository = trackRepository;
|
||||||
}
|
_configService = configService;
|
||||||
|
_logger = logger;
|
||||||
public Track FindTrackByTitle(string artistId, string albumId, string releaseTitle)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Track> GeTracksByFileId(int trackFileId)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Track GetTrack(int id)
|
public Track GetTrack(int id)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _trackRepository.Get(id);
|
||||||
}
|
|
||||||
|
|
||||||
public List<Track> GetTrackByArtist(string artistId)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTracks(IEnumerable<int> ids)
|
public List<Track> GetTracks(IEnumerable<int> ids)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _trackRepository.Get(ids).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Track FindTrack(string artistId, string albumId, int episodeNumber)
|
||||||
|
{
|
||||||
|
return _trackRepository.Find(artistId, albumId, episodeNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Track> GetTracksByArtist(string artistId)
|
||||||
|
{
|
||||||
|
return _trackRepository.GetTracks(artistId).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTracksByAlbum(string artistId, string albumId)
|
public List<Track> GetTracksByAlbum(string artistId, string albumId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _trackRepository.GetTracks(artistId, albumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle)
|
public Track FindTrackByTitle(string artistId, string albumId, string releaseTitle)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
// TODO: can replace this search mechanism with something smarter/faster/better
|
||||||
}
|
var normalizedReleaseTitle = Parser.Parser.NormalizeEpisodeTitle(releaseTitle).Replace(".", " ");
|
||||||
|
var tracks = _trackRepository.GetTracks(artistId, albumId);
|
||||||
|
|
||||||
public void InsertMany(List<Track> tracks)
|
var matches = tracks.Select(
|
||||||
{
|
track => new
|
||||||
throw new NotImplementedException();
|
{
|
||||||
}
|
Position = normalizedReleaseTitle.IndexOf(Parser.Parser.NormalizeEpisodeTitle(track.Title), StringComparison.CurrentCultureIgnoreCase),
|
||||||
|
Length = Parser.Parser.NormalizeEpisodeTitle(track.Title).Length,
|
||||||
|
Track = track
|
||||||
|
})
|
||||||
|
.Where(e => e.Track.Title.Length > 0 && e.Position >= 0)
|
||||||
|
.OrderBy(e => e.Position)
|
||||||
|
.ThenByDescending(e => e.Length)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
public void SetTrackMonitored(int trackId, bool monitored)
|
if (matches.Any())
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return matches.First().Track;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTrackMonitoredByAlbum(string artistId, string albumId, bool monitored)
|
return null;
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> TracksWithFiles(string artistId)
|
public List<Track> TracksWithFiles(string artistId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _trackRepository.TracksWithFiles(artistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec)
|
public PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var episodeResult = _trackRepository.TracksWithoutFiles(pagingSpec);
|
||||||
|
|
||||||
|
return episodeResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateMany(List<Track> tracks)
|
public List<Track> GetTracksByFileId(int trackFileId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _trackRepository.GetTracksByFileId(trackFileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateTrack(Track track)
|
public void UpdateTrack(Track track)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
_trackRepository.Update(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTrackMonitored(int trackId, bool monitored)
|
||||||
|
{
|
||||||
|
var track = _trackRepository.Get(trackId);
|
||||||
|
_trackRepository.SetMonitoredFlat(track, monitored);
|
||||||
|
|
||||||
|
_logger.Debug("Monitored flag for Track:{0} was set to {1}", trackId, monitored);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTrackMonitoredByAlbum(string artistId, string albumId, bool monitored)
|
||||||
|
{
|
||||||
|
_trackRepository.SetMonitoredByAlbum(artistId, albumId, monitored);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateEpisodes(List<Track> tracks)
|
||||||
|
{
|
||||||
|
_trackRepository.UpdateMany(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertMany(List<Track> tracks)
|
||||||
|
{
|
||||||
|
_trackRepository.InsertMany(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateMany(List<Track> tracks)
|
||||||
|
{
|
||||||
|
_trackRepository.UpdateMany(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteMany(List<Track> tracks)
|
||||||
|
{
|
||||||
|
_trackRepository.DeleteMany(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleAsync(ArtistDeletedEvent message)
|
||||||
|
{
|
||||||
|
var tracks = GetTracksByArtist(message.Artist.SpotifyId);
|
||||||
|
_trackRepository.DeleteMany(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(TrackFileDeletedEvent message)
|
||||||
|
{
|
||||||
|
foreach (var track in GetTracksByFileId(message.TrackFile.Id))
|
||||||
|
{
|
||||||
|
_logger.Debug("Detaching track {0} from file.", track.Id);
|
||||||
|
track.TrackFileId = 0;
|
||||||
|
|
||||||
|
if (message.Reason != DeleteMediaFileReason.Upgrade && _configService.AutoUnmonitorPreviouslyDownloadedEpisodes)
|
||||||
|
{
|
||||||
|
track.Monitored = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTrack(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(TrackFileAddedEvent message)
|
||||||
|
{
|
||||||
|
foreach (var track in message.TrackFile.Tracks.Value)
|
||||||
|
{
|
||||||
|
_trackRepository.SetFileId(track.Id, message.TrackFile.Id);
|
||||||
|
_logger.Debug("Linking [{0}] > [{1}]", message.TrackFile.RelativePath, track);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateTracks(List<Track> tracks)
|
public void UpdateTracks(List<Track> tracks)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
_trackRepository.UpdateMany(tracks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -769,6 +769,7 @@
|
||||||
<Compile Include="MediaFiles\Events\SeriesRenamedEvent.cs" />
|
<Compile Include="MediaFiles\Events\SeriesRenamedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\SeriesScanSkippedEvent.cs" />
|
<Compile Include="MediaFiles\Events\SeriesScanSkippedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\SeriesScannedEvent.cs" />
|
<Compile Include="MediaFiles\Events\SeriesScannedEvent.cs" />
|
||||||
|
<Compile Include="MediaFiles\Events\TrackFileAddedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\TrackFileDeletedEvent.cs" />
|
<Compile Include="MediaFiles\Events\TrackFileDeletedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\TrackImportedEvent.cs" />
|
<Compile Include="MediaFiles\Events\TrackImportedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\FileDateType.cs" />
|
<Compile Include="MediaFiles\FileDateType.cs" />
|
||||||
|
@ -868,7 +869,9 @@
|
||||||
<Compile Include="Music\Events\TrackInfoRefreshedEvent.cs" />
|
<Compile Include="Music\Events\TrackInfoRefreshedEvent.cs" />
|
||||||
<Compile Include="Music\RefreshArtistService.cs" />
|
<Compile Include="Music\RefreshArtistService.cs" />
|
||||||
<Compile Include="Music\RefreshTrackService.cs" />
|
<Compile Include="Music\RefreshTrackService.cs" />
|
||||||
|
<Compile Include="Music\ShouldRefreshArtist.cs" />
|
||||||
<Compile Include="Music\Track.cs" />
|
<Compile Include="Music\Track.cs" />
|
||||||
|
<Compile Include="Music\TrackRepository.cs" />
|
||||||
<Compile Include="Music\TrackService.cs" />
|
<Compile Include="Music\TrackService.cs" />
|
||||||
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
||||||
<Compile Include="Notifications\Join\JoinInvalidDeviceException.cs" />
|
<Compile Include="Notifications\Join\JoinInvalidDeviceException.cs" />
|
||||||
|
|
|
@ -9,14 +9,14 @@ module.exports = Marionette.ItemView.extend({
|
||||||
|
|
||||||
events : {
|
events : {
|
||||||
'click .x-edit' : '_editSeries',
|
'click .x-edit' : '_editSeries',
|
||||||
'click .x-refresh' : '_refreshSeries'
|
'click .x-refresh' : '_refreshArtist'
|
||||||
},
|
},
|
||||||
|
|
||||||
onRender : function() {
|
onRender : function() {
|
||||||
CommandController.bindToCommand({
|
CommandController.bindToCommand({
|
||||||
element : this.ui.refresh,
|
element : this.ui.refresh,
|
||||||
command : {
|
command : {
|
||||||
name : 'refreshSeries',
|
name : 'refreshArtist',
|
||||||
seriesId : this.model.get('id')
|
seriesId : this.model.get('id')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -26,9 +26,9 @@ module.exports = Marionette.ItemView.extend({
|
||||||
vent.trigger(vent.Commands.EditSeriesCommand, { series : this.model });
|
vent.trigger(vent.Commands.EditSeriesCommand, { series : this.model });
|
||||||
},
|
},
|
||||||
|
|
||||||
_refreshSeries : function() {
|
_refreshArtist : function() {
|
||||||
CommandController.Execute('refreshSeries', {
|
CommandController.Execute('refreshArtist', {
|
||||||
name : 'refreshSeries',
|
name : 'refreshArtist',
|
||||||
seriesId : this.model.id
|
seriesId : this.model.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue