2017-06-18 02:27:01 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
2020-01-03 12:49:24 +00:00
|
|
|
using NLog;
|
|
|
|
using NzbDrone.Common.Extensions;
|
2017-07-02 00:21:39 +00:00
|
|
|
using NzbDrone.Core.Datastore;
|
2020-01-03 12:49:24 +00:00
|
|
|
using NzbDrone.Core.Messaging.Events;
|
|
|
|
using NzbDrone.Core.Music.Events;
|
2018-11-16 17:46:46 +00:00
|
|
|
using NzbDrone.Core.Parser;
|
2017-06-18 02:27:01 +00:00
|
|
|
|
|
|
|
namespace NzbDrone.Core.Music
|
|
|
|
{
|
|
|
|
public interface IAlbumService
|
|
|
|
{
|
2017-11-16 02:24:33 +00:00
|
|
|
Album GetAlbum(int albumId);
|
2017-06-18 02:27:01 +00:00
|
|
|
List<Album> GetAlbums(IEnumerable<int> albumIds);
|
|
|
|
List<Album> GetAlbumsByArtist(int artistId);
|
2019-08-17 07:04:59 +00:00
|
|
|
List<Album> GetNextAlbumsByArtistMetadataId(IEnumerable<int> artistMetadataIds);
|
|
|
|
List<Album> GetLastAlbumsByArtistMetadataId(IEnumerable<int> artistMetadataIds);
|
2018-12-15 00:02:43 +00:00
|
|
|
List<Album> GetAlbumsByArtistMetadataId(int artistMetadataId);
|
2019-03-15 12:10:45 +00:00
|
|
|
List<Album> GetAlbumsForRefresh(int artistMetadataId, IEnumerable<string> foreignIds);
|
2019-03-22 09:33:48 +00:00
|
|
|
Album AddAlbum(Album newAlbum);
|
2019-01-12 16:56:13 +00:00
|
|
|
Album FindById(string foreignId);
|
2019-08-01 22:01:36 +00:00
|
|
|
Album FindByTitle(int artistMetadataId, string title);
|
|
|
|
Album FindByTitleInexact(int artistMetadataId, string title);
|
2019-12-16 21:21:32 +00:00
|
|
|
List<Album> GetCandidates(int artistMetadataId, string title);
|
|
|
|
void DeleteAlbum(int albumId, bool deleteFiles, bool addImportListExclusion = false);
|
2017-06-18 02:27:01 +00:00
|
|
|
List<Album> GetAllAlbums();
|
|
|
|
Album UpdateAlbum(Album album);
|
2017-06-25 13:17:49 +00:00
|
|
|
void SetAlbumMonitored(int albumId, bool monitored);
|
2017-09-04 02:20:56 +00:00
|
|
|
void SetMonitored(IEnumerable<int> ids, bool monitored);
|
2017-07-02 00:21:39 +00:00
|
|
|
PagingSpec<Album> AlbumsWithoutFiles(PagingSpec<Album> pagingSpec);
|
2017-06-28 02:25:51 +00:00
|
|
|
List<Album> AlbumsBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
2018-02-10 04:03:12 +00:00
|
|
|
List<Album> ArtistAlbumsBetweenDates(Artist artist, DateTime start, DateTime end, bool includeUnmonitored);
|
2017-06-18 02:27:01 +00:00
|
|
|
void InsertMany(List<Album> albums);
|
|
|
|
void UpdateMany(List<Album> albums);
|
|
|
|
void DeleteMany(List<Album> albums);
|
2019-12-16 21:21:32 +00:00
|
|
|
void SetAddOptions(IEnumerable<Album> albums);
|
2018-12-15 00:02:43 +00:00
|
|
|
Album FindAlbumByRelease(string albumReleaseId);
|
|
|
|
Album FindAlbumByTrackId(int trackId);
|
2018-04-08 06:25:34 +00:00
|
|
|
List<Album> GetArtistAlbumsWithFiles(Artist artist);
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
|
2017-07-03 18:39:06 +00:00
|
|
|
public class AlbumService : IAlbumService,
|
2019-03-15 12:10:45 +00:00
|
|
|
IHandle<ArtistDeletedEvent>
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
|
|
|
private readonly IAlbumRepository _albumRepository;
|
|
|
|
private readonly IEventAggregator _eventAggregator;
|
|
|
|
private readonly Logger _logger;
|
|
|
|
|
|
|
|
public AlbumService(IAlbumRepository albumRepository,
|
|
|
|
IEventAggregator eventAggregator,
|
|
|
|
Logger logger)
|
|
|
|
{
|
|
|
|
_albumRepository = albumRepository;
|
|
|
|
_eventAggregator = eventAggregator;
|
|
|
|
_logger = logger;
|
|
|
|
}
|
|
|
|
|
2019-03-22 09:33:48 +00:00
|
|
|
public Album AddAlbum(Album newAlbum)
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
|
|
|
_albumRepository.Insert(newAlbum);
|
2019-03-22 09:33:48 +00:00
|
|
|
|
2019-12-16 21:21:32 +00:00
|
|
|
_eventAggregator.PublishEvent(new AlbumAddedEvent(GetAlbum(newAlbum.Id)));
|
2017-06-18 02:27:01 +00:00
|
|
|
|
|
|
|
return newAlbum;
|
|
|
|
}
|
|
|
|
|
2019-12-16 21:21:32 +00:00
|
|
|
public void DeleteAlbum(int albumId, bool deleteFiles, bool addImportListExclusion = false)
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
|
|
|
var album = _albumRepository.Get(albumId);
|
2019-12-16 21:21:32 +00:00
|
|
|
album.Artist.LazyLoad();
|
2017-06-18 02:27:01 +00:00
|
|
|
_albumRepository.Delete(albumId);
|
2019-12-16 21:21:32 +00:00
|
|
|
_eventAggregator.PublishEvent(new AlbumDeletedEvent(album, deleteFiles, addImportListExclusion));
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
|
2019-12-16 21:21:32 +00:00
|
|
|
public Album FindById(string foreignId)
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
2019-12-16 21:21:32 +00:00
|
|
|
return _albumRepository.FindById(foreignId);
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
|
2019-08-01 22:01:36 +00:00
|
|
|
public Album FindByTitle(int artistMetadataId, string title)
|
2017-08-14 02:58:42 +00:00
|
|
|
{
|
2019-08-01 22:01:36 +00:00
|
|
|
return _albumRepository.FindByTitle(artistMetadataId, title);
|
2017-08-14 02:58:42 +00:00
|
|
|
}
|
2017-06-18 02:27:01 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
private List<Tuple<Func<Album, string, double>, string>> AlbumScoringFunctions(string title, string cleanTitle)
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
2020-01-03 12:49:24 +00:00
|
|
|
Func<Func<Album, string, double>, string, Tuple<Func<Album, string, double>, string>> tc = Tuple.Create;
|
|
|
|
var scoringFunctions = new List<Tuple<Func<Album, string, double>, string>>
|
|
|
|
{
|
2018-11-16 17:46:46 +00:00
|
|
|
tc((a, t) => a.CleanTitle.FuzzyMatch(t), cleanTitle),
|
|
|
|
tc((a, t) => a.Title.FuzzyMatch(t), title),
|
|
|
|
tc((a, t) => a.CleanTitle.FuzzyMatch(t), title.RemoveBracketsAndContents().CleanArtistName()),
|
|
|
|
tc((a, t) => a.CleanTitle.FuzzyMatch(t), title.RemoveAfterDash().CleanArtistName()),
|
|
|
|
tc((a, t) => a.CleanTitle.FuzzyMatch(t), title.RemoveBracketsAndContents().RemoveAfterDash().CleanArtistName()),
|
|
|
|
tc((a, t) => t.FuzzyContains(a.CleanTitle), cleanTitle),
|
|
|
|
tc((a, t) => t.FuzzyContains(a.Title), title)
|
|
|
|
};
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
return scoringFunctions;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Album FindByTitleInexact(int artistMetadataId, string title)
|
|
|
|
{
|
|
|
|
var albums = GetAlbumsByArtistMetadataId(artistMetadataId);
|
|
|
|
|
|
|
|
foreach (var func in AlbumScoringFunctions(title, title.CleanArtistName()))
|
2018-11-16 17:46:46 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
var results = FindByStringInexact(albums, func.Item1, func.Item2);
|
|
|
|
if (results.Count == 1)
|
2018-11-16 17:46:46 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
return results[0];
|
2018-11-16 17:46:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
public List<Album> GetCandidates(int artistMetadataId, string title)
|
|
|
|
{
|
|
|
|
var albums = GetAlbumsByArtistMetadataId(artistMetadataId);
|
|
|
|
var output = new List<Album>();
|
2020-01-03 12:49:24 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
foreach (var func in AlbumScoringFunctions(title, title.CleanArtistName()))
|
|
|
|
{
|
|
|
|
output.AddRange(FindByStringInexact(albums, func.Item1, func.Item2));
|
|
|
|
}
|
|
|
|
|
|
|
|
return output.DistinctBy(x => x.Id).ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<Album> FindByStringInexact(List<Album> albums, Func<Album, string, double> scoreFunction, string title)
|
2018-11-16 17:46:46 +00:00
|
|
|
{
|
|
|
|
const double fuzzThreshold = 0.7;
|
|
|
|
const double fuzzGap = 0.4;
|
|
|
|
|
|
|
|
var sortedAlbums = albums.Select(s => new
|
2020-01-03 12:49:24 +00:00
|
|
|
{
|
|
|
|
MatchProb = scoreFunction(s, title),
|
|
|
|
Album = s
|
|
|
|
})
|
2018-11-16 17:46:46 +00:00
|
|
|
.ToList()
|
|
|
|
.OrderByDescending(s => s.MatchProb)
|
|
|
|
.ToList();
|
|
|
|
|
2019-12-16 21:21:32 +00:00
|
|
|
return sortedAlbums.TakeWhile((x, i) => i == 0 || sortedAlbums[i - 1].MatchProb - x.MatchProb < fuzzGap)
|
2019-02-16 14:49:24 +00:00
|
|
|
.TakeWhile((x, i) => x.MatchProb > fuzzThreshold || (i > 0 && sortedAlbums[i - 1].MatchProb > fuzzThreshold))
|
|
|
|
.Select(x => x.Album)
|
|
|
|
.ToList();
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public List<Album> GetAllAlbums()
|
|
|
|
{
|
|
|
|
return _albumRepository.All().ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
public Album GetAlbum(int albumId)
|
|
|
|
{
|
|
|
|
return _albumRepository.Get(albumId);
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<Album> GetAlbums(IEnumerable<int> albumIds)
|
|
|
|
{
|
|
|
|
return _albumRepository.Get(albumIds).ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<Album> GetAlbumsByArtist(int artistId)
|
|
|
|
{
|
|
|
|
return _albumRepository.GetAlbums(artistId).ToList();
|
|
|
|
}
|
|
|
|
|
2019-08-17 07:04:59 +00:00
|
|
|
public List<Album> GetNextAlbumsByArtistMetadataId(IEnumerable<int> artistMetadataIds)
|
|
|
|
{
|
|
|
|
return _albumRepository.GetNextAlbums(artistMetadataIds).ToList();
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<Album> GetLastAlbumsByArtistMetadataId(IEnumerable<int> artistMetadataIds)
|
|
|
|
{
|
|
|
|
return _albumRepository.GetLastAlbums(artistMetadataIds).ToList();
|
|
|
|
}
|
|
|
|
|
2018-12-15 00:02:43 +00:00
|
|
|
public List<Album> GetAlbumsByArtistMetadataId(int artistMetadataId)
|
|
|
|
{
|
|
|
|
return _albumRepository.GetAlbumsByArtistMetadataId(artistMetadataId).ToList();
|
|
|
|
}
|
|
|
|
|
2019-12-16 21:21:32 +00:00
|
|
|
public List<Album> GetAlbumsForRefresh(int artistMetadataId, IEnumerable<string> foreignIds)
|
2019-03-15 12:10:45 +00:00
|
|
|
{
|
2019-12-16 21:21:32 +00:00
|
|
|
return _albumRepository.GetAlbumsForRefresh(artistMetadataId, foreignIds);
|
2019-03-15 12:10:45 +00:00
|
|
|
}
|
|
|
|
|
2018-12-15 00:02:43 +00:00
|
|
|
public Album FindAlbumByRelease(string albumReleaseId)
|
|
|
|
{
|
|
|
|
return _albumRepository.FindAlbumByRelease(albumReleaseId);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Album FindAlbumByTrackId(int trackId)
|
2018-04-07 04:52:28 +00:00
|
|
|
{
|
2018-12-15 00:02:43 +00:00
|
|
|
return _albumRepository.FindAlbumByTrack(trackId);
|
2018-04-07 04:52:28 +00:00
|
|
|
}
|
|
|
|
|
2019-12-16 21:21:32 +00:00
|
|
|
public void SetAddOptions(IEnumerable<Album> albums)
|
2017-06-18 02:27:01 +00:00
|
|
|
{
|
2019-12-16 21:21:32 +00:00
|
|
|
_albumRepository.SetFields(albums, s => s.AddOptions);
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
|
2017-07-02 00:21:39 +00:00
|
|
|
public PagingSpec<Album> AlbumsWithoutFiles(PagingSpec<Album> pagingSpec)
|
|
|
|
{
|
|
|
|
var albumResult = _albumRepository.AlbumsWithoutFiles(pagingSpec);
|
|
|
|
|
|
|
|
return albumResult;
|
|
|
|
}
|
|
|
|
|
2017-06-28 02:25:51 +00:00
|
|
|
public List<Album> AlbumsBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
|
|
|
|
{
|
|
|
|
var albums = _albumRepository.AlbumsBetweenDates(start.ToUniversalTime(), end.ToUniversalTime(), includeUnmonitored);
|
|
|
|
|
|
|
|
return albums;
|
|
|
|
}
|
|
|
|
|
2018-02-10 04:03:12 +00:00
|
|
|
public List<Album> ArtistAlbumsBetweenDates(Artist artist, DateTime start, DateTime end, bool includeUnmonitored)
|
|
|
|
{
|
|
|
|
var albums = _albumRepository.ArtistAlbumsBetweenDates(artist, start.ToUniversalTime(), end.ToUniversalTime(), includeUnmonitored);
|
|
|
|
|
|
|
|
return albums;
|
|
|
|
}
|
|
|
|
|
2018-04-08 06:25:34 +00:00
|
|
|
public List<Album> GetArtistAlbumsWithFiles(Artist artist)
|
|
|
|
{
|
|
|
|
return _albumRepository.GetArtistAlbumsWithFiles(artist);
|
|
|
|
}
|
|
|
|
|
2017-06-18 02:27:01 +00:00
|
|
|
public void InsertMany(List<Album> albums)
|
|
|
|
{
|
|
|
|
_albumRepository.InsertMany(albums);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void UpdateMany(List<Album> albums)
|
|
|
|
{
|
|
|
|
_albumRepository.UpdateMany(albums);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void DeleteMany(List<Album> albums)
|
|
|
|
{
|
|
|
|
_albumRepository.DeleteMany(albums);
|
2018-01-18 02:28:47 +00:00
|
|
|
|
|
|
|
foreach (var album in albums)
|
|
|
|
{
|
2019-12-16 21:21:32 +00:00
|
|
|
_eventAggregator.PublishEvent(new AlbumDeletedEvent(album, false, false));
|
2018-01-18 02:28:47 +00:00
|
|
|
}
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public Album UpdateAlbum(Album album)
|
|
|
|
{
|
2018-01-18 02:28:47 +00:00
|
|
|
var storedAlbum = GetAlbum(album.Id);
|
2017-06-18 02:27:01 +00:00
|
|
|
var updatedAlbum = _albumRepository.Update(album);
|
2018-12-15 00:02:43 +00:00
|
|
|
|
|
|
|
// If updatedAlbum has populated the Releases, populate in the storedAlbum too
|
|
|
|
if (updatedAlbum.AlbumReleases.IsLoaded)
|
|
|
|
{
|
|
|
|
storedAlbum.AlbumReleases.LazyLoad();
|
|
|
|
}
|
2020-01-03 12:49:24 +00:00
|
|
|
|
2017-06-18 02:27:01 +00:00
|
|
|
_eventAggregator.PublishEvent(new AlbumEditedEvent(updatedAlbum, storedAlbum));
|
|
|
|
|
|
|
|
return updatedAlbum;
|
|
|
|
}
|
|
|
|
|
2017-06-25 13:17:49 +00:00
|
|
|
public void SetAlbumMonitored(int albumId, bool monitored)
|
|
|
|
{
|
|
|
|
var album = _albumRepository.Get(albumId);
|
|
|
|
_albumRepository.SetMonitoredFlat(album, monitored);
|
|
|
|
|
2019-03-15 21:48:50 +00:00
|
|
|
// publish album edited event so artist stats update
|
|
|
|
_eventAggregator.PublishEvent(new AlbumEditedEvent(album, album));
|
|
|
|
|
2017-06-25 13:17:49 +00:00
|
|
|
_logger.Debug("Monitored flag for Album:{0} was set to {1}", albumId, monitored);
|
|
|
|
}
|
|
|
|
|
2017-09-04 02:20:56 +00:00
|
|
|
public void SetMonitored(IEnumerable<int> ids, bool monitored)
|
|
|
|
{
|
|
|
|
_albumRepository.SetMonitored(ids, monitored);
|
2019-03-15 21:48:50 +00:00
|
|
|
|
|
|
|
// publish album edited event so artist stats update
|
|
|
|
foreach (var album in _albumRepository.Get(ids))
|
|
|
|
{
|
|
|
|
_eventAggregator.PublishEvent(new AlbumEditedEvent(album, album));
|
|
|
|
}
|
2017-09-04 02:20:56 +00:00
|
|
|
}
|
|
|
|
|
2019-03-15 12:10:45 +00:00
|
|
|
public void Handle(ArtistDeletedEvent message)
|
2017-07-03 18:39:06 +00:00
|
|
|
{
|
2018-12-15 00:02:43 +00:00
|
|
|
var albums = GetAlbumsByArtistMetadataId(message.Artist.ArtistMetadataId);
|
|
|
|
DeleteMany(albums);
|
2017-07-03 18:39:06 +00:00
|
|
|
}
|
2017-06-18 02:27:01 +00:00
|
|
|
}
|
|
|
|
}
|