diff --git a/frontend/src/Store/Reducers/wantedReducers.js b/frontend/src/Store/Reducers/wantedReducers.js
index 1c00963fc..c126901c2 100644
--- a/frontend/src/Store/Reducers/wantedReducers.js
+++ b/frontend/src/Store/Reducers/wantedReducers.js
@@ -76,19 +76,19 @@ export const defaultState = {
isSortable: true,
isVisible: true
},
+ // {
+ // name: 'episode',
+ // label: 'Episode',
+ // isVisible: true
+ // },
{
- name: 'episode',
- label: 'Episode',
- isVisible: true
- },
- {
- name: 'episodeTitle',
- label: 'Episode Title',
+ name: 'albumTitle',
+ label: 'Album Title',
isVisible: true
},
{
- name: 'airDateUtc',
- label: 'Air Date',
+ name: 'releaseDate',
+ label: 'Release Date',
isSortable: true,
isVisible: true
},
@@ -97,11 +97,11 @@ export const defaultState = {
label: 'Language',
isVisible: false
},
- {
- name: 'status',
- label: 'Status',
- isVisible: true
- },
+ // {
+ // name: 'status',
+ // label: 'Status',
+ // isVisible: true
+ // },
{
name: 'actions',
columnLabel: 'Actions',
diff --git a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js
index 89cfc5b49..e7f0f9172 100644
--- a/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js
+++ b/frontend/src/Wanted/CutoffUnmet/CutoffUnmetRow.js
@@ -3,7 +3,6 @@ import React from 'react';
import episodeEntities from 'Episode/episodeEntities';
import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
import EpisodeStatusConnector from 'Episode/EpisodeStatusConnector';
-import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
import EpisodeSearchCellConnector from 'Episode/EpisodeSearchCellConnector';
import TrackFileLanguageConnector from 'TrackFile/TrackFileLanguageConnector';
import ArtistNameLink from 'Artist/ArtistNameLink';
@@ -18,13 +17,7 @@ function CutoffUnmetRow(props) {
id,
trackFileId,
artist,
- seasonNumber,
- episodeNumber,
- absoluteEpisodeNumber,
- sceneSeasonNumber,
- sceneEpisodeNumber,
- sceneAbsoluteEpisodeNumber,
- airDateUtc,
+ releaseDate,
title,
isSelected,
columns,
@@ -54,33 +47,14 @@ function CutoffUnmetRow(props) {
return (
);
}
- if (name === 'episode') {
- return (
-
-
-
- );
- }
-
- if (name === 'episodeTitle') {
+ if (name === 'albumTitle') {
return (
);
}
@@ -155,13 +129,7 @@ CutoffUnmetRow.propTypes = {
id: PropTypes.number.isRequired,
trackFileId: PropTypes.number,
artist: PropTypes.object.isRequired,
- seasonNumber: PropTypes.number.isRequired,
- episodeNumber: PropTypes.number.isRequired,
- absoluteEpisodeNumber: PropTypes.number,
- sceneSeasonNumber: PropTypes.number,
- sceneEpisodeNumber: PropTypes.number,
- sceneAbsoluteEpisodeNumber: PropTypes.number,
- airDateUtc: PropTypes.string.isRequired,
+ releaseDate: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
isSelected: PropTypes.bool,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
diff --git a/src/Lidarr.Api.V3/Wanted/CutoffModule.cs b/src/Lidarr.Api.V3/Wanted/CutoffModule.cs
index 70fc721ba..e104269fe 100644
--- a/src/Lidarr.Api.V3/Wanted/CutoffModule.cs
+++ b/src/Lidarr.Api.V3/Wanted/CutoffModule.cs
@@ -1,7 +1,6 @@
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Music;
-using NzbDrone.Core.Tv; //TODO Remove after EpisodeCutoffService is Refactored
using NzbDrone.Core.ArtistStats;
using NzbDrone.SignalR;
using Lidarr.Api.V3.Albums;
@@ -12,9 +11,9 @@ namespace Lidarr.Api.V3.Wanted
{
public class CutoffModule : AlbumModuleWithSignalR
{
- private readonly IEpisodeCutoffService _episodeCutoffService;
+ private readonly IAlbumCutoffService _albumCutoffService;
- public CutoffModule(IEpisodeCutoffService episodeCutoffService,
+ public CutoffModule(IAlbumCutoffService albumCutoffService,
IAlbumService albumService,
IArtistStatisticsService artistStatisticsService,
IArtistService artistService,
@@ -22,7 +21,7 @@ namespace Lidarr.Api.V3.Wanted
IBroadcastSignalRMessage signalRBroadcaster)
: base(albumService, artistStatisticsService, artistService, upgradableSpecification, signalRBroadcaster, "wanted/cutoff")
{
- _episodeCutoffService = episodeCutoffService;
+ _albumCutoffService = albumCutoffService;
GetResourcePaged = GetCutoffUnmetAlbums;
}
@@ -37,7 +36,6 @@ namespace Lidarr.Api.V3.Wanted
};
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
- var includeTrackFile = Request.GetBooleanQueryParameter("includeTrackFile");
if (pagingResource.FilterKey == "monitored" && pagingResource.FilterValue == "false")
{
@@ -48,9 +46,9 @@ namespace Lidarr.Api.V3.Wanted
pagingSpec.FilterExpression = v => v.Monitored == true && v.Artist.Monitored == true;
}
- //var resource = ApplyToPage(_episodeCutoffService.EpisodesWhereCutoffUnmet, pagingSpec, v => MapToResource(v, includeSeries, includeEpisodeFile));
- return null;
- //return resource;
+ var resource = ApplyToPage(_albumCutoffService.AlbumsWhereCutoffUnmet, pagingSpec, v => MapToResource(v, includeArtist));
+
+ return resource;
}
}
}
diff --git a/src/NzbDrone.Core/Music/AlbumCutoffService.cs b/src/NzbDrone.Core/Music/AlbumCutoffService.cs
new file mode 100644
index 000000000..58eb2cf05
--- /dev/null
+++ b/src/NzbDrone.Core/Music/AlbumCutoffService.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using System.Linq;
+using NLog;
+using NzbDrone.Core.Datastore;
+using NzbDrone.Core.Languages;
+using NzbDrone.Core.Profiles.Qualities;
+using NzbDrone.Core.Profiles.Languages;
+using NzbDrone.Core.Qualities;
+
+namespace NzbDrone.Core.Music
+{
+ public interface IAlbumCutoffService
+ {
+ PagingSpec AlbumsWhereCutoffUnmet(PagingSpec pagingSpec);
+ }
+
+ public class AlbumCutoffService : IAlbumCutoffService
+ {
+ private readonly IAlbumRepository _albumRepository;
+ private readonly IProfileService _profileService;
+ private readonly ILanguageProfileService _languageProfileService;
+ private readonly Logger _logger;
+
+ public AlbumCutoffService(IAlbumRepository albumRepository, IProfileService profileService, ILanguageProfileService languageProfileService, Logger logger)
+ {
+ _albumRepository = albumRepository;
+ _profileService = profileService;
+ _languageProfileService = languageProfileService;
+ _logger = logger;
+ }
+
+ public PagingSpec AlbumsWhereCutoffUnmet(PagingSpec pagingSpec)
+ {
+ var qualitiesBelowCutoff = new List();
+ var languagesBelowCutoff = new List();
+ var profiles = _profileService.All();
+ var languageProfiles = _languageProfileService.All();
+
+ //Get all items less than the cutoff
+ foreach (var profile in profiles)
+ {
+ var cutoffIndex = profile.Items.FindIndex(v => v.Quality == profile.Cutoff);
+ var belowCutoff = profile.Items.Take(cutoffIndex).ToList();
+
+ if (belowCutoff.Any())
+ {
+ qualitiesBelowCutoff.Add(new QualitiesBelowCutoff(profile.Id, belowCutoff.Select(i => i.Quality.Id)));
+ }
+ }
+
+ foreach (var profile in languageProfiles)
+ {
+ var languageCutoffIndex = profile.Languages.FindIndex(v => v.Language == profile.Cutoff);
+ var belowLanguageCutoff = profile.Languages.Take(languageCutoffIndex).ToList();
+
+ if (belowLanguageCutoff.Any())
+ {
+ languagesBelowCutoff.Add(new LanguagesBelowCutoff(profile.Id, belowLanguageCutoff.Select(l => l.Language.Id)));
+ }
+ }
+
+ return _albumRepository.AlbumsWhereCutoffUnmet(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Music/AlbumRepository.cs b/src/NzbDrone.Core/Music/AlbumRepository.cs
index 83e6d9296..439e9c698 100644
--- a/src/NzbDrone.Core/Music/AlbumRepository.cs
+++ b/src/NzbDrone.Core/Music/AlbumRepository.cs
@@ -5,6 +5,8 @@ using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Extensions;
using System.Collections.Generic;
using NzbDrone.Core.Messaging.Events;
+using NzbDrone.Core.Languages;
+using NzbDrone.Core.Qualities;
using Marr.Data.QGen;
namespace NzbDrone.Core.Music
@@ -18,6 +20,7 @@ namespace NzbDrone.Core.Music
Album FindByArtistAndName(string artistName, string cleanTitle);
Album FindById(string spotifyId);
PagingSpec AlbumsWithoutFiles(PagingSpec pagingSpec);
+ PagingSpec AlbumsWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff, List languagesBelowCutoff);
List AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored);
void SetMonitoredFlat(Album album, bool monitored);
void SetMonitored(IEnumerable ids, bool monitored);
@@ -61,6 +64,15 @@ namespace NzbDrone.Core.Music
return pagingSpec;
}
+ public PagingSpec AlbumsWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff, List languagesBelowCutoff)
+ {
+
+ pagingSpec.TotalRecords = GetCutOffAlbumsQueryCount(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff);
+ pagingSpec.Records = GetCutOffAlbumsQuery(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff).ToList();
+
+ return pagingSpec;
+ }
+
public List AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored)
{
var query = Query.Join(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistId == s.Id)
@@ -148,6 +160,93 @@ namespace NzbDrone.Core.Music
currentTime.ToString("yyyy-MM-dd HH:mm:ss"));
}
+ private QueryBuilder GetCutOffAlbumsQuery(PagingSpec pagingSpec, List qualitiesBelowCutoff, List languagesBelowCutoff)
+ {
+ string sortKey;
+ string monitored = "(Albums.[Monitored] = 0) OR (Artists.[Monitored] = 0)";
+
+ if (pagingSpec.FilterExpression.ToString().Contains("True"))
+ {
+ monitored = "(Albums.[Monitored] = 1) AND (Artists.[Monitored] = 1)";
+ }
+
+ if (pagingSpec.SortKey == "releaseDate")
+ {
+ sortKey = "Albums." + pagingSpec.SortKey;
+ }
+ else if (pagingSpec.SortKey == "artist.sortName")
+ {
+ sortKey = "Artists." + pagingSpec.SortKey.Split('.').Last();
+ }
+ else
+ {
+ sortKey = "Albums.releaseDate";
+ }
+
+ string query = string.Format("SELECT Albums.* FROM(SELECT TrackFiles.AlbumId, TrackFiles.Language, COUNT(*) AS FileCount, " +
+ " MIN(Quality) AS MinQuality FROM TrackFiles GROUP BY TrackFiles.ArtistId, TrackFiles.AlbumId) as TrackFiles" +
+ " LEFT OUTER JOIN Albums ON TrackFiles.AlbumId == Albums.Id" +
+ " LEFT OUTER JOIN Artists ON Albums.ArtistId == Artists.Id" +
+ " WHERE ({0}) AND ({1} OR {2})" +
+ " GROUP BY TrackFiles.AlbumId" +
+ " ORDER BY {3} {4} LIMIT {5} OFFSET {6}",
+ monitored, BuildQualityCutoffWhereClause(qualitiesBelowCutoff), BuildLanguageCutoffWhereClause(languagesBelowCutoff), sortKey, pagingSpec.ToSortDirection(), pagingSpec.PageSize, pagingSpec.PagingOffset());
+
+ return Query.QueryText(query);
+
+ }
+
+ private int GetCutOffAlbumsQueryCount(PagingSpec pagingSpec, List qualitiesBelowCutoff, List languagesBelowCutoff)
+ {
+ var monitored = 0;
+
+ if (pagingSpec.FilterExpression.ToString().Contains("True"))
+ {
+ monitored = 1;
+ }
+
+ string query = string.Format("SELECT Albums.* FROM (SELECT TrackFiles.AlbumId, TrackFiles.Language, COUNT(*) AS FileCount," +
+ " MIN(Quality) AS MinQuality FROM TrackFiles GROUP BY TrackFiles.ArtistId, TrackFiles.AlbumId) as TrackFiles" +
+ " LEFT OUTER JOIN Albums ON TrackFiles.AlbumId == Albums.Id" +
+ " LEFT OUTER JOIN Artists ON Albums.ArtistId == Artists.Id" +
+ " WHERE ({0}) AND ({1} OR {2})" +
+ " GROUP BY TrackFiles.AlbumId",
+ monitored, BuildQualityCutoffWhereClause(qualitiesBelowCutoff), BuildLanguageCutoffWhereClause(languagesBelowCutoff));
+
+ return Query.QueryText(query).Count();
+ }
+
+
+ private string BuildLanguageCutoffWhereClause(List languagesBelowCutoff)
+ {
+ var clauses = new List();
+
+ foreach (var language in languagesBelowCutoff)
+ {
+ foreach (var belowCutoff in language.LanguageIds)
+ {
+ clauses.Add(String.Format("(Artists.[LanguageProfileId] = {0} AND TrackFiles.[Language] = {1})", language.ProfileId, belowCutoff));
+ }
+ }
+
+ return String.Format("({0})", String.Join(" OR ", clauses));
+ }
+
+ 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.[ProfileId] = {0} AND TrackFiles.MinQuality LIKE '%_quality_: {1},%')", profile.ProfileId, belowCutoff));
+ }
+ }
+
+ return string.Format("({0})", string.Join(" OR ", clauses));
+ }
+
public void SetMonitoredFlat(Album album, bool monitored)
{
album.Monitored = monitored;
diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj
index dbe1dfa8d..a996e2f07 100644
--- a/src/NzbDrone.Core/NzbDrone.Core.csproj
+++ b/src/NzbDrone.Core/NzbDrone.Core.csproj
@@ -866,6 +866,7 @@
+