Wanted is much much faster now.

pull/2/head
Mark McDowall 11 years ago
parent 79767aa7bf
commit 20dec3c205

@ -3,19 +3,18 @@ using NzbDrone.Api.Episodes;
using NzbDrone.Api.Extensions; using NzbDrone.Api.Extensions;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Api.Wanted namespace NzbDrone.Api.Wanted
{ {
public class CutoffModule : NzbDroneRestModule<EpisodeResource> public class CutoffModule : NzbDroneRestModule<EpisodeResource>
{ {
private readonly IEpisodeService _episodeService; private readonly IEpisodeCutoffService _episodeCutoffService;
private readonly SeriesRepository _seriesRepository; private readonly SeriesRepository _seriesRepository;
public CutoffModule(IEpisodeService episodeService, SeriesRepository seriesRepository) public CutoffModule(IEpisodeCutoffService episodeCutoffService, SeriesRepository seriesRepository)
:base("wanted/cutoff") :base("wanted/cutoff")
{ {
_episodeService = episodeService; _episodeCutoffService = episodeCutoffService;
_seriesRepository = seriesRepository; _seriesRepository = seriesRepository;
GetResourcePaged = GetCutoffUnmetEpisodes; GetResourcePaged = GetCutoffUnmetEpisodes;
} }
@ -39,7 +38,7 @@ namespace NzbDrone.Api.Wanted
pagingSpec.FilterExpression = v => v.Monitored == true && v.Series.Monitored == true; pagingSpec.FilterExpression = v => v.Monitored == true && v.Series.Monitored == true;
} }
PagingResource<EpisodeResource> resource = ApplyToPage(_episodeService.EpisodesWhereCutoffUnmet, pagingSpec); PagingResource<EpisodeResource> resource = ApplyToPage(_episodeCutoffService.EpisodesWhereCutoffUnmet, pagingSpec);
resource.Records = resource.Records.LoadSubtype(e => e.SeriesId, _seriesRepository).ToList(); resource.Records = resource.Records.LoadSubtype(e => e.SeriesId, _seriesRepository).ToList();

@ -512,6 +512,7 @@
<Compile Include="Parser\Parser.cs" /> <Compile Include="Parser\Parser.cs" />
<Compile Include="Parser\ParsingService.cs" /> <Compile Include="Parser\ParsingService.cs" />
<Compile Include="Parser\QualityParser.cs" /> <Compile Include="Parser\QualityParser.cs" />
<Compile Include="Qualities\QualitiesBelowCutoff.cs" />
<Compile Include="Qualities\QualityModelComparer.cs" /> <Compile Include="Qualities\QualityModelComparer.cs" />
<Compile Include="Qualities\QualityProfileItem.cs" /> <Compile Include="Qualities\QualityProfileItem.cs" />
<Compile Include="Rest\JsonNetSerializer.cs" /> <Compile Include="Rest\JsonNetSerializer.cs" />
@ -536,6 +537,7 @@
<Compile Include="ThingiProvider\ProviderRepository.cs" /> <Compile Include="ThingiProvider\ProviderRepository.cs" />
<Compile Include="ThingiProvider\ProviderFactory.cs" /> <Compile Include="ThingiProvider\ProviderFactory.cs" />
<Compile Include="Tv\Actor.cs" /> <Compile Include="Tv\Actor.cs" />
<Compile Include="Tv\EpisodeCutoffService.cs" />
<Compile Include="Tv\EpisodeService.cs" /> <Compile Include="Tv\EpisodeService.cs" />
<Compile Include="Tv\Events\SeriesEditedEvent.cs" /> <Compile Include="Tv\Events\SeriesEditedEvent.cs" />
<Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" /> <Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" />

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.Qualities
{
public class QualitiesBelowCutoff
{
public Int32 ProfileId { get; set; }
public IEnumerable<Int32> QualityIds { get; set; }
public QualitiesBelowCutoff(int profileId, IEnumerable<int> qualityIds)
{
ProfileId = profileId;
QualityIds = qualityIds;
}
}
}

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Tv
{
public interface IEpisodeCutoffService
{
PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec);
}
public class EpisodeCutoffService : IEpisodeCutoffService
{
private readonly IEpisodeRepository _episodeRepository;
private readonly IQualityProfileService _qualityProfileService;
private readonly Logger _logger;
public EpisodeCutoffService(IEpisodeRepository episodeRepository, IQualityProfileService qualityProfileService, Logger logger)
{
_episodeRepository = episodeRepository;
_qualityProfileService = qualityProfileService;
_logger = logger;
}
public PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec)
{
var qualitiesBelowCutoff = new List<QualitiesBelowCutoff>();
var qualityProfiles = _qualityProfileService.All();
//Get all items less than the cutoff
foreach (var qualityProfile in qualityProfiles)
{
var cutoffIndex = qualityProfile.Items.FindIndex(v => v.Quality == qualityProfile.Cutoff);
var belowCutoff = qualityProfile.Items.Take(cutoffIndex).ToList();
if (belowCutoff.Any())
{
qualitiesBelowCutoff.Add(new QualitiesBelowCutoff(qualityProfile.Id, belowCutoff.Select(i => i.Quality.Id)));
}
}
return _episodeRepository.EpisodesWhereCutoffUnmet(pagingSpec, qualitiesBelowCutoff, false);
}
}
}

@ -5,6 +5,7 @@ using Marr.Data.QGen;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Tv namespace NzbDrone.Core.Tv
{ {
@ -18,7 +19,7 @@ namespace NzbDrone.Core.Tv
List<Episode> GetEpisodes(int seriesId, int seasonNumber); List<Episode> GetEpisodes(int seriesId, int seasonNumber);
List<Episode> GetEpisodeByFileId(int fileId); List<Episode> GetEpisodeByFileId(int fileId);
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials); PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec, bool includeSpecials);
List<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, bool includeSpecials); PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, bool includeSpecials);
Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber); Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber);
List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate); List<Episode> EpisodesBetweenDates(DateTime startDate, DateTime endDate);
void SetMonitoredFlat(Episode episode, bool monitored); void SetMonitoredFlat(Episode episode, bool monitored);
@ -98,7 +99,7 @@ namespace NzbDrone.Core.Tv
return pagingSpec; return pagingSpec;
} }
public List<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, bool includeSpecials) public PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec, List<QualitiesBelowCutoff> qualitiesBelowCutoff, bool includeSpecials)
{ {
var currentTime = DateTime.UtcNow; var currentTime = DateTime.UtcNow;
var startingSeasonNumber = 1; var startingSeasonNumber = 1;
@ -108,29 +109,10 @@ namespace NzbDrone.Core.Tv
startingSeasonNumber = 0; startingSeasonNumber = 0;
} }
var query = Query.Join<Episode, Series>(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id) pagingSpec.TotalRecords = EpisodesWhereCutoffUnmetQuery(pagingSpec, currentTime, qualitiesBelowCutoff, startingSeasonNumber).GetRowCount();
.Join<Episode, EpisodeFile>(JoinType.Left, e => e.EpisodeFile, (e, s) => e.EpisodeFileId == s.Id) pagingSpec.Records = EpisodesWhereCutoffUnmetQuery(pagingSpec, currentTime, qualitiesBelowCutoff, startingSeasonNumber).ToList();
.Where(pagingSpec.FilterExpression)
.AndWhere(e => e.EpisodeFileId != 0)
.AndWhere(e => e.SeasonNumber >= startingSeasonNumber)
.AndWhere(e => e.AirDateUtc <= currentTime)
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection());
return query.ToList();
}
private SortBuilder<Episode> GetMissingEpisodesQuery(PagingSpec<Episode> pagingSpec, DateTime currentTime, int startingSeasonNumber)
{
var query = Query.Join<Episode, Series>(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id)
.Where(pagingSpec.FilterExpression)
.AndWhere(e => e.EpisodeFileId == 0)
.AndWhere(e => e.SeasonNumber >= startingSeasonNumber)
.AndWhere(e => e.AirDateUtc <= currentTime)
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
.Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize);
return query; return pagingSpec;
} }
public Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) public Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber)
@ -177,5 +159,46 @@ namespace NzbDrone.Core.Tv
{ {
SetFields(new Episode { Id = episodeId, EpisodeFileId = fileId }, episode => episode.EpisodeFileId); SetFields(new Episode { Id = episodeId, EpisodeFileId = fileId }, episode => episode.EpisodeFileId);
} }
private SortBuilder<Episode> GetMissingEpisodesQuery(PagingSpec<Episode> pagingSpec, DateTime currentTime, int startingSeasonNumber)
{
return Query.Join<Episode, Series>(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id)
.Where(pagingSpec.FilterExpression)
.AndWhere(e => e.EpisodeFileId == 0)
.AndWhere(e => e.SeasonNumber >= startingSeasonNumber)
.AndWhere(e => e.AirDateUtc <= currentTime)
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
.Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize);
}
private SortBuilder<Episode> EpisodesWhereCutoffUnmetQuery(PagingSpec<Episode> pagingSpec, DateTime currentTime, List<QualitiesBelowCutoff> qualitiesBelowCutoff, int startingSeasonNumber)
{
return Query.Join<Episode, Series>(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id)
.Join<Episode, EpisodeFile>(JoinType.Left, e => e.EpisodeFile, (e, s) => e.EpisodeFileId == s.Id)
.Where(pagingSpec.FilterExpression)
.AndWhere(e => e.EpisodeFileId != 0)
.AndWhere(e => e.SeasonNumber >= startingSeasonNumber)
.AndWhere(e => e.AirDateUtc <= currentTime)
.AndWhere(BuildQualityCutoffWhereClause(qualitiesBelowCutoff))
.OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection())
.Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize);
}
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("([t1].[QualityProfileId] = {0} AND [t2].[Quality] LIKE '%_quality_:%{1}%')", profile.ProfileId, belowCutoff));
}
}
return String.Format("({0})", String.Join(" OR ", clauses));
}
} }
} }

@ -22,7 +22,6 @@ namespace NzbDrone.Core.Tv
List<Episode> GetEpisodeBySeries(int seriesId); List<Episode> GetEpisodeBySeries(int seriesId);
List<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber); List<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber);
PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec); PagingSpec<Episode> EpisodesWithoutFiles(PagingSpec<Episode> pagingSpec);
PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec);
List<Episode> GetEpisodesByFileId(int episodeFileId); List<Episode> GetEpisodesByFileId(int episodeFileId);
void UpdateEpisode(Episode episode); void UpdateEpisode(Episode episode);
void SetEpisodeMonitored(int episodeId, bool monitored); void SetEpisodeMonitored(int episodeId, bool monitored);
@ -114,34 +113,6 @@ namespace NzbDrone.Core.Tv
return episodeResult; return episodeResult;
} }
public PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec)
{
var allSpec = new PagingSpec<Episode>
{
SortKey = pagingSpec.SortKey,
SortDirection = pagingSpec.SortDirection,
FilterExpression = pagingSpec.FilterExpression
};
var allItems = _episodeRepository.EpisodesWhereCutoffUnmet(allSpec, false);
var qualityProfileComparers = _qualityProfileRepository.All().ToDictionary(v => v.Id, v => new { Profile = v, Comparer = new QualityModelComparer(v) });
var filtered = allItems.Where(episode =>
{
var profile = qualityProfileComparers[episode.Series.QualityProfileId];
return profile.Comparer.Compare(episode.EpisodeFile.Value.Quality.Quality, profile.Profile.Cutoff) < 0;
}).ToList();
pagingSpec.Records = filtered
.Skip(pagingSpec.PagingOffset())
.Take(pagingSpec.PageSize)
.ToList();
pagingSpec.TotalRecords = filtered.Count;
return pagingSpec;
}
public List<Episode> GetEpisodesByFileId(int episodeFileId) public List<Episode> GetEpisodesByFileId(int episodeFileId)
{ {
return _episodeRepository.GetEpisodeByFileId(episodeFileId); return _episodeRepository.GetEpisodeByFileId(episodeFileId);

Loading…
Cancel
Save