using System; using System.Collections.Generic; using System.Linq; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Qualities; namespace NzbDrone.Core.Music { public interface IAlbumRepository : IBasicRepository { List GetAlbums(int artistId); List GetLastAlbums(IEnumerable artistMetadataIds); List GetNextAlbums(IEnumerable artistMetadataIds); List GetAlbumsByArtistMetadataId(int artistMetadataId); List GetAlbumsForRefresh(int artistMetadataId, List foreignIds); Album FindByTitle(int artistMetadataId, string title); Album FindById(string foreignAlbumId); PagingSpec AlbumsWithoutFiles(PagingSpec pagingSpec); PagingSpec AlbumsWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff); List AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored); List ArtistAlbumsBetweenDates(Artist artist, DateTime startDate, DateTime endDate, bool includeUnmonitored); void SetMonitoredFlat(Album album, bool monitored); void SetMonitored(IEnumerable ids, bool monitored); Album FindAlbumByRelease(string albumReleaseId); Album FindAlbumByTrack(int trackId); List GetArtistAlbumsWithFiles(Artist artist); } public class AlbumRepository : BasicRepository, IAlbumRepository { public AlbumRepository(IMainDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { } public List GetAlbums(int artistId) { return Query(Builder().Join((l, r) => l.ArtistMetadataId == r.ArtistMetadataId).Where(a => a.Id == artistId)); } public List GetLastAlbums(IEnumerable artistMetadataIds) { var now = DateTime.UtcNow; var inner = Builder() .Select("MIN(\"Albums\".\"Id\") as id, MAX(\"Albums\".\"ReleaseDate\") as date") .Where(x => artistMetadataIds.Contains(x.ArtistMetadataId) && x.ReleaseDate < now) .GroupBy(x => x.ArtistMetadataId) .AddSelectTemplate(typeof(Album)); var outer = Builder() .Join($"({inner.RawSql}) ids on ids.id = \"Albums\".\"Id\" and ids.date = \"Albums\".\"ReleaseDate\"") .AddParameters(inner.Parameters); return Query(outer); } public List GetNextAlbums(IEnumerable artistMetadataIds) { var now = DateTime.UtcNow; var inner = Builder() .Select("MIN(\"Albums\".\"Id\") as id, MIN(\"Albums\".\"ReleaseDate\") as date") .Where(x => artistMetadataIds.Contains(x.ArtistMetadataId) && x.ReleaseDate > now) .GroupBy(x => x.ArtistMetadataId) .AddSelectTemplate(typeof(Album)); var outer = Builder() .Join($"({inner.RawSql}) ids on ids.id = \"Albums\".\"Id\" and ids.date = \"Albums\".\"ReleaseDate\"") .AddParameters(inner.Parameters); return Query(outer); } public List GetAlbumsByArtistMetadataId(int artistMetadataId) { return Query(s => s.ArtistMetadataId == artistMetadataId); } public List GetAlbumsForRefresh(int artistMetadataId, List foreignIds) { return Query(a => a.ArtistMetadataId == artistMetadataId || foreignIds.Contains(a.ForeignAlbumId)); } public Album FindById(string foreignAlbumId) { return Query(s => s.ForeignAlbumId == foreignAlbumId).SingleOrDefault(); } // x.Id == null is converted to SQL, so warning incorrect #pragma warning disable CS0472 private SqlBuilder AlbumsWithoutFilesBuilder(DateTime currentTime) { return Builder() .Join((l, r) => l.ArtistMetadataId == r.ArtistMetadataId) .Join((a, r) => a.Id == r.AlbumId) .Join((r, t) => r.Id == t.AlbumReleaseId) .LeftJoin((t, f) => t.TrackFileId == f.Id) .Where(f => f.Id == null) .Where(r => r.Monitored == true) .Where(a => a.ReleaseDate <= currentTime); } #pragma warning restore CS0472 public PagingSpec AlbumsWithoutFiles(PagingSpec pagingSpec) { var currentTime = DateTime.UtcNow; pagingSpec.Records = GetPagedRecords(AlbumsWithoutFilesBuilder(currentTime), pagingSpec, PagedQuery); pagingSpec.TotalRecords = GetPagedRecordCount(AlbumsWithoutFilesBuilder(currentTime).SelectCountDistinct(x => x.Id), pagingSpec); return pagingSpec; } private SqlBuilder AlbumsWhereCutoffUnmetBuilder(List qualitiesBelowCutoff) { return Builder() .Join((l, r) => l.ArtistMetadataId == r.ArtistMetadataId) .Join((a, r) => a.Id == r.AlbumId) .Join((r, t) => r.Id == t.AlbumReleaseId) .LeftJoin((t, f) => t.TrackFileId == f.Id) .Where(r => r.Monitored == true) .Where(BuildQualityCutoffWhereClause(qualitiesBelowCutoff)); } private string BuildQualityCutoffWhereClause(List qualitiesBelowCutoff) { var clauses = new List(); foreach (var profile in qualitiesBelowCutoff) { foreach (var belowCutoff in profile.QualityIds) { clauses.Add(string.Format("(\"Artists\".\"QualityProfileId\" = {0} AND \"TrackFiles\".\"Quality\" LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff)); } } return string.Format("({0})", string.Join(" OR ", clauses)); } public PagingSpec AlbumsWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff) { pagingSpec.Records = GetPagedRecords(AlbumsWhereCutoffUnmetBuilder(qualitiesBelowCutoff), pagingSpec, PagedQuery); var countTemplate = $"SELECT COUNT(*) FROM (SELECT /**select**/ FROM \"{TableMapping.Mapper.TableNameMapping(typeof(Album))}\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/) AS \"Inner\""; pagingSpec.TotalRecords = GetPagedRecordCount(AlbumsWhereCutoffUnmetBuilder(qualitiesBelowCutoff).Select(typeof(Album)), pagingSpec, countTemplate); return pagingSpec; } public List AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored) { SqlBuilder builder; builder = Builder().Where(rg => rg.ReleaseDate >= startDate && rg.ReleaseDate <= endDate); if (!includeUnmonitored) { builder = builder.Where(e => e.Monitored == true) .Join((l, r) => l.ArtistMetadataId == r.ArtistMetadataId) .Where(e => e.Monitored == true); } return Query(builder); } public List ArtistAlbumsBetweenDates(Artist artist, DateTime startDate, DateTime endDate, bool includeUnmonitored) { SqlBuilder builder; builder = Builder().Where(rg => rg.ReleaseDate >= startDate && rg.ReleaseDate <= endDate && rg.ArtistMetadataId == artist.ArtistMetadataId); if (!includeUnmonitored) { builder = builder.Where(e => e.Monitored == true) .Join((l, r) => l.ArtistMetadataId == r.ArtistMetadataId) .Where(e => e.Monitored == true); } return Query(builder); } public void SetMonitoredFlat(Album album, bool monitored) { album.Monitored = monitored; SetFields(album, p => p.Monitored); } public void SetMonitored(IEnumerable ids, bool monitored) { var albums = ids.Select(x => new Album { Id = x, Monitored = monitored }).ToList(); SetFields(albums, p => p.Monitored); } public Album FindByTitle(int artistMetadataId, string title) { var cleanTitle = Parser.Parser.CleanArtistName(title); if (string.IsNullOrEmpty(cleanTitle)) { cleanTitle = title; } return Query(s => (s.CleanTitle == cleanTitle || s.Title == title) && s.ArtistMetadataId == artistMetadataId) .ExclusiveOrDefault(); } public Album FindAlbumByRelease(string albumReleaseId) { return Query(Builder().Join((a, r) => a.Id == r.AlbumId) .Where(x => x.ForeignReleaseId == albumReleaseId)).FirstOrDefault(); } public Album FindAlbumByTrack(int trackId) { return Query(Builder().Join((a, r) => a.Id == r.AlbumId) .Join((r, t) => r.Id == t.AlbumReleaseId) .Where(x => x.Id == trackId)).FirstOrDefault(); } public List GetArtistAlbumsWithFiles(Artist artist) { var id = artist.ArtistMetadataId; return Query(Builder().Join((a, r) => a.Id == r.AlbumId) .Join((r, t) => r.Id == t.AlbumReleaseId) .Join((t, f) => t.TrackFileId == f.Id) .Where(x => x.ArtistMetadataId == id) .Where(r => r.Monitored == true) .GroupBy(x => x.Id)); } } }