using System; using System.Collections.Generic; using System.Linq; using Marr.Data.QGen; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Tv { public interface IEpisodeRepository : IBasicRepository { Episode Find(int seriesId, int season, int episodeNumber); Episode Get(int seriesId, DateTime date); Episode Find(int seriesId, DateTime date); List GetEpisodes(int seriesId); List GetEpisodes(int seriesId, int seasonNumber); List GetEpisodeByFileId(int fileId); PagingSpec EpisodesWithoutFiles(PagingSpec pagingSpec, bool includeSpecials); Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber); List EpisodesBetweenDates(DateTime startDate, DateTime endDate); void SetMonitoredFlat(Episode episode, bool monitored); void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored); void SetFileId(int episodeId, int fileId); void CleanupOrphanedEpisodes(); } public class EpisodeRepository : BasicRepository, IEpisodeRepository { private readonly IDatabase _database; public EpisodeRepository(IDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { _database = database; } public Episode Find(int seriesId, int season, int episodeNumber) { return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SeasonNumber == season && s.EpisodeNumber == episodeNumber); } public Episode Get(int seriesId, DateTime date) { return Query.Single(s => s.SeriesId == seriesId && s.AirDate == date.ToString(Episode.AIR_DATE_FORMAT)); } public Episode Find(int seriesId, DateTime date) { return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.AirDate == date.ToString(Episode.AIR_DATE_FORMAT)); } public List GetEpisodes(int seriesId) { return Query.Where(s => s.SeriesId == seriesId).ToList(); } public List GetEpisodes(int seriesId, int seasonNumber) { return Query.Where(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber).ToList(); } public List GetEpisodeByFileId(int fileId) { return Query.Where(e => e.EpisodeFileId == fileId).ToList(); } public PagingSpec EpisodesWithoutFiles(PagingSpec pagingSpec, bool includeSpecials) { var currentTime = DateTime.UtcNow; var startingSeasonNumber = 1; if (includeSpecials) { startingSeasonNumber = 0; } pagingSpec.Records = GetEpisodesWithoutFilesQuery(pagingSpec, currentTime, startingSeasonNumber).ToList(); pagingSpec.TotalRecords = GetEpisodesWithoutFilesQuery(pagingSpec, currentTime, startingSeasonNumber).GetRowCount(); return pagingSpec; } public Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) { return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SceneSeasonNumber == seasonNumber && s.SceneEpisodeNumber == episodeNumber); } public List EpisodesBetweenDates(DateTime startDate, DateTime endDate) { return Query.Join(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id) .Where(e => e.AirDateUtc >= startDate) .AndWhere(e => e.AirDateUtc <= endDate) .AndWhere(e => e.Monitored) .AndWhere(e => e.Series.Monitored) .ToList(); } public void SetMonitoredFlat(Episode episode, bool monitored) { episode.Monitored = monitored; SetFields(episode, p => p.Monitored); } public void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored) { var mapper = _database.GetDataMapper(); mapper.AddParameter("seriesId", seriesId); mapper.AddParameter("seasonNumber", seasonNumber); mapper.AddParameter("monitored", monitored); const string sql = "UPDATE Episodes " + "SET Monitored = @monitored " + "WHERE SeriesId = @seriesId " + "AND SeasonNumber = @seasonNumber"; mapper.ExecuteNonQuery(sql); } public void SetFileId(int episodeId, int fileId) { SetFields(new Episode { Id = episodeId, EpisodeFileId = fileId }, episode => episode.EpisodeFileId); } public void CleanupOrphanedEpisodes() { var mapper = _database.GetDataMapper(); mapper.ExecuteNonQuery(@"DELETE FROM Episodes WHERE Id IN ( SELECT Episodes.Id FROM Episodes LEFT OUTER JOIN Series ON Episodes.SeriesId = Series.Id WHERE Series.Id IS NULL)"); } private SortBuilder GetEpisodesWithoutFilesQuery(PagingSpec pagingSpec, DateTime currentTime, int startingSeasonNumber) { return Query.Join(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id) .Where(e => e.EpisodeFileId == 0) .AndWhere(e => e.SeasonNumber >= startingSeasonNumber) .AndWhere(e => e.AirDateUtc <= currentTime) .AndWhere(e => e.Monitored) .AndWhere(e => e.Series.Monitored) .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) .Skip(pagingSpec.PagingOffset()) .Take(pagingSpec.PageSize); } } }