mirror of
https://github.com/Radarr/Radarr
synced 2026-01-25 08:53:02 +01:00
refactor(tv): integrate hierarchical monitoring and linter fixes
- Add SetTVShowMonitored/SetSeasonMonitored to monitoring service - Add IsEffectivelyMonitored for Episode/Season - Add GetEffectivelyMonitoredEpisodes - Cascade unmonitoring through TV hierarchy - Fix nullable types for TV entity IDs - Simplify repositories with consistent patterns - Add SeasonNumber to EpisodeFile for better queries 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
22eb0f76c6
commit
b9e086cca6
23 changed files with 557 additions and 287 deletions
|
|
@ -9,7 +9,7 @@ public class add_tv_tables : NzbDroneMigrationBase
|
|||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Create.TableForModel("TVShows")
|
||||
.WithColumn("TvdbId").AsInt32().NotNullable()
|
||||
.WithColumn("TvdbId").AsInt32().Nullable()
|
||||
.WithColumn("TmdbId").AsInt32().Nullable()
|
||||
.WithColumn("ImdbId").AsString().Nullable()
|
||||
.WithColumn("AniDbId").AsInt32().Nullable()
|
||||
|
|
@ -70,7 +70,9 @@ protected override void MainDbUpgrade()
|
|||
.WithColumn("AirDateUtc").AsDateTime().Nullable()
|
||||
.WithColumn("Runtime").AsInt32().Nullable()
|
||||
.WithColumn("UnverifiedSceneNumbering").AsBoolean().NotNullable().WithDefaultValue(false)
|
||||
.WithColumn("IsSpecial").AsBoolean().NotNullable().WithDefaultValue(false)
|
||||
.WithColumn("EpisodeFileId").AsInt32().Nullable()
|
||||
.WithColumn("MediaType").AsInt32().NotNullable().WithDefaultValue(2)
|
||||
.WithColumn("Monitored").AsBoolean().NotNullable().WithDefaultValue(true)
|
||||
.WithColumn("QualityProfileId").AsInt32().NotNullable()
|
||||
.WithColumn("Path").AsString().Nullable()
|
||||
|
|
@ -93,8 +95,9 @@ protected override void MainDbUpgrade()
|
|||
Create.Index("IX_Episodes_Monitored").OnTable("Episodes").OnColumn("Monitored");
|
||||
|
||||
Create.TableForModel("EpisodeFiles")
|
||||
.WithColumn("TVShowId").AsInt32().NotNullable()
|
||||
.WithColumn("SeasonNumber").AsInt32().NotNullable()
|
||||
.WithColumn("TVShowId").AsInt32().Nullable()
|
||||
.WithColumn("SeasonId").AsInt32().Nullable()
|
||||
.WithColumn("EpisodeId").AsInt32().Nullable()
|
||||
.WithColumn("RelativePath").AsString().Nullable()
|
||||
.WithColumn("Path").AsString().Nullable()
|
||||
.WithColumn("Size").AsInt64().NotNullable()
|
||||
|
|
@ -107,9 +110,8 @@ protected override void MainDbUpgrade()
|
|||
.WithColumn("MediaInfo").AsString().Nullable();
|
||||
|
||||
Create.Index("IX_EpisodeFiles_TVShowId").OnTable("EpisodeFiles").OnColumn("TVShowId");
|
||||
Create.Index("IX_EpisodeFiles_TVShowId_SeasonNumber").OnTable("EpisodeFiles")
|
||||
.OnColumn("TVShowId").Ascending()
|
||||
.OnColumn("SeasonNumber").Ascending();
|
||||
Create.Index("IX_EpisodeFiles_SeasonId").OnTable("EpisodeFiles").OnColumn("SeasonId");
|
||||
Create.Index("IX_EpisodeFiles_EpisodeId").OnTable("EpisodeFiles").OnColumn("EpisodeId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.TV;
|
||||
|
||||
namespace NzbDrone.Core.Monitoring.Events
|
||||
{
|
||||
public class SeasonMonitoringChangedEvent : IEvent
|
||||
{
|
||||
public Season Season { get; private set; }
|
||||
public bool PreviousMonitored { get; private set; }
|
||||
public int AffectedEpisodesCount { get; set; }
|
||||
|
||||
public SeasonMonitoringChangedEvent(Season season, bool previousMonitored)
|
||||
{
|
||||
Season = season;
|
||||
PreviousMonitored = previousMonitored;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.TV;
|
||||
|
||||
namespace NzbDrone.Core.Monitoring.Events
|
||||
{
|
||||
public class TVShowMonitoringChangedEvent : IEvent
|
||||
{
|
||||
public TVShow TVShow { get; private set; }
|
||||
public bool PreviousMonitored { get; private set; }
|
||||
public int AffectedSeasonsCount { get; set; }
|
||||
public int AffectedEpisodesCount { get; set; }
|
||||
|
||||
public TVShowMonitoringChangedEvent(TVShow tvShow, bool previousMonitored)
|
||||
{
|
||||
TVShow = tvShow;
|
||||
PreviousMonitored = previousMonitored;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Monitoring.Events;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.TV;
|
||||
|
||||
namespace NzbDrone.Core.Monitoring
|
||||
{
|
||||
|
|
@ -22,6 +23,9 @@ public class HierarchicalMonitoringService : IHierarchicalMonitoringService
|
|||
private readonly IArtistRepository _artistRepository;
|
||||
private readonly IAlbumRepository _albumRepository;
|
||||
private readonly ITrackRepository _trackRepository;
|
||||
private readonly ITVShowRepository _tvShowRepository;
|
||||
private readonly ISeasonRepository _seasonRepository;
|
||||
private readonly IEpisodeRepository _episodeRepository;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
|
@ -33,6 +37,9 @@ public HierarchicalMonitoringService(
|
|||
IArtistRepository artistRepository,
|
||||
IAlbumRepository albumRepository,
|
||||
ITrackRepository trackRepository,
|
||||
ITVShowRepository tvShowRepository,
|
||||
ISeasonRepository seasonRepository,
|
||||
IEpisodeRepository episodeRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
|
|
@ -43,6 +50,9 @@ public HierarchicalMonitoringService(
|
|||
_artistRepository = artistRepository;
|
||||
_albumRepository = albumRepository;
|
||||
_trackRepository = trackRepository;
|
||||
_tvShowRepository = tvShowRepository;
|
||||
_seasonRepository = seasonRepository;
|
||||
_episodeRepository = episodeRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
|
@ -254,6 +264,149 @@ public List<Track> GetEffectivelyMonitoredTracks()
|
|||
.ToList();
|
||||
}
|
||||
|
||||
public bool IsEffectivelyMonitored(Episode episode)
|
||||
{
|
||||
return episode.Monitored && !IsTVAncestorUnmonitored(episode.SeasonId, episode.TVShowId);
|
||||
}
|
||||
|
||||
public bool IsEffectivelyMonitored(Season season)
|
||||
{
|
||||
return season.Monitored && !IsTVAncestorUnmonitored(null, season.TVShowId);
|
||||
}
|
||||
|
||||
public void SetTVShowMonitored(int tvShowId, bool monitored)
|
||||
{
|
||||
var tvShow = _tvShowRepository.Get(tvShowId);
|
||||
if (tvShow == null)
|
||||
{
|
||||
_logger.Warn("TVShow with id {0} not found", tvShowId);
|
||||
return;
|
||||
}
|
||||
|
||||
var previousMonitored = tvShow.Monitored;
|
||||
if (previousMonitored == monitored)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tvShow.Monitored = monitored;
|
||||
_tvShowRepository.Update(tvShow);
|
||||
|
||||
var changeEvent = new TVShowMonitoringChangedEvent(tvShow, previousMonitored);
|
||||
|
||||
if (previousMonitored && !monitored)
|
||||
{
|
||||
CascadeUnmonitorFromTVShow(tvShowId, changeEvent);
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(changeEvent);
|
||||
|
||||
_logger.Info("TVShow {0} monitoring changed from {1} to {2}. Affected: {3} seasons, {4} episodes",
|
||||
tvShow.Title,
|
||||
previousMonitored,
|
||||
monitored,
|
||||
changeEvent.AffectedSeasonsCount,
|
||||
changeEvent.AffectedEpisodesCount);
|
||||
}
|
||||
|
||||
public void SetSeasonMonitored(int seasonId, bool monitored)
|
||||
{
|
||||
var season = _seasonRepository.Get(seasonId);
|
||||
if (season == null)
|
||||
{
|
||||
_logger.Warn("Season with id {0} not found", seasonId);
|
||||
return;
|
||||
}
|
||||
|
||||
var previousMonitored = season.Monitored;
|
||||
if (previousMonitored == monitored)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
season.Monitored = monitored;
|
||||
_seasonRepository.Update(season);
|
||||
|
||||
var changeEvent = new SeasonMonitoringChangedEvent(season, previousMonitored);
|
||||
|
||||
if (previousMonitored && !monitored)
|
||||
{
|
||||
CascadeUnmonitorFromSeason(seasonId, changeEvent);
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(changeEvent);
|
||||
|
||||
_logger.Info("Season {0} monitoring changed from {1} to {2}. Affected: {3} episodes",
|
||||
season.SeasonNumber,
|
||||
previousMonitored,
|
||||
monitored,
|
||||
changeEvent.AffectedEpisodesCount);
|
||||
}
|
||||
|
||||
public List<Episode> GetEffectivelyMonitoredEpisodes()
|
||||
{
|
||||
var monitoredTVShows = _tvShowRepository.GetMonitored()
|
||||
.Select(t => t.Id)
|
||||
.ToHashSet();
|
||||
|
||||
var monitoredSeasons = _seasonRepository.All()
|
||||
.Where(s => s.Monitored)
|
||||
.Where(s => !s.TVShowId.HasValue || monitoredTVShows.Contains(s.TVShowId.Value))
|
||||
.Select(s => s.Id)
|
||||
.ToHashSet();
|
||||
|
||||
return _episodeRepository.All()
|
||||
.Where(e => e.Monitored)
|
||||
.Where(e => !e.SeasonId.HasValue || monitoredSeasons.Contains(e.SeasonId.Value))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private bool IsTVAncestorUnmonitored(int? seasonId, int? tvShowId)
|
||||
{
|
||||
if (seasonId.HasValue)
|
||||
{
|
||||
var season = _seasonRepository.Get(seasonId.Value);
|
||||
if (season != null && !season.Monitored)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (tvShowId.HasValue)
|
||||
{
|
||||
var tvShow = _tvShowRepository.Get(tvShowId.Value);
|
||||
if (tvShow != null && !tvShow.Monitored)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CascadeUnmonitorFromTVShow(int tvShowId, TVShowMonitoringChangedEvent changeEvent)
|
||||
{
|
||||
var seasonsToUnmonitor = _seasonRepository.FindByTVShowId(tvShowId).Where(s => s.Monitored).ToList();
|
||||
changeEvent.AffectedSeasonsCount = UnmonitorEntities(
|
||||
seasonsToUnmonitor,
|
||||
s => s.Monitored = false,
|
||||
_seasonRepository.UpdateMany);
|
||||
|
||||
var seasonIds = seasonsToUnmonitor.Select(s => s.Id).ToList();
|
||||
changeEvent.AffectedEpisodesCount = UnmonitorEntities(
|
||||
seasonIds.SelectMany(id => _episodeRepository.FindBySeasonId(id)).Where(e => e.Monitored).ToList(),
|
||||
e => e.Monitored = false,
|
||||
_episodeRepository.UpdateMany);
|
||||
}
|
||||
|
||||
private void CascadeUnmonitorFromSeason(int seasonId, SeasonMonitoringChangedEvent changeEvent)
|
||||
{
|
||||
changeEvent.AffectedEpisodesCount = UnmonitorEntities(
|
||||
_episodeRepository.FindBySeasonId(seasonId).Where(e => e.Monitored).ToList(),
|
||||
e => e.Monitored = false,
|
||||
_episodeRepository.UpdateMany);
|
||||
}
|
||||
|
||||
private bool IsMusicAncestorUnmonitored(int? artistId)
|
||||
{
|
||||
if (artistId.HasValue)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using NzbDrone.Core.Audiobooks;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.TV;
|
||||
|
||||
namespace NzbDrone.Core.Monitoring
|
||||
{
|
||||
|
|
@ -12,14 +13,19 @@ public interface IHierarchicalMonitoringService
|
|||
bool IsEffectivelyMonitored(NzbDrone.Core.BookSeries.BookSeries bookSeries);
|
||||
bool IsEffectivelyMonitored(Album album);
|
||||
bool IsEffectivelyMonitored(Track track);
|
||||
bool IsEffectivelyMonitored(Episode episode);
|
||||
bool IsEffectivelyMonitored(Season season);
|
||||
|
||||
void SetAuthorMonitored(int authorId, bool monitored);
|
||||
void SetBookSeriesMonitored(int bookSeriesId, bool monitored);
|
||||
void SetArtistMonitored(int artistId, bool monitored);
|
||||
void SetAlbumMonitored(int albumId, bool monitored);
|
||||
void SetTVShowMonitored(int tvShowId, bool monitored);
|
||||
void SetSeasonMonitored(int seasonId, bool monitored);
|
||||
|
||||
List<Book> GetEffectivelyMonitoredBooks();
|
||||
List<Audiobook> GetEffectivelyMonitoredAudiobooks();
|
||||
List<Track> GetEffectivelyMonitoredTracks();
|
||||
List<Episode> GetEffectivelyMonitoredEpisodes();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ public Episode()
|
|||
MediaType = MediaType.TV;
|
||||
}
|
||||
|
||||
public int TVShowId { get; set; }
|
||||
public int SeasonId { get; set; }
|
||||
public int? TVShowId { get; set; }
|
||||
public int? SeasonId { get; set; }
|
||||
|
||||
public int SeasonNumber { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
|
|
@ -24,12 +24,11 @@ public Episode()
|
|||
|
||||
public string Title { get; set; }
|
||||
public string Overview { get; set; }
|
||||
|
||||
public DateTime? AirDate { get; set; }
|
||||
public DateTime? AirDateUtc { get; set; }
|
||||
public int? Runtime { get; set; }
|
||||
|
||||
public bool IsSpecial => SeasonNumber == 0;
|
||||
public bool IsSpecial { get; set; }
|
||||
public bool UnverifiedSceneNumbering { get; set; }
|
||||
|
||||
public int? EpisodeFileId { get; set; }
|
||||
|
|
@ -39,12 +38,7 @@ public Episode()
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
if (AbsoluteEpisodeNumber.HasValue && SeasonNumber > 0)
|
||||
{
|
||||
return $"{Title} - S{SeasonNumber:00}E{EpisodeNumber:00} ({AbsoluteEpisodeNumber})";
|
||||
}
|
||||
|
||||
return $"{Title} - S{SeasonNumber:00}E{EpisodeNumber:00}";
|
||||
return $"S{SeasonNumber:00}E{EpisodeNumber:00} - {Title}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ namespace NzbDrone.Core.TV
|
|||
{
|
||||
public class EpisodeFile : ModelBase
|
||||
{
|
||||
public int TVShowId { get; set; }
|
||||
public int? TVShowId { get; set; }
|
||||
public int? SeasonId { get; set; }
|
||||
public int? EpisodeId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
|
||||
public string RelativePath { get; set; }
|
||||
|
|
@ -17,16 +19,15 @@ public class EpisodeFile : ModelBase
|
|||
|
||||
public string SceneName { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
|
||||
public QualityModel Quality { get; set; }
|
||||
public Language Language { get; set; }
|
||||
public StreamingSource StreamingSource { get; set; }
|
||||
public Language Language { get; set; }
|
||||
|
||||
public string MediaInfo { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Path;
|
||||
return RelativePath ?? Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,13 @@ namespace NzbDrone.Core.TV
|
|||
public interface IEpisodeFileRepository : IBasicRepository<EpisodeFile>
|
||||
{
|
||||
List<EpisodeFile> FindByTVShowId(int tvShowId);
|
||||
List<EpisodeFile> FindByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber);
|
||||
EpisodeFile FindByPath(string path);
|
||||
List<EpisodeFile> FindBySeasonId(int seasonId);
|
||||
EpisodeFile FindByEpisodeId(int episodeId);
|
||||
}
|
||||
|
||||
public class EpisodeFileRepository : BasicRepository<EpisodeFile>, IEpisodeFileRepository
|
||||
{
|
||||
public EpisodeFileRepository(IMainDatabase database,
|
||||
IEventAggregator eventAggregator)
|
||||
public EpisodeFileRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
|
|
@ -25,14 +24,14 @@ public List<EpisodeFile> FindByTVShowId(int tvShowId)
|
|||
return Query(f => f.TVShowId == tvShowId);
|
||||
}
|
||||
|
||||
public List<EpisodeFile> FindByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber)
|
||||
public List<EpisodeFile> FindBySeasonId(int seasonId)
|
||||
{
|
||||
return Query(f => f.TVShowId == tvShowId && f.SeasonNumber == seasonNumber);
|
||||
return Query(f => f.SeasonId == seasonId);
|
||||
}
|
||||
|
||||
public EpisodeFile FindByPath(string path)
|
||||
public EpisodeFile FindByEpisodeId(int episodeId)
|
||||
{
|
||||
return Query(f => f.Path == path).FirstOrDefault();
|
||||
return Query(f => f.EpisodeId == episodeId).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,15 @@ public interface IEpisodeRepository : IBasicRepository<Episode>
|
|||
{
|
||||
List<Episode> FindByTVShowId(int tvShowId);
|
||||
List<Episode> FindBySeasonId(int seasonId);
|
||||
List<Episode> FindByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber);
|
||||
Episode FindByTVShowIdAndEpisode(int tvShowId, int seasonNumber, int episodeNumber);
|
||||
Episode FindByTVShowIdAndEpisodeNumber(int tvShowId, int seasonNumber, int episodeNumber);
|
||||
Episode FindByTVShowIdAndAbsoluteNumber(int tvShowId, int absoluteNumber);
|
||||
Episode FindByAirDate(int tvShowId, string airDate);
|
||||
List<Episode> FindBySeasonAndEpisode(int tvShowId, int seasonNumber, int[] episodeNumbers);
|
||||
List<Episode> FindByAbsoluteEpisodeNumber(int tvShowId, int[] absoluteEpisodeNumbers);
|
||||
List<Episode> EpisodesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
||||
Episode FindByPath(string path);
|
||||
Dictionary<int, string> AllEpisodePaths();
|
||||
List<Episode> FindByAirDate(int tvShowId, DateTime airDate);
|
||||
List<Episode> GetMonitored();
|
||||
}
|
||||
|
||||
public class EpisodeRepository : BasicRepository<Episode>, IEpisodeRepository
|
||||
{
|
||||
public EpisodeRepository(IMainDatabase database,
|
||||
IEventAggregator eventAggregator)
|
||||
public EpisodeRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
|
|
@ -39,71 +33,29 @@ public List<Episode> FindBySeasonId(int seasonId)
|
|||
return Query(e => e.SeasonId == seasonId);
|
||||
}
|
||||
|
||||
public List<Episode> FindByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber)
|
||||
{
|
||||
return Query(e => e.TVShowId == tvShowId && e.SeasonNumber == seasonNumber);
|
||||
}
|
||||
|
||||
public Episode FindByTVShowIdAndEpisode(int tvShowId, int seasonNumber, int episodeNumber)
|
||||
public Episode FindByTVShowIdAndEpisodeNumber(int tvShowId, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return Query(e => e.TVShowId == tvShowId &&
|
||||
e.SeasonNumber == seasonNumber &&
|
||||
e.EpisodeNumber == episodeNumber).FirstOrDefault();
|
||||
e.SeasonNumber == seasonNumber &&
|
||||
e.EpisodeNumber == episodeNumber).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Episode FindByTVShowIdAndAbsoluteNumber(int tvShowId, int absoluteNumber)
|
||||
{
|
||||
return Query(e => e.TVShowId == tvShowId &&
|
||||
e.AbsoluteEpisodeNumber == absoluteNumber).FirstOrDefault();
|
||||
e.AbsoluteEpisodeNumber == absoluteNumber).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Episode FindByAirDate(int tvShowId, string airDate)
|
||||
{
|
||||
if (!DateTime.TryParse(airDate, out var date))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Query(e => e.TVShowId == tvShowId &&
|
||||
e.AirDate.HasValue &&
|
||||
e.AirDate.Value.Date == date.Date).FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<Episode> FindBySeasonAndEpisode(int tvShowId, int seasonNumber, int[] episodeNumbers)
|
||||
public List<Episode> FindByAirDate(int tvShowId, DateTime airDate)
|
||||
{
|
||||
return Query(e => e.TVShowId == tvShowId &&
|
||||
e.SeasonNumber == seasonNumber &&
|
||||
episodeNumbers.Contains(e.EpisodeNumber));
|
||||
e.AirDate.HasValue &&
|
||||
e.AirDate.Value.Date == airDate.Date);
|
||||
}
|
||||
|
||||
public List<Episode> FindByAbsoluteEpisodeNumber(int tvShowId, int[] absoluteEpisodeNumbers)
|
||||
public List<Episode> GetMonitored()
|
||||
{
|
||||
return Query(e => e.TVShowId == tvShowId &&
|
||||
e.AbsoluteEpisodeNumber.HasValue &&
|
||||
absoluteEpisodeNumbers.Contains(e.AbsoluteEpisodeNumber.Value));
|
||||
}
|
||||
|
||||
public List<Episode> EpisodesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
|
||||
{
|
||||
var query = Query(e => e.AirDateUtc >= start && e.AirDateUtc <= end);
|
||||
|
||||
if (!includeUnmonitored)
|
||||
{
|
||||
query = query.Where(e => e.Monitored).ToList();
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
public Episode FindByPath(string path)
|
||||
{
|
||||
return Query(e => e.Path == path).FirstOrDefault();
|
||||
}
|
||||
|
||||
public Dictionary<int, string> AllEpisodePaths()
|
||||
{
|
||||
var episodes = All();
|
||||
return episodes.ToDictionary(e => e.Id, e => e.Path);
|
||||
return Query(e => e.Monitored);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.TV.Events;
|
||||
|
||||
|
|
@ -12,65 +11,81 @@ public interface IEpisodeService
|
|||
Episode GetEpisode(int episodeId);
|
||||
List<Episode> GetEpisodes(IEnumerable<int> episodeIds);
|
||||
List<Episode> GetEpisodesByTVShowId(int tvShowId);
|
||||
List<Episode> GetEpisodesBySeasonId(int seasonId);
|
||||
List<Episode> GetEpisodesByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber);
|
||||
Episode FindByTVShowIdAndEpisode(int tvShowId, int seasonNumber, int episodeNumber);
|
||||
Episode FindByTVShowIdAndAbsoluteNumber(int tvShowId, int absoluteNumber);
|
||||
Episode FindByAirDate(int tvShowId, string airDate);
|
||||
List<Episode> GetEpisodesBySeason(int tvShowId, int seasonNumber);
|
||||
List<Episode> FindBySeasonAndEpisode(int tvShowId, int seasonNumber, int[] episodeNumbers);
|
||||
List<Episode> FindByAbsoluteEpisodeNumber(int tvShowId, int[] absoluteEpisodeNumbers);
|
||||
List<Episode> GetEpisodesBySeasonId(int seasonId);
|
||||
Episode GetEpisode(int tvShowId, int seasonNumber, int episodeNumber);
|
||||
Episode GetEpisodeByAbsoluteNumber(int tvShowId, int absoluteNumber);
|
||||
List<Episode> GetEpisodesByAirDate(int tvShowId, DateTime airDate);
|
||||
Episode AddEpisode(Episode newEpisode);
|
||||
List<Episode> AddEpisodes(List<Episode> newEpisodes);
|
||||
void DeleteEpisode(int episodeId, bool deleteFiles);
|
||||
void DeleteEpisodes(List<int> episodeIds, bool deleteFiles);
|
||||
void DeleteEpisode(int episodeId);
|
||||
Episode UpdateEpisode(Episode episode);
|
||||
List<Episode> UpdateEpisodes(List<Episode> episodes);
|
||||
List<Episode> GetEpisodesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
|
||||
Episode FindByPath(string path);
|
||||
Dictionary<int, string> AllEpisodePaths();
|
||||
|
||||
List<Episode> GetEpisodesBySeason(int tvShowId, int seasonNumber);
|
||||
Episode FindByAirDate(int tvShowId, string airDate);
|
||||
List<Episode> FindByAbsoluteEpisodeNumber(int tvShowId, IEnumerable<int> absoluteNumbers);
|
||||
List<Episode> FindBySeasonAndEpisode(int tvShowId, int seasonNumber, IEnumerable<int> episodeNumbers);
|
||||
}
|
||||
|
||||
public class EpisodeService : IEpisodeService
|
||||
{
|
||||
private readonly IEpisodeRepository _episodeRepository;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public EpisodeService(IEpisodeRepository episodeRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
public EpisodeService(
|
||||
IEpisodeRepository episodeRepository,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
_episodeRepository = episodeRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Episode GetEpisode(int episodeId) => _episodeRepository.Get(episodeId);
|
||||
public List<Episode> GetEpisodes(IEnumerable<int> episodeIds) => _episodeRepository.Get(episodeIds).ToList();
|
||||
public List<Episode> GetEpisodesByTVShowId(int tvShowId) => _episodeRepository.FindByTVShowId(tvShowId);
|
||||
public List<Episode> GetEpisodesBySeasonId(int seasonId) => _episodeRepository.FindBySeasonId(seasonId);
|
||||
public Episode GetEpisode(int episodeId)
|
||||
{
|
||||
return _episodeRepository.Get(episodeId);
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodes(IEnumerable<int> episodeIds)
|
||||
{
|
||||
return _episodeRepository.Get(episodeIds).ToList();
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodesByTVShowId(int tvShowId)
|
||||
{
|
||||
return _episodeRepository.FindByTVShowId(tvShowId);
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodesByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber)
|
||||
=> _episodeRepository.FindByTVShowIdAndSeasonNumber(tvShowId, seasonNumber);
|
||||
public Episode FindByTVShowIdAndEpisode(int tvShowId, int seasonNumber, int episodeNumber)
|
||||
=> _episodeRepository.FindByTVShowIdAndEpisode(tvShowId, seasonNumber, episodeNumber);
|
||||
public Episode FindByTVShowIdAndAbsoluteNumber(int tvShowId, int absoluteNumber)
|
||||
=> _episodeRepository.FindByTVShowIdAndAbsoluteNumber(tvShowId, absoluteNumber);
|
||||
public Episode FindByAirDate(int tvShowId, string airDate)
|
||||
=> _episodeRepository.FindByAirDate(tvShowId, airDate);
|
||||
public List<Episode> GetEpisodesBySeason(int tvShowId, int seasonNumber)
|
||||
=> GetEpisodesByTVShowIdAndSeasonNumber(tvShowId, seasonNumber);
|
||||
public List<Episode> FindBySeasonAndEpisode(int tvShowId, int seasonNumber, int[] episodeNumbers)
|
||||
=> _episodeRepository.FindBySeasonAndEpisode(tvShowId, seasonNumber, episodeNumbers);
|
||||
public List<Episode> FindByAbsoluteEpisodeNumber(int tvShowId, int[] absoluteEpisodeNumbers)
|
||||
=> _episodeRepository.FindByAbsoluteEpisodeNumber(tvShowId, absoluteEpisodeNumbers);
|
||||
public Episode FindByPath(string path) => _episodeRepository.FindByPath(path);
|
||||
public Dictionary<int, string> AllEpisodePaths() => _episodeRepository.AllEpisodePaths();
|
||||
public List<Episode> GetEpisodesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
|
||||
=> _episodeRepository.EpisodesBetweenDates(start, end, includeUnmonitored);
|
||||
{
|
||||
return _episodeRepository.FindByTVShowId(tvShowId)
|
||||
.Where(e => e.SeasonNumber == seasonNumber)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodesBySeasonId(int seasonId)
|
||||
{
|
||||
return _episodeRepository.FindBySeasonId(seasonId);
|
||||
}
|
||||
|
||||
public Episode GetEpisode(int tvShowId, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _episodeRepository.FindByTVShowIdAndEpisodeNumber(tvShowId, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
public Episode GetEpisodeByAbsoluteNumber(int tvShowId, int absoluteNumber)
|
||||
{
|
||||
return _episodeRepository.FindByTVShowIdAndAbsoluteNumber(tvShowId, absoluteNumber);
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodesByAirDate(int tvShowId, DateTime airDate)
|
||||
{
|
||||
return _episodeRepository.FindByAirDate(tvShowId, airDate);
|
||||
}
|
||||
|
||||
public Episode AddEpisode(Episode newEpisode)
|
||||
{
|
||||
newEpisode.Added = DateTime.UtcNow;
|
||||
var episode = _episodeRepository.Insert(newEpisode);
|
||||
_eventAggregator.PublishEvent(new EpisodeAddedEvent(episode));
|
||||
return episode;
|
||||
|
|
@ -78,6 +93,12 @@ public Episode AddEpisode(Episode newEpisode)
|
|||
|
||||
public List<Episode> AddEpisodes(List<Episode> newEpisodes)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var episode in newEpisodes)
|
||||
{
|
||||
episode.Added = now;
|
||||
}
|
||||
|
||||
_episodeRepository.InsertMany(newEpisodes);
|
||||
|
||||
foreach (var episode in newEpisodes)
|
||||
|
|
@ -88,30 +109,19 @@ public List<Episode> AddEpisodes(List<Episode> newEpisodes)
|
|||
return newEpisodes;
|
||||
}
|
||||
|
||||
public void DeleteEpisode(int episodeId, bool deleteFiles)
|
||||
public void DeleteEpisode(int episodeId)
|
||||
{
|
||||
var episode = _episodeRepository.Get(episodeId);
|
||||
_episodeRepository.Delete(episodeId);
|
||||
_eventAggregator.PublishEvent(new EpisodeDeletedEvent(episode, deleteFiles));
|
||||
}
|
||||
|
||||
public void DeleteEpisodes(List<int> episodeIds, bool deleteFiles)
|
||||
{
|
||||
var episodes = _episodeRepository.Get(episodeIds).ToList();
|
||||
_episodeRepository.DeleteMany(episodeIds);
|
||||
|
||||
foreach (var episode in episodes)
|
||||
{
|
||||
_eventAggregator.PublishEvent(new EpisodeDeletedEvent(episode, deleteFiles));
|
||||
}
|
||||
_eventAggregator.PublishEvent(new EpisodeDeletedEvent(episode));
|
||||
}
|
||||
|
||||
public Episode UpdateEpisode(Episode episode)
|
||||
{
|
||||
var storedEpisode = _episodeRepository.Get(episode.Id);
|
||||
_episodeRepository.Update(episode);
|
||||
_eventAggregator.PublishEvent(new EpisodeEditedEvent(episode, storedEpisode));
|
||||
return episode;
|
||||
var existingEpisode = _episodeRepository.Get(episode.Id);
|
||||
var updatedEpisode = _episodeRepository.Update(episode);
|
||||
_eventAggregator.PublishEvent(new EpisodeEditedEvent(updatedEpisode, existingEpisode));
|
||||
return updatedEpisode;
|
||||
}
|
||||
|
||||
public List<Episode> UpdateEpisodes(List<Episode> episodes)
|
||||
|
|
@ -120,5 +130,50 @@ public List<Episode> UpdateEpisodes(List<Episode> episodes)
|
|||
_eventAggregator.PublishEvent(new EpisodesBulkEditedEvent(episodes));
|
||||
return episodes;
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodesBySeason(int tvShowId, int seasonNumber)
|
||||
{
|
||||
return GetEpisodesByTVShowIdAndSeasonNumber(tvShowId, seasonNumber);
|
||||
}
|
||||
|
||||
public Episode FindByAirDate(int tvShowId, string airDate)
|
||||
{
|
||||
if (!DateTime.TryParse(airDate, out var parsedDate))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _episodeRepository.FindByAirDate(tvShowId, parsedDate).FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<Episode> FindByAbsoluteEpisodeNumber(int tvShowId, IEnumerable<int> absoluteNumbers)
|
||||
{
|
||||
var episodes = new List<Episode>();
|
||||
foreach (var absNum in absoluteNumbers)
|
||||
{
|
||||
var episode = _episodeRepository.FindByTVShowIdAndAbsoluteNumber(tvShowId, absNum);
|
||||
if (episode != null)
|
||||
{
|
||||
episodes.Add(episode);
|
||||
}
|
||||
}
|
||||
|
||||
return episodes;
|
||||
}
|
||||
|
||||
public List<Episode> FindBySeasonAndEpisode(int tvShowId, int seasonNumber, IEnumerable<int> episodeNumbers)
|
||||
{
|
||||
var episodes = new List<Episode>();
|
||||
foreach (var epNum in episodeNumbers)
|
||||
{
|
||||
var episode = _episodeRepository.FindByTVShowIdAndEpisodeNumber(tvShowId, seasonNumber, epNum);
|
||||
if (episode != null)
|
||||
{
|
||||
episodes.Add(episode);
|
||||
}
|
||||
}
|
||||
|
||||
return episodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@ namespace NzbDrone.Core.TV.Events
|
|||
public class EpisodeDeletedEvent : IEvent
|
||||
{
|
||||
public Episode Episode { get; private set; }
|
||||
public bool DeleteFiles { get; private set; }
|
||||
|
||||
public EpisodeDeletedEvent(Episode episode, bool deleteFiles)
|
||||
public EpisodeDeletedEvent(Episode episode)
|
||||
{
|
||||
Episode = episode;
|
||||
DeleteFiles = deleteFiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
src/NzbDrone.Core/TV/Events/SeasonAddedEvent.cs
Normal file
14
src/NzbDrone.Core/TV/Events/SeasonAddedEvent.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.TV.Events
|
||||
{
|
||||
public class SeasonAddedEvent : IEvent
|
||||
{
|
||||
public Season Season { get; private set; }
|
||||
|
||||
public SeasonAddedEvent(Season season)
|
||||
{
|
||||
Season = season;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/NzbDrone.Core/TV/Events/SeasonDeletedEvent.cs
Normal file
14
src/NzbDrone.Core/TV/Events/SeasonDeletedEvent.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.TV.Events
|
||||
{
|
||||
public class SeasonDeletedEvent : IEvent
|
||||
{
|
||||
public Season Season { get; private set; }
|
||||
|
||||
public SeasonDeletedEvent(Season season)
|
||||
{
|
||||
Season = season;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/NzbDrone.Core/TV/Events/SeasonEditedEvent.cs
Normal file
16
src/NzbDrone.Core/TV/Events/SeasonEditedEvent.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.TV.Events
|
||||
{
|
||||
public class SeasonEditedEvent : IEvent
|
||||
{
|
||||
public Season Season { get; private set; }
|
||||
public Season OldSeason { get; private set; }
|
||||
|
||||
public SeasonEditedEvent(Season season, Season oldSeason)
|
||||
{
|
||||
Season = season;
|
||||
OldSeason = oldSeason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,17 +4,15 @@ namespace NzbDrone.Core.TV
|
|||
{
|
||||
public class Season : ModelBase
|
||||
{
|
||||
public int TVShowId { get; set; }
|
||||
public int? TVShowId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
public string Overview { get; set; }
|
||||
|
||||
public bool Monitored { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return SeasonNumber == 0 ? "Specials" : $"Season {SeasonNumber}";
|
||||
return $"Season {SeasonNumber}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ public interface ISeasonRepository : IBasicRepository<Season>
|
|||
|
||||
public class SeasonRepository : BasicRepository<Season>, ISeasonRepository
|
||||
{
|
||||
public SeasonRepository(IMainDatabase database,
|
||||
IEventAggregator eventAggregator)
|
||||
public SeasonRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Monitoring;
|
||||
using NzbDrone.Core.TV.Events;
|
||||
|
||||
namespace NzbDrone.Core.TV
|
||||
{
|
||||
|
|
@ -10,63 +11,75 @@ public interface ISeasonService
|
|||
Season GetSeason(int seasonId);
|
||||
List<Season> GetSeasons(IEnumerable<int> seasonIds);
|
||||
List<Season> GetSeasonsByTVShowId(int tvShowId);
|
||||
Season FindByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber);
|
||||
Season GetSeasonByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber);
|
||||
Season AddSeason(Season newSeason);
|
||||
List<Season> AddSeasons(List<Season> newSeasons);
|
||||
void DeleteSeason(int seasonId);
|
||||
void DeleteSeasons(List<int> seasonIds);
|
||||
Season UpdateSeason(Season season);
|
||||
List<Season> UpdateSeasons(List<Season> seasons);
|
||||
List<Season> GetMonitored();
|
||||
}
|
||||
|
||||
public class SeasonService : ISeasonService
|
||||
{
|
||||
private readonly ISeasonRepository _seasonRepository;
|
||||
private readonly IHierarchicalMonitoringService _hierarchicalMonitoringService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SeasonService(ISeasonRepository seasonRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
public SeasonService(
|
||||
ISeasonRepository seasonRepository,
|
||||
IHierarchicalMonitoringService hierarchicalMonitoringService,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
_seasonRepository = seasonRepository;
|
||||
_hierarchicalMonitoringService = hierarchicalMonitoringService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Season GetSeason(int seasonId) => _seasonRepository.Get(seasonId);
|
||||
public List<Season> GetSeasons(IEnumerable<int> seasonIds) => _seasonRepository.Get(seasonIds).ToList();
|
||||
public List<Season> GetSeasonsByTVShowId(int tvShowId) => _seasonRepository.FindByTVShowId(tvShowId);
|
||||
public Season FindByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber)
|
||||
=> _seasonRepository.FindByTVShowIdAndSeasonNumber(tvShowId, seasonNumber);
|
||||
public List<Season> GetMonitored() => _seasonRepository.GetMonitored();
|
||||
public Season GetSeason(int seasonId)
|
||||
{
|
||||
return _seasonRepository.Get(seasonId);
|
||||
}
|
||||
|
||||
public List<Season> GetSeasons(IEnumerable<int> seasonIds)
|
||||
{
|
||||
return _seasonRepository.Get(seasonIds).ToList();
|
||||
}
|
||||
|
||||
public List<Season> GetSeasonsByTVShowId(int tvShowId)
|
||||
{
|
||||
return _seasonRepository.FindByTVShowId(tvShowId);
|
||||
}
|
||||
|
||||
public Season GetSeasonByTVShowIdAndSeasonNumber(int tvShowId, int seasonNumber)
|
||||
{
|
||||
return _seasonRepository.FindByTVShowIdAndSeasonNumber(tvShowId, seasonNumber);
|
||||
}
|
||||
|
||||
public Season AddSeason(Season newSeason)
|
||||
{
|
||||
return _seasonRepository.Insert(newSeason);
|
||||
}
|
||||
|
||||
public List<Season> AddSeasons(List<Season> newSeasons)
|
||||
{
|
||||
_seasonRepository.InsertMany(newSeasons);
|
||||
return newSeasons;
|
||||
var season = _seasonRepository.Insert(newSeason);
|
||||
_eventAggregator.PublishEvent(new SeasonAddedEvent(season));
|
||||
return season;
|
||||
}
|
||||
|
||||
public void DeleteSeason(int seasonId)
|
||||
{
|
||||
var season = _seasonRepository.Get(seasonId);
|
||||
_seasonRepository.Delete(seasonId);
|
||||
}
|
||||
|
||||
public void DeleteSeasons(List<int> seasonIds)
|
||||
{
|
||||
_seasonRepository.DeleteMany(seasonIds);
|
||||
_eventAggregator.PublishEvent(new SeasonDeletedEvent(season));
|
||||
}
|
||||
|
||||
public Season UpdateSeason(Season season)
|
||||
{
|
||||
_seasonRepository.Update(season);
|
||||
return season;
|
||||
var existingSeason = _seasonRepository.Get(season.Id);
|
||||
|
||||
if (existingSeason.Monitored != season.Monitored)
|
||||
{
|
||||
_hierarchicalMonitoringService.SetSeasonMonitored(season.Id, season.Monitored);
|
||||
}
|
||||
|
||||
var updatedSeason = _seasonRepository.Update(season);
|
||||
_eventAggregator.PublishEvent(new SeasonEditedEvent(updatedSeason, existingSeason));
|
||||
return updatedSeason;
|
||||
}
|
||||
|
||||
public List<Season> UpdateSeasons(List<Season> seasons)
|
||||
|
|
|
|||
|
|
@ -3,34 +3,47 @@ namespace NzbDrone.Core.TV
|
|||
public enum StreamingSource
|
||||
{
|
||||
Unknown = 0,
|
||||
Amazon, // AMZN
|
||||
Netflix, // NF
|
||||
Disney, // DSNP
|
||||
AppleTV, // ATVP
|
||||
Hulu, // HULU
|
||||
HBO, // HBO, HMAX
|
||||
Peacock, // PCOK
|
||||
Paramount, // PMTP
|
||||
CrunchyRoll, // CR
|
||||
Funimation, // FUNI
|
||||
Hidive, // HIDV
|
||||
VRV, // VRV
|
||||
Rakuten, // RKTN
|
||||
ITunes, // iTunes
|
||||
Vudu, // VUDU
|
||||
Stan, // STAN
|
||||
BBC, // iP
|
||||
ITV, // ITV
|
||||
All4, // 4OD
|
||||
Now, // NOW
|
||||
Canal, // CANAL
|
||||
Wakanim, // WAKA
|
||||
DCUniverse, // DCU
|
||||
Quibi, // QIBI
|
||||
Spectrum, // SPEC
|
||||
Showtime, // SHO
|
||||
Starz, // STRP
|
||||
TVLand, // TVLAND
|
||||
BritBox // BRTBX
|
||||
Amazon,
|
||||
Netflix,
|
||||
Disney,
|
||||
Hulu,
|
||||
AppleTV,
|
||||
Peacock,
|
||||
HBO,
|
||||
HBOMax,
|
||||
Paramount,
|
||||
Crunchyroll,
|
||||
CrunchyRoll,
|
||||
Funimation,
|
||||
Hidive,
|
||||
VRV,
|
||||
YouTube,
|
||||
Tubi,
|
||||
Pluto,
|
||||
Roku,
|
||||
ITV,
|
||||
ITVX,
|
||||
BBC,
|
||||
Channel4,
|
||||
All4,
|
||||
Stan,
|
||||
Binge,
|
||||
Crave,
|
||||
SkyShowtime,
|
||||
Discovery,
|
||||
Showtime,
|
||||
Starz,
|
||||
AMC,
|
||||
BritBox,
|
||||
Acorn,
|
||||
Rakuten,
|
||||
ITunes,
|
||||
Vudu,
|
||||
Now,
|
||||
Canal,
|
||||
Wakanim,
|
||||
DCUniverse,
|
||||
Quibi,
|
||||
Spectrum
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public TVShow()
|
|||
Genres = new List<string>();
|
||||
}
|
||||
|
||||
public int TvdbId { get; set; }
|
||||
public int? TvdbId { get; set; }
|
||||
public int? TmdbId { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
public int? AniDbId { get; set; }
|
||||
|
|
@ -23,13 +23,11 @@ public TVShow()
|
|||
public string Overview { get; set; }
|
||||
public string Network { get; set; }
|
||||
public TVShowStatus Status { get; set; }
|
||||
|
||||
public int? Runtime { get; set; }
|
||||
public string AirTime { get; set; }
|
||||
public string Certification { get; set; }
|
||||
public DateTime? FirstAired { get; set; }
|
||||
public int Year { get; set; }
|
||||
|
||||
public List<string> Genres { get; set; }
|
||||
public string OriginalLanguage { get; set; }
|
||||
|
||||
|
|
@ -41,11 +39,10 @@ public TVShow()
|
|||
public string RootFolderPath { get; set; }
|
||||
public int QualityProfileId { get; set; }
|
||||
public bool SeasonFolder { get; set; }
|
||||
|
||||
public bool Monitored { get; set; }
|
||||
public bool MonitorNewItems { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
public DateTime Added { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
public DateTime? LastSearchTime { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
|||
|
|
@ -7,70 +7,43 @@ namespace NzbDrone.Core.TV
|
|||
{
|
||||
public interface ITVShowRepository : IBasicRepository<TVShow>
|
||||
{
|
||||
bool TVShowPathExists(string path);
|
||||
TVShow FindByTitle(string title);
|
||||
TVShow FindByTvdbId(int tvdbId);
|
||||
TVShow FindByImdbId(string imdbId);
|
||||
TVShow FindByAniDbId(int aniDbId);
|
||||
TVShow FindByTitle(string title);
|
||||
TVShow FindByPath(string path);
|
||||
List<TVShow> GetMonitored();
|
||||
Dictionary<int, string> AllTVShowPaths();
|
||||
Dictionary<int, List<int>> AllTVShowTags();
|
||||
bool TVShowPathExists(string path);
|
||||
}
|
||||
|
||||
public class TVShowRepository : BasicRepository<TVShow>, ITVShowRepository
|
||||
{
|
||||
public TVShowRepository(IMainDatabase database,
|
||||
IEventAggregator eventAggregator)
|
||||
public TVShowRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
|
||||
public bool TVShowPathExists(string path)
|
||||
public TVShow FindByTitle(string title)
|
||||
{
|
||||
return Query(s => s.Path == path).Any();
|
||||
return Query(t => t.Title == title).FirstOrDefault();
|
||||
}
|
||||
|
||||
public TVShow FindByTvdbId(int tvdbId)
|
||||
{
|
||||
return Query(s => s.TvdbId == tvdbId).FirstOrDefault();
|
||||
return Query(t => t.TvdbId == tvdbId).FirstOrDefault();
|
||||
}
|
||||
|
||||
public TVShow FindByImdbId(string imdbId)
|
||||
{
|
||||
return Query(s => s.ImdbId == imdbId).FirstOrDefault();
|
||||
}
|
||||
|
||||
public TVShow FindByAniDbId(int aniDbId)
|
||||
{
|
||||
return Query(s => s.AniDbId == aniDbId).FirstOrDefault();
|
||||
}
|
||||
|
||||
public TVShow FindByTitle(string title)
|
||||
{
|
||||
return Query(s => s.Title == title).FirstOrDefault();
|
||||
}
|
||||
|
||||
public TVShow FindByPath(string path)
|
||||
{
|
||||
return Query(s => s.Path == path).FirstOrDefault();
|
||||
return Query(t => t.ImdbId == imdbId).FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<TVShow> GetMonitored()
|
||||
{
|
||||
return Query(s => s.Monitored);
|
||||
return Query(t => t.Monitored);
|
||||
}
|
||||
|
||||
public Dictionary<int, string> AllTVShowPaths()
|
||||
public bool TVShowPathExists(string path)
|
||||
{
|
||||
var tvShows = All();
|
||||
return tvShows.ToDictionary(s => s.Id, s => s.Path);
|
||||
}
|
||||
|
||||
public Dictionary<int, List<int>> AllTVShowTags()
|
||||
{
|
||||
var tvShows = All();
|
||||
return tvShows.ToDictionary(s => s.Id, s => s.Tags.ToList());
|
||||
return Query(t => t.Path == path).Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Monitoring;
|
||||
using NzbDrone.Core.TV.Events;
|
||||
|
||||
namespace NzbDrone.Core.TV
|
||||
|
|
@ -12,42 +13,47 @@ public interface ITVShowService
|
|||
List<TVShow> GetTVShows(IEnumerable<int> tvShowIds);
|
||||
TVShow AddTVShow(TVShow newTVShow);
|
||||
List<TVShow> AddTVShows(List<TVShow> newTVShows);
|
||||
TVShow FindByTitle(string title);
|
||||
TVShow FindByTvdbId(int tvdbId);
|
||||
TVShow FindByImdbId(string imdbId);
|
||||
TVShow FindByAniDbId(int aniDbId);
|
||||
TVShow FindByTitle(string title);
|
||||
TVShow FindByPath(string path);
|
||||
List<TVShow> GetMonitored();
|
||||
Dictionary<int, string> AllTVShowPaths();
|
||||
void DeleteTVShow(int tvShowId, bool deleteFiles);
|
||||
void DeleteTVShows(List<int> tvShowIds, bool deleteFiles);
|
||||
List<TVShow> GetAllTVShows();
|
||||
Dictionary<int, List<int>> AllTVShowTags();
|
||||
List<TVShow> GetMonitoredTVShows();
|
||||
TVShow UpdateTVShow(TVShow tvShow);
|
||||
List<TVShow> UpdateTVShows(List<TVShow> tvShows);
|
||||
bool TVShowPathExists(string folder);
|
||||
bool TVShowPathExists(string path);
|
||||
}
|
||||
|
||||
public class TVShowService : ITVShowService
|
||||
{
|
||||
private readonly ITVShowRepository _tvShowRepository;
|
||||
private readonly IHierarchicalMonitoringService _hierarchicalMonitoringService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public TVShowService(ITVShowRepository tvShowRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
public TVShowService(
|
||||
ITVShowRepository tvShowRepository,
|
||||
IHierarchicalMonitoringService hierarchicalMonitoringService,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
_tvShowRepository = tvShowRepository;
|
||||
_hierarchicalMonitoringService = hierarchicalMonitoringService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public TVShow GetTVShow(int tvShowId) => _tvShowRepository.Get(tvShowId);
|
||||
public List<TVShow> GetTVShows(IEnumerable<int> tvShowIds) => _tvShowRepository.Get(tvShowIds).ToList();
|
||||
public TVShow GetTVShow(int tvShowId)
|
||||
{
|
||||
return _tvShowRepository.Get(tvShowId);
|
||||
}
|
||||
|
||||
public List<TVShow> GetTVShows(IEnumerable<int> tvShowIds)
|
||||
{
|
||||
return _tvShowRepository.Get(tvShowIds).ToList();
|
||||
}
|
||||
|
||||
public TVShow AddTVShow(TVShow newTVShow)
|
||||
{
|
||||
newTVShow.Added = DateTime.UtcNow;
|
||||
var tvShow = _tvShowRepository.Insert(newTVShow);
|
||||
_eventAggregator.PublishEvent(new TVShowAddedEvent(tvShow));
|
||||
return tvShow;
|
||||
|
|
@ -55,6 +61,12 @@ public TVShow AddTVShow(TVShow newTVShow)
|
|||
|
||||
public List<TVShow> AddTVShows(List<TVShow> newTVShows)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var tvShow in newTVShows)
|
||||
{
|
||||
tvShow.Added = now;
|
||||
}
|
||||
|
||||
_tvShowRepository.InsertMany(newTVShows);
|
||||
|
||||
foreach (var tvShow in newTVShows)
|
||||
|
|
@ -65,16 +77,20 @@ public List<TVShow> AddTVShows(List<TVShow> newTVShows)
|
|||
return newTVShows;
|
||||
}
|
||||
|
||||
public TVShow FindByTvdbId(int tvdbId) => _tvShowRepository.FindByTvdbId(tvdbId);
|
||||
public TVShow FindByImdbId(string imdbId) => _tvShowRepository.FindByImdbId(imdbId);
|
||||
public TVShow FindByAniDbId(int aniDbId) => _tvShowRepository.FindByAniDbId(aniDbId);
|
||||
public TVShow FindByTitle(string title) => _tvShowRepository.FindByTitle(title);
|
||||
public TVShow FindByPath(string path) => _tvShowRepository.FindByPath(path);
|
||||
public List<TVShow> GetMonitored() => _tvShowRepository.GetMonitored();
|
||||
public Dictionary<int, string> AllTVShowPaths() => _tvShowRepository.AllTVShowPaths();
|
||||
public Dictionary<int, List<int>> AllTVShowTags() => _tvShowRepository.AllTVShowTags();
|
||||
public bool TVShowPathExists(string folder) => _tvShowRepository.TVShowPathExists(folder);
|
||||
public List<TVShow> GetAllTVShows() => _tvShowRepository.All().ToList();
|
||||
public TVShow FindByTitle(string title)
|
||||
{
|
||||
return _tvShowRepository.FindByTitle(title);
|
||||
}
|
||||
|
||||
public TVShow FindByTvdbId(int tvdbId)
|
||||
{
|
||||
return _tvShowRepository.FindByTvdbId(tvdbId);
|
||||
}
|
||||
|
||||
public TVShow FindByImdbId(string imdbId)
|
||||
{
|
||||
return _tvShowRepository.FindByImdbId(imdbId);
|
||||
}
|
||||
|
||||
public void DeleteTVShow(int tvShowId, bool deleteFiles)
|
||||
{
|
||||
|
|
@ -94,19 +110,39 @@ public void DeleteTVShows(List<int> tvShowIds, bool deleteFiles)
|
|||
}
|
||||
}
|
||||
|
||||
public List<TVShow> GetAllTVShows()
|
||||
{
|
||||
return _tvShowRepository.All().ToList();
|
||||
}
|
||||
|
||||
public List<TVShow> GetMonitoredTVShows()
|
||||
{
|
||||
return _tvShowRepository.GetMonitored();
|
||||
}
|
||||
|
||||
public TVShow UpdateTVShow(TVShow tvShow)
|
||||
{
|
||||
var storedTVShow = _tvShowRepository.Get(tvShow.Id);
|
||||
_tvShowRepository.Update(tvShow);
|
||||
_eventAggregator.PublishEvent(new TVShowEditedEvent(tvShow, storedTVShow));
|
||||
return tvShow;
|
||||
var existingTVShow = _tvShowRepository.Get(tvShow.Id);
|
||||
|
||||
if (existingTVShow.Monitored != tvShow.Monitored)
|
||||
{
|
||||
_hierarchicalMonitoringService.SetTVShowMonitored(tvShow.Id, tvShow.Monitored);
|
||||
}
|
||||
|
||||
var updatedTVShow = _tvShowRepository.Update(tvShow);
|
||||
_eventAggregator.PublishEvent(new TVShowEditedEvent(updatedTVShow, existingTVShow));
|
||||
return updatedTVShow;
|
||||
}
|
||||
|
||||
public List<TVShow> UpdateTVShows(List<TVShow> tvShows)
|
||||
{
|
||||
_tvShowRepository.UpdateMany(tvShows);
|
||||
_eventAggregator.PublishEvent(new TVShowsBulkEditedEvent(tvShows));
|
||||
return tvShows;
|
||||
}
|
||||
|
||||
public bool TVShowPathExists(string path)
|
||||
{
|
||||
return _tvShowRepository.TVShowPathExists(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public static SeasonResource ToResource(this Season model)
|
|||
return new SeasonResource
|
||||
{
|
||||
Id = model.Id,
|
||||
TVShowId = model.TVShowId,
|
||||
TVShowId = model.TVShowId ?? 0,
|
||||
SeasonNumber = model.SeasonNumber,
|
||||
Title = model.Title,
|
||||
Overview = model.Overview,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public TVShowResource()
|
|||
Monitored = true;
|
||||
}
|
||||
|
||||
public int TvdbId { get; set; }
|
||||
public int? TvdbId { get; set; }
|
||||
public int? TmdbId { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
public int? AniDbId { get; set; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue