From fbb6691ea155765be299f7b17b3b616367d48ab5 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Sun, 7 May 2017 17:50:07 -0500 Subject: [PATCH 1/2] Implemented Tracks and ability to save to the DB. Updated SkyHook to support ArtistSlug. --- .../Datastore/Migration/111_setup_music.cs | 8 +- src/NzbDrone.Core/Datastore/TableMapping.cs | 3 +- src/NzbDrone.Core/Jobs/TaskManager.cs | 2 +- .../MediaFiles/Events/TrackFileAddedEvent.cs | 18 ++ .../MetadataSource/SkyHook/SkyHookProxy.cs | 23 +-- .../Music/RefreshArtistService.cs | 10 +- .../Music/RefreshTrackService.cs | 23 ++- .../Music/ShouldRefreshArtist.cs | 43 +++++ src/NzbDrone.Core/Music/Track.cs | 9 +- src/NzbDrone.Core/Music/TrackRepository.cs | 165 ++++++++++++++++++ src/NzbDrone.Core/Music/TrackService.cs | 146 ++++++++++++---- src/NzbDrone.Core/NzbDrone.Core.csproj | 3 + src/UI/Series/Index/SeriesIndexItemView.js | 10 +- 13 files changed, 390 insertions(+), 73 deletions(-) create mode 100644 src/NzbDrone.Core/MediaFiles/Events/TrackFileAddedEvent.cs create mode 100644 src/NzbDrone.Core/Music/ShouldRefreshArtist.cs create mode 100644 src/NzbDrone.Core/Music/TrackRepository.cs diff --git a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs index 0e2da7d1b..12b0c2d0b 100644 --- a/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs +++ b/src/NzbDrone.Core/Datastore/Migration/111_setup_music.cs @@ -48,16 +48,16 @@ namespace NzbDrone.Core.Datastore.Migration .WithColumn("Overview").AsString(); 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("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("Title").AsString().Nullable() .WithColumn("Ignored").AsBoolean().Nullable() .WithColumn("Explict").AsBoolean() .WithColumn("Monitored").AsBoolean() - .WithColumn("TrackExplicitName").AsString().Nullable() - .WithColumn("TrackCensoredName").AsString().Nullable() .WithColumn("TrackFileId").AsInt32().Nullable() .WithColumn("ReleaseDate").AsDateTime().Nullable(); diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 09163a47c..9b9905adc 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -102,7 +102,7 @@ namespace NzbDrone.Core.Datastore .Relationships.AutoMapICollectionOrComplexProperties() .For("Tracks") .LazyLoad(condition: parent => parent.Id > 0, - query: (db, parent) => db.Query().Where(c => c.SpotifyTrackId == parent.Id).ToList()) + query: (db, parent) => db.Query().Where(c => c.ArtistId == parent.Id).ToList()) // TODO: Figure what the hell to do here .HasOne(file => file.Artist, file => file.AlbumId); Mapper.Entity().RegisterModel("Tracks") @@ -110,6 +110,7 @@ namespace NzbDrone.Core.Datastore .Ignore(e => e.Album) .Ignore(e => e.HasFile) .Relationship() + // TODO: Need to implement ArtistId to Artist.Id here .HasOne(track => track.TrackFile, track => track.TrackFileId); // TODO: Check lazy load for artists Mapper.Entity().RegisterModel("QualityDefinitions") diff --git a/src/NzbDrone.Core/Jobs/TaskManager.cs b/src/NzbDrone.Core/Jobs/TaskManager.cs index ef974f612..d791fa136 100644 --- a/src/NzbDrone.Core/Jobs/TaskManager.cs +++ b/src/NzbDrone.Core/Jobs/TaskManager.cs @@ -68,7 +68,7 @@ namespace NzbDrone.Core.Jobs //new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).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(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 = 7*24*60, TypeName = typeof(BackupCommand).FullName}, diff --git a/src/NzbDrone.Core/MediaFiles/Events/TrackFileAddedEvent.cs b/src/NzbDrone.Core/MediaFiles/Events/TrackFileAddedEvent.cs new file mode 100644 index 000000000..d8333833b --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/Events/TrackFileAddedEvent.cs @@ -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; + } + } +} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index bf47348b1..5a76a4c8e 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -115,15 +115,15 @@ namespace NzbDrone.Core.MetadataSource.SkyHook artist.SpotifyId = httpResponse.Resource.Id; artist.Genres = httpResponse.Resource.Genres; + var albumRet = MapAlbums(artist); + artist = albumRet.Item1; + - artist = MapAlbums(artist); - - // TODO: implement tracks api call - return new Tuple>(artist, new List()); + return new Tuple>(albumRet.Item1, albumRet.Item2); } - private Artist MapAlbums(Artist artist) + private Tuple> MapAlbums(Artist artist) { // 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); } + List masterTracks = new List(); List albums = new List(); foreach(var albumResource in httpResponse.Resource.Items) { Album album = new Album(); album.AlbumId = albumResource.Id; album.Title = albumResource.Name; - album.ArtworkUrl = albumResource.Images[0].Url; + album.ArtworkUrl = albumResource.Images.Count > 0 ? albumResource.Images[0].Url : ""; album.Tracks = MapTracksToAlbum(album); + masterTracks.InsertRange(masterTracks.Count, album.Tracks); albums.Add(album); } // TODO: We now need to get all tracks for each album artist.Albums = albums; - return artist; + return new Tuple>(artist, masterTracks); } private List MapTracksToAlbum(Album album) @@ -183,11 +185,12 @@ namespace NzbDrone.Core.MetadataSource.SkyHook // TODO: Implement more track mapping //track.Artist = trackResource.Artists //track.ArtistId = album. + track.SpotifyTrackId = trackResource.Id; + track.ArtistSpotifyId = trackResource.Artists.Count > 0 ? trackResource.Artists[0].Id : null; track.Explict = trackResource.Explicit; track.Compilation = trackResource.Artists.Count > 1; track.TrackNumber = trackResource.TrackNumber; - track.TrackExplicitName = trackResource.Name; - track.TrackCensoredName = trackResource.Name; + track.Title = trackResource.Name; tracks.Add(track); } @@ -272,7 +275,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook artist.ArtistName = artistResource.Name; artist.SpotifyId = artistResource.Id; artist.Genres = artistResource.Genres; - //artist.ArtistSlug = a//TODO implement artistSlug mapping; + artist.ArtistSlug = Parser.Parser.CleanArtistTitle(artist.ArtistName); artists.Add(artist); } diff --git a/src/NzbDrone.Core/Music/RefreshArtistService.cs b/src/NzbDrone.Core/Music/RefreshArtistService.cs index af0717437..314597c63 100644 --- a/src/NzbDrone.Core/Music/RefreshArtistService.cs +++ b/src/NzbDrone.Core/Music/RefreshArtistService.cs @@ -22,9 +22,8 @@ namespace NzbDrone.Core.Music private readonly IArtistService _artistService; private readonly IRefreshTrackService _refreshTrackService; private readonly IEventAggregator _eventAggregator; - //private readonly IDailySeriesService _dailySeriesService; private readonly IDiskScanService _diskScanService; - //private readonly ICheckIfArtistShouldBeRefreshed _checkIfArtistShouldBeRefreshed; + private readonly ICheckIfArtistShouldBeRefreshed _checkIfArtistShouldBeRefreshed; private readonly Logger _logger; public RefreshArtistService(IProvideArtistInfo artistInfo, @@ -32,7 +31,7 @@ namespace NzbDrone.Core.Music IRefreshTrackService refreshTrackService, IEventAggregator eventAggregator, IDiskScanService diskScanService, - //ICheckIfArtistShouldBeRefreshed checkIfArtistShouldBeRefreshed, + ICheckIfArtistShouldBeRefreshed checkIfArtistShouldBeRefreshed, Logger logger) { _artistInfo = artistInfo; @@ -40,7 +39,7 @@ namespace NzbDrone.Core.Music _refreshTrackService = refreshTrackService; _eventAggregator = eventAggregator; _diskScanService = diskScanService; - //_checkIfArtistShouldBeRefreshed = checkIfArtistShouldBeRefreshed; + _checkIfArtistShouldBeRefreshed = checkIfArtistShouldBeRefreshed; _logger = logger; } @@ -75,7 +74,6 @@ namespace NzbDrone.Core.Music artist.CleanTitle = artistInfo.CleanTitle; artist.LastInfoSync = DateTime.UtcNow; artist.Images = artistInfo.Images; - //artist.Actors = artistInfo.Actors; artist.Genres = artistInfo.Genres; try @@ -142,7 +140,7 @@ namespace NzbDrone.Core.Music foreach (var artist in allArtists) { - if (message.Trigger == CommandTrigger.Manual /*|| _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)*/) + if (message.Trigger == CommandTrigger.Manual || _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)) { try { diff --git a/src/NzbDrone.Core/Music/RefreshTrackService.cs b/src/NzbDrone.Core/Music/RefreshTrackService.cs index bee0e8a5d..169582e3d 100644 --- a/src/NzbDrone.Core/Music/RefreshTrackService.cs +++ b/src/NzbDrone.Core/Music/RefreshTrackService.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Music var successCount = 0; var failCount = 0; - var existingTracks = _trackService.GetTrackByArtist(artist.SpotifyId); + var existingTracks = _trackService.GetTracksByArtist(artist.SpotifyId); var albums = artist.Albums; var updateList = new List(); @@ -57,13 +57,26 @@ namespace NzbDrone.Core.Music trackToUpdate.Monitored = GetMonitoredStatus(track, albums); newList.Add(trackToUpdate); } - trackToUpdate.ArtistId = artist.SpotifyId; // TODO: Ensure LazyLoaded field gets updated. + + trackToUpdate.SpotifyTrackId = track.SpotifyTrackId; trackToUpdate.TrackNumber = track.TrackNumber; 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 - - + + successCount++; } diff --git a/src/NzbDrone.Core/Music/ShouldRefreshArtist.cs b/src/NzbDrone.Core/Music/ShouldRefreshArtist.cs new file mode 100644 index 000000000..669a1db3e --- /dev/null +++ b/src/NzbDrone.Core/Music/ShouldRefreshArtist.cs @@ -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; + } + } +} diff --git a/src/NzbDrone.Core/Music/Track.cs b/src/NzbDrone.Core/Music/Track.cs index f1f563306..77fcc4e41 100644 --- a/src/NzbDrone.Core/Music/Track.cs +++ b/src/NzbDrone.Core/Music/Track.cs @@ -17,18 +17,17 @@ namespace NzbDrone.Core.Music 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 LazyLoaded Artist { get; set; } - public string ArtistId { get; set; } - public int CompilationId { get; set; } + public string ArtistSpotifyId { 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 int TrackNumber { get; set; } public string Title { get; set; } public bool Ignored { get; set; } public bool Explict { get; set; } - public string TrackExplicitName { get; set; } - public string TrackCensoredName { get; set; } public bool Monitored { get; set; } public int TrackFileId { get; set; } public DateTime? ReleaseDate { get; set; } diff --git a/src/NzbDrone.Core/Music/TrackRepository.cs b/src/NzbDrone.Core/Music/TrackRepository.cs new file mode 100644 index 000000000..354d09afc --- /dev/null +++ b/src/NzbDrone.Core/Music/TrackRepository.cs @@ -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 Find(string artistId, string albumId, int trackNumber); + List GetTracks(string artistId); + List GetTracks(string artistId, string albumId); + List GetTracksByFileId(int fileId); + List TracksWithFiles(string artistId); + PagingSpec TracksWithoutFiles(PagingSpec pagingSpec); + PagingSpec TracksWhereCutoffUnmet(PagingSpec pagingSpec, List 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, 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 GetTracks(string artistId) + { + return Query.Where(s => s.ArtistSpotifyId == artistId).ToList(); + } + + public List GetTracks(string artistId, string albumId) + { + return Query.Where(s => s.ArtistSpotifyId == artistId) + .AndWhere(s => s.AlbumId == albumId) + .ToList(); + } + + public List GetTracksByFileId(int fileId) + { + return Query.Where(e => e.TrackFileId == fileId).ToList(); + } + + public List TracksWithFiles(string artistId) + { + return Query.Join(JoinType.Inner, e => e.TrackFile, (e, ef) => e.TrackFileId == ef.Id) + .Where(e => e.ArtistSpotifyId == artistId); + } + + public PagingSpec TracksWhereCutoffUnmet(PagingSpec pagingSpec, List 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 TracksWithoutFiles(PagingSpec pagingSpec) + { + var currentTime = DateTime.UtcNow; + + pagingSpec.TotalRecords = GetMissingEpisodesQuery(pagingSpec, currentTime).GetRowCount(); + pagingSpec.Records = GetMissingEpisodesQuery(pagingSpec, currentTime).ToList(); + + return pagingSpec; + } + + private SortBuilder GetMissingEpisodesQuery(PagingSpec pagingSpec, DateTime currentTime) + { + return Query.Join(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 EpisodesWhereCutoffUnmetQuery(PagingSpec pagingSpec, List qualitiesBelowCutoff) + { + return Query.Join(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistSpotifyId == s.SpotifyId) + .Join(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) + { + var clauses = new List(); + + 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)); + } + } +} diff --git a/src/NzbDrone.Core/Music/TrackService.cs b/src/NzbDrone.Core/Music/TrackService.cs index b8bbbafa7..cf09139e5 100644 --- a/src/NzbDrone.Core/Music/TrackService.cs +++ b/src/NzbDrone.Core/Music/TrackService.cs @@ -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.Collections.Generic; using System.Linq; @@ -12,12 +17,12 @@ namespace NzbDrone.Core.Music List GetTracks(IEnumerable ids); Track FindTrack(string artistId, string albumId, int trackNumber); Track FindTrackByTitle(string artistId, string albumId, string releaseTitle); - List GetTrackByArtist(string artistId); - List GetTracksByAlbum(string artistId, string albumId); - List GetTracksByAlbumTitle(string artistId, string albumTitle); + List GetTracksByArtist(string artistId); + //List GetTracksByAlbum(string artistId, string albumId); + //List GetTracksByAlbumTitle(string artistId, string albumTitle); List TracksWithFiles(string artistId); - PagingSpec TracksWithoutFiles(PagingSpec pagingSpec); - List GeTracksByFileId(int trackFileId); + //PagingSpec TracksWithoutFiles(PagingSpec pagingSpec); + List GetTracksByFileId(int trackFileId); void UpdateTrack(Track track); void SetTrackMonitored(int trackId, bool monitored); void UpdateTracks(List tracks); @@ -29,89 +34,158 @@ namespace NzbDrone.Core.Music public class TrackService : ITrackService { - public void DeleteMany(List tracks) + private readonly ITrackRepository _trackRepository; + private readonly IConfigService _configService; + private readonly Logger _logger; + + public TrackService(ITrackRepository trackRepository, IConfigService configService, Logger logger) { - throw new NotImplementedException(); + _trackRepository = trackRepository; + _configService = configService; + _logger = logger; } - public Track FindTrack(string artistId, string albumId, int trackNumber) + public Track GetTrack(int id) { - throw new NotImplementedException(); + return _trackRepository.Get(id); } - public Track FindTrackByTitle(string artistId, string albumId, string releaseTitle) + public List GetTracks(IEnumerable ids) { - throw new NotImplementedException(); + return _trackRepository.Get(ids).ToList(); } - public List GeTracksByFileId(int trackFileId) + public Track FindTrack(string artistId, string albumId, int episodeNumber) { - throw new NotImplementedException(); + return _trackRepository.Find(artistId, albumId, episodeNumber); } - public Track GetTrack(int id) + public List GetTracksByArtist(string artistId) { - throw new NotImplementedException(); + return _trackRepository.GetTracks(artistId).ToList(); } - public List GetTrackByArtist(string artistId) + public List GetTracksByAlbum(string artistId, string albumId) { - throw new NotImplementedException(); + return _trackRepository.GetTracks(artistId, albumId); } - public List GetTracks(IEnumerable ids) + 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); + + var matches = tracks.Select( + track => new + { + 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(); + + if (matches.Any()) + { + return matches.First().Track; + } + + return null; } - public List GetTracksByAlbum(string artistId, string albumId) + public List TracksWithFiles(string artistId) { - throw new NotImplementedException(); + return _trackRepository.TracksWithFiles(artistId); } - public List GetTracksByAlbumTitle(string artistId, string albumTitle) + + public PagingSpec TracksWithoutFiles(PagingSpec pagingSpec) { - throw new NotImplementedException(); + var episodeResult = _trackRepository.TracksWithoutFiles(pagingSpec); + + return episodeResult; } - public void InsertMany(List tracks) + public List GetTracksByFileId(int trackFileId) { - throw new NotImplementedException(); + return _trackRepository.GetTracksByFileId(trackFileId); + } + + public void UpdateTrack(Track track) + { + _trackRepository.Update(track); } public void SetTrackMonitored(int trackId, bool monitored) { - throw new NotImplementedException(); + 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) { - throw new NotImplementedException(); + _trackRepository.SetMonitoredByAlbum(artistId, albumId, monitored); } - public List TracksWithFiles(string artistId) + public void UpdateEpisodes(List tracks) { - throw new NotImplementedException(); + _trackRepository.UpdateMany(tracks); } - public PagingSpec TracksWithoutFiles(PagingSpec pagingSpec) + public void InsertMany(List tracks) { - throw new NotImplementedException(); + _trackRepository.InsertMany(tracks); } public void UpdateMany(List tracks) { - throw new NotImplementedException(); + _trackRepository.UpdateMany(tracks); } - public void UpdateTrack(Track track) + public void DeleteMany(List 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) { - throw new NotImplementedException(); + 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 tracks) { - throw new NotImplementedException(); + _trackRepository.UpdateMany(tracks); } } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index d263b8013..65494030b 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -769,6 +769,7 @@ + @@ -868,7 +869,9 @@ + + diff --git a/src/UI/Series/Index/SeriesIndexItemView.js b/src/UI/Series/Index/SeriesIndexItemView.js index 427fe489e..91d3b329b 100644 --- a/src/UI/Series/Index/SeriesIndexItemView.js +++ b/src/UI/Series/Index/SeriesIndexItemView.js @@ -9,14 +9,14 @@ module.exports = Marionette.ItemView.extend({ events : { 'click .x-edit' : '_editSeries', - 'click .x-refresh' : '_refreshSeries' + 'click .x-refresh' : '_refreshArtist' }, onRender : function() { CommandController.bindToCommand({ element : this.ui.refresh, command : { - name : 'refreshSeries', + name : 'refreshArtist', seriesId : this.model.get('id') } }); @@ -26,9 +26,9 @@ module.exports = Marionette.ItemView.extend({ vent.trigger(vent.Commands.EditSeriesCommand, { series : this.model }); }, - _refreshSeries : function() { - CommandController.Execute('refreshSeries', { - name : 'refreshSeries', + _refreshArtist : function() { + CommandController.Execute('refreshArtist', { + name : 'refreshArtist', seriesId : this.model.id }); } From d1eb9ff16cb2b67624b89aa37ffe522bfc4af5c3 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Mon, 8 May 2017 07:43:23 -0500 Subject: [PATCH 2/2] Changed AristId to a long --- src/NzbDrone.Core/Music/Track.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/NzbDrone.Core/Music/Track.cs b/src/NzbDrone.Core/Music/Track.cs index 77fcc4e41..599328c58 100644 --- a/src/NzbDrone.Core/Music/Track.cs +++ b/src/NzbDrone.Core/Music/Track.cs @@ -21,7 +21,7 @@ namespace NzbDrone.Core.Music public string AlbumId { get; set; } public LazyLoaded Artist { get; set; } public string ArtistSpotifyId { get; set; } - public int ArtistId { get; set; } // This is the DB Id of the Artist, not the SpotifyId + public long 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 int TrackNumber { get; set; } @@ -31,11 +31,6 @@ namespace NzbDrone.Core.Music public bool Monitored { get; set; } public int TrackFileId { get; set; } public DateTime? ReleaseDate { get; set; } - /* - public Ratings Ratings { get; set; } // This might be aplicable as can be pulled from IDv3 tags - public List Images { get; set; }*/ - - //public string SeriesTitle { get; private set; } public LazyLoaded TrackFile { get; set; }