Add AlbumCutoffService and Refactor UI

pull/6/head
Qstick 7 years ago
parent 8683c92de6
commit eaba78ad4a

@ -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',

@ -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 (
<TableRowCell key={name}>
<ArtistNameLink
titleSlug={artist.titleSlug}
title={artist.title}
nameSlug={artist.nameSlug}
artistName={artist.artistName}
/>
</TableRowCell>
);
}
if (name === 'episode') {
return (
<TableRowCell
key={name}
className={styles.episode}
>
<SeasonEpisodeNumber
seasonNumber={seasonNumber}
episodeNumber={episodeNumber}
absoluteEpisodeNumber={absoluteEpisodeNumber}
artistType={artist.artistType}
sceneSeasonNumber={sceneSeasonNumber}
sceneEpisodeNumber={sceneEpisodeNumber}
sceneAbsoluteEpisodeNumber={sceneAbsoluteEpisodeNumber}
/>
</TableRowCell>
);
}
if (name === 'episodeTitle') {
if (name === 'albumTitle') {
return (
<TableRowCell key={name}>
<EpisodeTitleLink
@ -94,11 +68,11 @@ function CutoffUnmetRow(props) {
);
}
if (name === 'airDateUtc') {
if (name === 'releaseDate') {
return (
<RelativeDateCellConnector
key={name}
date={airDateUtc}
date={releaseDate}
/>
);
}
@ -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,

@ -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;
}
}
}

@ -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<Album> AlbumsWhereCutoffUnmet(PagingSpec<Album> 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<Album> AlbumsWhereCutoffUnmet(PagingSpec<Album> pagingSpec)
{
var qualitiesBelowCutoff = new List<QualitiesBelowCutoff>();
var languagesBelowCutoff = new List<LanguagesBelowCutoff>();
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);
}
}
}

@ -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<Album> AlbumsWithoutFiles(PagingSpec<Album> pagingSpec);
PagingSpec<Album> AlbumsWhereCutoffUnmet(PagingSpec<Album> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, List<LanguagesBelowCutoff> languagesBelowCutoff);
List<Album> AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored);
void SetMonitoredFlat(Album album, bool monitored);
void SetMonitored(IEnumerable<int> ids, bool monitored);
@ -61,6 +64,15 @@ namespace NzbDrone.Core.Music
return pagingSpec;
}
public PagingSpec<Album> AlbumsWhereCutoffUnmet(PagingSpec<Album> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, List<LanguagesBelowCutoff> languagesBelowCutoff)
{
pagingSpec.TotalRecords = GetCutOffAlbumsQueryCount(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff);
pagingSpec.Records = GetCutOffAlbumsQuery(pagingSpec, qualitiesBelowCutoff, languagesBelowCutoff).ToList();
return pagingSpec;
}
public List<Album> AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored)
{
var query = Query.Join<Album, Artist>(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<Album> GetCutOffAlbumsQuery(PagingSpec<Album> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, List<LanguagesBelowCutoff> 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<Album> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, List<LanguagesBelowCutoff> 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> languagesBelowCutoff)
{
var clauses = new List<String>();
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> qualitiesBelowCutoff)
{
var clauses = new List<string>();
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;

@ -866,6 +866,7 @@
<Compile Include="Extras\Metadata\MetadataService.cs" />
<Compile Include="Extras\Metadata\MetadataType.cs" />
<Compile Include="Music\ArtistStatusType.cs" />
<Compile Include="Music\AlbumCutoffService.cs" />
<Compile Include="Music\Links.cs" />
<Compile Include="Music\Commands\MoveArtistCommand.cs" />
<Compile Include="Music\Events\ArtistMovedEvent.cs" />

Loading…
Cancel
Save