mirror of
https://github.com/Sonarr/Sonarr
synced 2026-05-09 05:40:53 +02:00
Add async overloaded methods for SeriesRepository
This commit is contained in:
parent
7daa508327
commit
e80cd90614
8 changed files with 299 additions and 36 deletions
|
|
@ -46,8 +46,8 @@ public void SetUp()
|
|||
_xemEpisodes = new List<Episode>();
|
||||
|
||||
Mocker.GetMock<ISeriesService>()
|
||||
.Setup(v => v.GetSeries(_xemSeries.Id))
|
||||
.Returns(_xemSeries);
|
||||
.Setup(v => v.GetSeriesAsync(_xemSeries.Id))
|
||||
.ReturnsAsync(_xemSeries);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Setup(v => v.GetEpisodesBySeason(_xemSeries.Id, It.IsAny<int>()))
|
||||
|
|
@ -205,8 +205,8 @@ public async Task Tags_IndexerAndSeriesTagsMatch_IndexerIncluded()
|
|||
.Build();
|
||||
|
||||
Mocker.GetMock<ISeriesService>()
|
||||
.Setup(v => v.GetSeries(_xemSeries.Id))
|
||||
.Returns(_xemSeries);
|
||||
.Setup(v => v.GetSeriesAsync(_xemSeries.Id))
|
||||
.ReturnsAsync(_xemSeries);
|
||||
|
||||
WithEpisodes();
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public async Task<List<DownloadDecision>> EpisodeSearch(int episodeId, bool user
|
|||
|
||||
public async Task<List<DownloadDecision>> EpisodeSearch(Episode episode, bool userInvokedSearch, bool interactiveSearch)
|
||||
{
|
||||
var series = _seriesService.GetSeries(episode.SeriesId);
|
||||
var series = await _seriesService.GetSeriesAsync(episode.SeriesId);
|
||||
|
||||
if (series.SeriesType == SeriesTypes.Daily)
|
||||
{
|
||||
|
|
@ -113,7 +113,7 @@ public async Task<List<DownloadDecision>> SeasonSearch(int seriesId, int seasonN
|
|||
|
||||
public async Task<List<DownloadDecision>> SeasonSearch(int seriesId, int seasonNumber, List<Episode> episodes, bool monitoredOnly, bool userInvokedSearch, bool interactiveSearch)
|
||||
{
|
||||
var series = _seriesService.GetSeries(seriesId);
|
||||
var series = await _seriesService.GetSeriesAsync(seriesId);
|
||||
|
||||
if (series.SeriesType == SeriesTypes.Anime)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
|
|
@ -17,6 +19,7 @@ namespace NzbDrone.Core.Tv
|
|||
public interface IAddSeriesService
|
||||
{
|
||||
Series AddSeries(Series newSeries);
|
||||
Task<Series> AddSeriesAsync(Series newSeries, CancellationToken cancellationToken = default);
|
||||
List<Series> AddSeries(List<Series> newSeries, bool ignoreErrors = false);
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +57,19 @@ public Series AddSeries(Series newSeries)
|
|||
return newSeries;
|
||||
}
|
||||
|
||||
public async Task<Series> AddSeriesAsync(Series newSeries, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(newSeries);
|
||||
|
||||
newSeries = AddSkyhookData(newSeries);
|
||||
newSeries = SetPropertiesAndValidate(newSeries);
|
||||
|
||||
_logger.Info("Adding Series {0} Path: [{1}]", newSeries, newSeries.Path);
|
||||
await _seriesService.AddSeriesAsync(newSeries, cancellationToken);
|
||||
|
||||
return newSeries;
|
||||
}
|
||||
|
||||
public List<Series> AddSeries(List<Series> newSeries, bool ignoreErrors = false)
|
||||
{
|
||||
var added = DateTime.UtcNow;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
|
@ -9,17 +11,29 @@ namespace NzbDrone.Core.Tv
|
|||
public interface ISeriesRepository : IBasicRepository<Series>
|
||||
{
|
||||
bool SeriesPathExists(string path);
|
||||
Task<bool> SeriesPathExistsAsync(string path, CancellationToken cancellationToken = default);
|
||||
Series FindByTitle(string cleanTitle);
|
||||
Task<Series> FindByTitleAsync(string cleanTitle, CancellationToken cancellationToken = default);
|
||||
Series FindByTitle(string cleanTitle, int year);
|
||||
Task<Series> FindByTitleAsync(string cleanTitle, int year, CancellationToken cancellationToken = default);
|
||||
List<Series> FindByTitleInexact(string cleanTitle);
|
||||
Task<List<Series>> FindByTitleInexactAsync(string cleanTitle, CancellationToken cancellationToken = default);
|
||||
Series FindByTvdbId(int tvdbId);
|
||||
Task<Series> FindByTvdbIdAsync(int tvdbId, CancellationToken cancellationToken = default);
|
||||
Series FindByTvRageId(int tvRageId);
|
||||
Task<Series> FindByTvRageIdAsync(int tvRageId, CancellationToken cancellationToken = default);
|
||||
Series FindByImdbId(string imdbId);
|
||||
Task<Series> FindByImdbIdAsync(string imdbId, CancellationToken cancellationToken = default);
|
||||
Series FindByPath(string path);
|
||||
Task<Series> FindByPathAsync(string path, CancellationToken cancellationToken = default);
|
||||
List<int> AllSeriesTvdbIds();
|
||||
Task<List<int>> AllSeriesTvdbIdsAsync(CancellationToken cancellationToken = default);
|
||||
Dictionary<int, string> AllSeriesPaths();
|
||||
Task<Dictionary<int, string>> AllSeriesPathsAsync(CancellationToken cancellationToken = default);
|
||||
Dictionary<int, List<int>> AllSeriesTags();
|
||||
Task<Dictionary<int, List<int>>> AllSeriesTagsAsync(CancellationToken cancellationToken = default);
|
||||
Dictionary<int, int> AllSeriesQualityProfiles();
|
||||
Task<Dictionary<int, int>> AllSeriesQualityProfilesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public class SeriesRepository : BasicRepository<Series>, ISeriesRepository
|
||||
|
|
@ -34,6 +48,11 @@ public bool SeriesPathExists(string path)
|
|||
return Query(c => c.Path == path).Any();
|
||||
}
|
||||
|
||||
public async Task<bool> SeriesPathExistsAsync(string path, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await QueryAsync(c => c.Path == path, cancellationToken).AnyAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByTitle(string cleanTitle)
|
||||
{
|
||||
cleanTitle = cleanTitle.ToLowerInvariant();
|
||||
|
|
@ -44,6 +63,15 @@ public Series FindByTitle(string cleanTitle)
|
|||
return ReturnSingleSeriesOrThrow(series);
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTitleAsync(string cleanTitle, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cleanTitle = cleanTitle.ToLowerInvariant();
|
||||
|
||||
var series = await QueryAsync(s => s.CleanTitle == cleanTitle, cancellationToken).ToListAsync(cancellationToken);
|
||||
|
||||
return ReturnSingleSeriesOrThrow(series);
|
||||
}
|
||||
|
||||
public Series FindByTitle(string cleanTitle, int year)
|
||||
{
|
||||
cleanTitle = cleanTitle.ToLowerInvariant();
|
||||
|
|
@ -53,6 +81,15 @@ public Series FindByTitle(string cleanTitle, int year)
|
|||
return ReturnSingleSeriesOrThrow(series);
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTitleAsync(string cleanTitle, int year, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cleanTitle = cleanTitle.ToLowerInvariant();
|
||||
|
||||
var series = await QueryAsync(s => s.CleanTitle == cleanTitle && s.Year == year, cancellationToken).ToListAsync(cancellationToken);
|
||||
|
||||
return ReturnSingleSeriesOrThrow(series);
|
||||
}
|
||||
|
||||
public List<Series> FindByTitleInexact(string cleanTitle)
|
||||
{
|
||||
var builder = Builder().Where($"instr(@cleanTitle, \"Series\".\"CleanTitle\")", new { cleanTitle = cleanTitle });
|
||||
|
|
@ -65,27 +102,59 @@ public List<Series> FindByTitleInexact(string cleanTitle)
|
|||
return Query(builder).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<Series>> FindByTitleInexactAsync(string cleanTitle, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var builder = Builder().Where("instr(@cleanTitle, \"Series\".\"CleanTitle\")", new { cleanTitle = cleanTitle });
|
||||
|
||||
if (_database.DatabaseType == DatabaseType.PostgreSQL)
|
||||
{
|
||||
builder = Builder().Where("(strpos(@cleanTitle, \"Series\".\"CleanTitle\") > 0)", new { cleanTitle = cleanTitle });
|
||||
}
|
||||
|
||||
return await QueryAsync(builder, cancellationToken).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByTvdbId(int tvdbId)
|
||||
{
|
||||
return Query(s => s.TvdbId == tvdbId).SingleOrDefault();
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTvdbIdAsync(int tvdbId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await QueryAsync(s => s.TvdbId == tvdbId, cancellationToken).SingleOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByTvRageId(int tvRageId)
|
||||
{
|
||||
return Query(s => s.TvRageId == tvRageId).SingleOrDefault();
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTvRageIdAsync(int tvRageId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await QueryAsync(s => s.TvRageId == tvRageId, cancellationToken).SingleOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByImdbId(string imdbId)
|
||||
{
|
||||
return Query(s => s.ImdbId == imdbId).SingleOrDefault();
|
||||
}
|
||||
|
||||
public async Task<Series> FindByImdbIdAsync(string imdbId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await QueryAsync(s => s.ImdbId == imdbId, cancellationToken).SingleOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByPath(string path)
|
||||
{
|
||||
return Query(s => s.Path == path)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task<Series> FindByPathAsync(string path, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await QueryAsync(s => s.Path == path, cancellationToken).SingleOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public List<int> AllSeriesTvdbIds()
|
||||
{
|
||||
using (var conn = _database.OpenConnection())
|
||||
|
|
@ -94,6 +163,13 @@ public List<int> AllSeriesTvdbIds()
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<List<int>> AllSeriesTvdbIdsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var conn = await _database.OpenConnectionAsync(cancellationToken);
|
||||
|
||||
return await conn.QueryUnbufferedAsync<int>("SELECT \"TvdbId\" FROM \"Series\"").ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Dictionary<int, string> AllSeriesPaths()
|
||||
{
|
||||
using (var conn = _database.OpenConnection())
|
||||
|
|
@ -103,6 +179,14 @@ public Dictionary<int, string> AllSeriesPaths()
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<int, string>> AllSeriesPathsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var conn = await _database.OpenConnectionAsync(cancellationToken);
|
||||
|
||||
return await conn.QueryUnbufferedAsync<KeyValuePair<int, string>>("SELECT \"Id\" AS Key, \"Path\" AS Value FROM \"Series\"")
|
||||
.ToDictionaryAsync(x => x.Key, x => x.Value, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Dictionary<int, List<int>> AllSeriesTags()
|
||||
{
|
||||
using (var conn = _database.OpenConnection())
|
||||
|
|
@ -112,6 +196,14 @@ public Dictionary<int, List<int>> AllSeriesTags()
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<int, List<int>>> AllSeriesTagsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var conn = await _database.OpenConnectionAsync(cancellationToken);
|
||||
|
||||
return await conn.QueryUnbufferedAsync<KeyValuePair<int, List<int>>>("SELECT \"Id\" AS Key, \"Tags\" AS Value FROM \"Series\" WHERE \"Tags\" IS NOT NULL")
|
||||
.ToDictionaryAsync(x => x.Key, x => x.Value, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Dictionary<int, int> AllSeriesQualityProfiles()
|
||||
{
|
||||
using (var conn = _database.OpenConnection())
|
||||
|
|
@ -121,6 +213,14 @@ public Dictionary<int, int> AllSeriesQualityProfiles()
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<int, int>> AllSeriesQualityProfilesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var conn = await _database.OpenConnectionAsync(cancellationToken);
|
||||
|
||||
return await conn.QueryUnbufferedAsync<KeyValuePair<int, int>>("SELECT \"Id\" AS Key, \"QualityProfileId\" AS Value FROM \"Series\"")
|
||||
.ToDictionaryAsync(x => x.Key, x => x.Value, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
private Series ReturnSingleSeriesOrThrow(List<Series> series)
|
||||
{
|
||||
if (series.Count == 0)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.AutoTagging;
|
||||
|
|
@ -12,27 +14,46 @@ namespace NzbDrone.Core.Tv
|
|||
public interface ISeriesService
|
||||
{
|
||||
Series GetSeries(int seriesId);
|
||||
Task<Series> GetSeriesAsync(int seriesId, CancellationToken cancellationToken = default);
|
||||
List<Series> GetSeries(IEnumerable<int> seriesIds);
|
||||
IAsyncEnumerable<Series> GetSeriesAsync(IEnumerable<int> seriesIds, CancellationToken cancellationToken = default);
|
||||
Series AddSeries(Series newSeries);
|
||||
Task<Series> AddSeriesAsync(Series newSeries, CancellationToken cancellationToken = default);
|
||||
List<Series> AddSeries(List<Series> newSeries);
|
||||
Task<List<Series>> AddSeriesAsync(List<Series> newSeries, CancellationToken cancellationToken = default);
|
||||
Series FindByTvdbId(int tvdbId);
|
||||
Task<Series> FindByTvdbIdAsync(int tvRageId, CancellationToken cancellationToken = default);
|
||||
Series FindByTvRageId(int tvRageId);
|
||||
Task<Series> FindByTvRageIdAsync(int tvRageId, CancellationToken cancellationToken = default);
|
||||
Series FindByImdbId(string imdbId);
|
||||
Task<Series> FindByImdbIdAsync(string imdbId, CancellationToken cancellationToken = default);
|
||||
Series FindByTitle(string title);
|
||||
Task<Series> FindByTitleAsync(string title, CancellationToken cancellationToken = default);
|
||||
Series FindByTitle(string title, int year);
|
||||
Task<Series> FindByTitleAsync(string title, int year, CancellationToken cancellationToken = default);
|
||||
Series FindByTitleInexact(string title);
|
||||
Series FindByPath(string path);
|
||||
Task<Series> FindByPathAsync(string path, CancellationToken cancellationToken = default);
|
||||
void DeleteSeries(List<int> seriesIds, bool deleteFiles, bool addImportListExclusion);
|
||||
Task DeleteSeriesAsync(List<int> seriesIds, bool deleteFiles, bool addImportListExclusion, CancellationToken cancellationToken = default);
|
||||
List<Series> GetAllSeries();
|
||||
IAsyncEnumerable<Series> GetAllSeriesAsync(CancellationToken cancellationToken = default);
|
||||
List<int> AllSeriesTvdbIds();
|
||||
Task<List<int>> AllSeriesTvdbIdsAsync(CancellationToken cancellation = default);
|
||||
Dictionary<int, string> GetAllSeriesPaths();
|
||||
Task<Dictionary<int, string>> GetAllSeriesPathsAsync(CancellationToken cancellationToken = default);
|
||||
Dictionary<int, List<int>> GetAllSeriesTags();
|
||||
Task<Dictionary<int, List<int>>> GetAllSeriesTagsAsync(CancellationToken cancellationToken = default);
|
||||
List<Series> AllForTag(int tagId);
|
||||
IAsyncEnumerable<Series> AllForTagAsync(int tagId, CancellationToken cancellationToken = default);
|
||||
Dictionary<int, int> GetAllSeriesQualityProfiles();
|
||||
Task<Dictionary<int, int>> GetAllSeriesQualityProfilesAsync(CancellationToken cancellationToken = default);
|
||||
Series UpdateSeries(Series series, bool updateEpisodesToMatchSeason = true, bool publishUpdatedEvent = true);
|
||||
List<Series> UpdateSeries(List<Series> series, bool useExistingRelativeFolder);
|
||||
bool SeriesPathExists(string folder);
|
||||
Task<bool> SeriesPathExistsAsync(string folder, CancellationToken cancellationToken = default);
|
||||
void RemoveAddOptions(Series series);
|
||||
Task RemoveAddOptionsAsync(Series series, CancellationToken cancellationToken = default);
|
||||
bool UpdateTags(Series series);
|
||||
}
|
||||
|
||||
|
|
@ -65,11 +86,21 @@ public Series GetSeries(int seriesId)
|
|||
return _seriesRepository.Get(seriesId);
|
||||
}
|
||||
|
||||
public async Task<Series> GetSeriesAsync(int seriesId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.GetAsync(seriesId, cancellationToken);
|
||||
}
|
||||
|
||||
public List<Series> GetSeries(IEnumerable<int> seriesIds)
|
||||
{
|
||||
return _seriesRepository.Get(seriesIds).ToList();
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<Series> GetSeriesAsync(IEnumerable<int> seriesIds, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _seriesRepository.GetAsync(seriesIds, cancellationToken);
|
||||
}
|
||||
|
||||
public Series AddSeries(Series newSeries)
|
||||
{
|
||||
_seriesRepository.Insert(newSeries);
|
||||
|
|
@ -78,6 +109,14 @@ public Series AddSeries(Series newSeries)
|
|||
return newSeries;
|
||||
}
|
||||
|
||||
public async Task<Series> AddSeriesAsync(Series newSeries, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _seriesRepository.InsertAsync(newSeries, cancellationToken);
|
||||
_eventAggregator.PublishEvent(new SeriesAddedEvent(await GetSeriesAsync(newSeries.Id, cancellationToken)));
|
||||
|
||||
return newSeries;
|
||||
}
|
||||
|
||||
public List<Series> AddSeries(List<Series> newSeries)
|
||||
{
|
||||
_seriesRepository.InsertMany(newSeries);
|
||||
|
|
@ -86,26 +125,54 @@ public List<Series> AddSeries(List<Series> newSeries)
|
|||
return newSeries;
|
||||
}
|
||||
|
||||
public async Task<List<Series>> AddSeriesAsync(List<Series> newSeries, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _seriesRepository.InsertManyAsync(newSeries, cancellationToken);
|
||||
_eventAggregator.PublishEvent(new SeriesImportedEvent(newSeries.Select(s => s.Id).ToList()));
|
||||
|
||||
return newSeries;
|
||||
}
|
||||
|
||||
public Series FindByTvdbId(int tvRageId)
|
||||
{
|
||||
return _seriesRepository.FindByTvdbId(tvRageId);
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTvdbIdAsync(int tvRageId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.FindByTvdbIdAsync(tvRageId, cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByTvRageId(int tvRageId)
|
||||
{
|
||||
return _seriesRepository.FindByTvRageId(tvRageId);
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTvRageIdAsync(int tvRageId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.FindByTvRageIdAsync(tvRageId, cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByImdbId(string imdbId)
|
||||
{
|
||||
return _seriesRepository.FindByImdbId(imdbId);
|
||||
}
|
||||
|
||||
public async Task<Series> FindByImdbIdAsync(string imdbId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.FindByImdbIdAsync(imdbId, cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByTitle(string title)
|
||||
{
|
||||
return _seriesRepository.FindByTitle(title.CleanSeriesTitle());
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTitleAsync(string title, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.FindByTitleAsync(title.CleanSeriesTitle(), cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByTitleInexact(string title)
|
||||
{
|
||||
// find any series clean title within the provided release title
|
||||
|
|
@ -155,11 +222,21 @@ public Series FindByPath(string path)
|
|||
return _seriesRepository.FindByPath(path);
|
||||
}
|
||||
|
||||
public async Task<Series> FindByPathAsync(string path, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.FindByPathAsync(path, cancellationToken);
|
||||
}
|
||||
|
||||
public Series FindByTitle(string title, int year)
|
||||
{
|
||||
return _seriesRepository.FindByTitle(title.CleanSeriesTitle(), year);
|
||||
}
|
||||
|
||||
public async Task<Series> FindByTitleAsync(string title, int year, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.FindByTitleAsync(title.CleanSeriesTitle(), year, cancellationToken);
|
||||
}
|
||||
|
||||
public void DeleteSeries(List<int> seriesIds, bool deleteFiles, bool addImportListExclusion)
|
||||
{
|
||||
var series = _seriesRepository.Get(seriesIds).ToList();
|
||||
|
|
@ -167,37 +244,74 @@ public void DeleteSeries(List<int> seriesIds, bool deleteFiles, bool addImportLi
|
|||
_eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles, addImportListExclusion));
|
||||
}
|
||||
|
||||
public async Task DeleteSeriesAsync(List<int> seriesIds, bool deleteFiles, bool addImportListExclusion, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var series = await _seriesRepository.GetAsync(seriesIds, cancellationToken).ToListAsync(cancellationToken: cancellationToken);
|
||||
await _seriesRepository.DeleteManyAsync(seriesIds, cancellationToken);
|
||||
_eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles, addImportListExclusion));
|
||||
}
|
||||
|
||||
public List<Series> GetAllSeries()
|
||||
{
|
||||
return _seriesRepository.All().ToList();
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<Series> GetAllSeriesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _seriesRepository.AllAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public List<int> AllSeriesTvdbIds()
|
||||
{
|
||||
return _seriesRepository.AllSeriesTvdbIds().ToList();
|
||||
}
|
||||
|
||||
public async Task<List<int>> AllSeriesTvdbIdsAsync(CancellationToken cancellation = default)
|
||||
{
|
||||
return await _seriesRepository.AllSeriesTvdbIdsAsync(cancellation);
|
||||
}
|
||||
|
||||
public Dictionary<int, string> GetAllSeriesPaths()
|
||||
{
|
||||
return _seriesRepository.AllSeriesPaths();
|
||||
}
|
||||
|
||||
public async Task<Dictionary<int, string>> GetAllSeriesPathsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.AllSeriesPathsAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Dictionary<int, List<int>> GetAllSeriesTags()
|
||||
{
|
||||
return _seriesRepository.AllSeriesTags();
|
||||
}
|
||||
|
||||
public async Task<Dictionary<int, List<int>>> GetAllSeriesTagsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.AllSeriesTagsAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Dictionary<int, int> GetAllSeriesQualityProfiles()
|
||||
{
|
||||
return _seriesRepository.AllSeriesQualityProfiles();
|
||||
}
|
||||
|
||||
public async Task<Dictionary<int, int>> GetAllSeriesQualityProfilesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.AllSeriesQualityProfilesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public List<Series> AllForTag(int tagId)
|
||||
{
|
||||
return GetAllSeries().Where(s => s.Tags.Contains(tagId))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<Series> AllForTagAsync(int tagId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return GetAllSeriesAsync(cancellationToken).Where(s => s.Tags.Contains(tagId));
|
||||
}
|
||||
|
||||
// updateEpisodesToMatchSeason is an override for EpisodeMonitoredService to use so a change via Season pass doesn't get nuked by the seasons loop.
|
||||
// TODO: Remove when seasons are split from series (or we come up with a better way to address this)
|
||||
public Series UpdateSeries(Series series, bool updateEpisodesToMatchSeason = true, bool publishUpdatedEvent = true)
|
||||
|
|
@ -267,11 +381,23 @@ public bool SeriesPathExists(string folder)
|
|||
return _seriesRepository.SeriesPathExists(folder);
|
||||
}
|
||||
|
||||
public async Task<bool> SeriesPathExistsAsync(string folder, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _seriesRepository.SeriesPathExistsAsync(folder, cancellationToken);
|
||||
}
|
||||
|
||||
public void RemoveAddOptions(Series series)
|
||||
{
|
||||
_seriesRepository.SetFields(series, s => s.AddOptions);
|
||||
}
|
||||
|
||||
public async Task RemoveAddOptionsAsync(Series series, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await _seriesRepository.SetFieldsAsync(series, s => s.AddOptions);
|
||||
}
|
||||
|
||||
public bool UpdateTags(Series series)
|
||||
{
|
||||
_logger.Trace("Updating tags for {0}", series);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
@ -68,7 +69,7 @@ public ReleaseController(IFetchAndParseRss rssFetcherAndParser,
|
|||
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public async Task<object> DownloadRelease([FromBody] ReleaseResource release)
|
||||
public async Task<object> DownloadRelease([FromBody] ReleaseResource release, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release));
|
||||
|
||||
|
|
@ -105,7 +106,7 @@ public async Task<object> DownloadRelease([FromBody] ReleaseResource release)
|
|||
ReleaseSource = remoteEpisode.ReleaseSource
|
||||
};
|
||||
|
||||
remoteEpisode.Series = _seriesService.GetSeries(release.SeriesId!.Value);
|
||||
remoteEpisode.Series = await _seriesService.GetSeriesAsync(release.SeriesId!.Value, cancellationToken);
|
||||
remoteEpisode.Episodes = _episodeService.GetEpisodes(release.EpisodeIds);
|
||||
remoteEpisode.ParsedEpisodeInfo.Quality = release.Quality;
|
||||
remoteEpisode.Languages = release.Languages;
|
||||
|
|
@ -117,12 +118,12 @@ public async Task<object> DownloadRelease([FromBody] ReleaseResource release)
|
|||
{
|
||||
var episode = _episodeService.GetEpisode(release.EpisodeId.Value);
|
||||
|
||||
remoteEpisode.Series = _seriesService.GetSeries(episode.SeriesId);
|
||||
remoteEpisode.Series = await _seriesService.GetSeriesAsync(episode.SeriesId, cancellationToken);
|
||||
remoteEpisode.Episodes = new List<Episode> { episode };
|
||||
}
|
||||
else if (release.SeriesId.HasValue)
|
||||
{
|
||||
var series = _seriesService.GetSeries(release.SeriesId.Value);
|
||||
var series = await _seriesService.GetSeriesAsync(release.SeriesId.Value, cancellationToken);
|
||||
var episodes = _parsingService.GetEpisodes(remoteEpisode.ParsedEpisodeInfo, series, true);
|
||||
|
||||
if (episodes.Empty())
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ protected override ReleaseResource GetResourceById(int id)
|
|||
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public async Task<Results<Ok<ReleaseGrabResource>, NotFound>> DownloadRelease([FromBody] ReleaseGrabResource release)
|
||||
public async Task<Results<Ok<ReleaseGrabResource>, NotFound>> DownloadRelease([FromBody] ReleaseGrabResource release, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release));
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ public async Task<Results<Ok<ReleaseGrabResource>, NotFound>> DownloadRelease([F
|
|||
ReleaseSource = remoteEpisode.ReleaseSource
|
||||
};
|
||||
|
||||
remoteEpisode.Series = _seriesService.GetSeries(overrideInfo.SeriesId!.Value);
|
||||
remoteEpisode.Series = await _seriesService.GetSeriesAsync(overrideInfo.SeriesId!.Value, cancellationToken);
|
||||
remoteEpisode.Episodes = _episodeService.GetEpisodes(overrideInfo.EpisodeIds);
|
||||
remoteEpisode.ParsedEpisodeInfo.Quality = overrideInfo.Quality;
|
||||
remoteEpisode.Languages = overrideInfo.Languages;
|
||||
|
|
@ -136,12 +136,12 @@ public async Task<Results<Ok<ReleaseGrabResource>, NotFound>> DownloadRelease([F
|
|||
{
|
||||
var episode = _episodeService.GetEpisode(release.SearchInfo.EpisodeId.Value);
|
||||
|
||||
remoteEpisode.Series = _seriesService.GetSeries(episode.SeriesId);
|
||||
remoteEpisode.Series = await _seriesService.GetSeriesAsync(episode.SeriesId, cancellationToken);
|
||||
remoteEpisode.Episodes = new List<Episode> { episode };
|
||||
}
|
||||
else if (release.SearchInfo?.SeriesId.HasValue == true)
|
||||
{
|
||||
var series = _seriesService.GetSeries(release.SearchInfo.SeriesId.Value);
|
||||
var series = await _seriesService.GetSeriesAsync(release.SearchInfo.SeriesId.Value, cancellationToken);
|
||||
var episodes = _parsingService.GetEpisodes(remoteEpisode.ParsedEpisodeInfo, series, true);
|
||||
|
||||
if (episodes.Empty())
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
|
|
@ -109,27 +111,22 @@ public SeriesController(IBroadcastSignalRMessage signalRBroadcaster,
|
|||
|
||||
[HttpGet]
|
||||
[Produces("application/json")]
|
||||
public Ok<List<SeriesResource>> AllSeries(int? tvdbId, [FromQuery] SeriesSubresource[]? includeSubresources = null)
|
||||
public async IAsyncEnumerable<SeriesResource> AllSeries(int? tvdbId, [FromQuery] SeriesSubresource[]? includeSubresources = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
var seriesStats = _seriesStatisticsService.SeriesStatistics();
|
||||
var seriesResources = new List<SeriesResource>();
|
||||
var seriesStats = _seriesStatisticsService.SeriesStatistics().ToDictionary(x => x.SeriesId);
|
||||
var includeSeasonImages = includeSubresources.Contains(SeriesSubresource.SeasonImages);
|
||||
|
||||
if (tvdbId.HasValue)
|
||||
await foreach (var series in FetchSeriesAsync(tvdbId, cancellationToken))
|
||||
{
|
||||
seriesResources.AddIfNotNull(_seriesService.FindByTvdbId(tvdbId.Value)?.ToResource(includeSeasonImages));
|
||||
}
|
||||
else
|
||||
{
|
||||
seriesResources.AddRange(_seriesService.GetAllSeries().Select(s => s.ToResource(includeSeasonImages)));
|
||||
}
|
||||
var seriesResource = series.ToResource(includeSeasonImages);
|
||||
|
||||
MapCoversToLocal(seriesResources.ToArray());
|
||||
LinkSeriesStatistics(seriesResources, seriesStats.ToDictionary(x => x.SeriesId));
|
||||
PopulateAlternateTitles(seriesResources);
|
||||
seriesResources.ForEach(LinkRootFolderPath);
|
||||
MapCoversToLocal(seriesResource);
|
||||
LinkSeriesStatistics(seriesResource, seriesStats.GetValueOrDefault(seriesResource.Id));
|
||||
PopulateAlternateTitles(seriesResource);
|
||||
LinkRootFolderPath(seriesResource);
|
||||
|
||||
return TypedResults.Ok(seriesResources);
|
||||
yield return seriesResource;
|
||||
}
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
|
|
@ -183,9 +180,9 @@ public Results<Ok<SeriesResource>, NotFound> GetResourceByIdWithErrorHandler(int
|
|||
[RestPostById]
|
||||
[Consumes("application/json")]
|
||||
[Produces("application/json")]
|
||||
public Results<Created<SeriesResource>, NotFound> AddSeries([FromBody] SeriesResource seriesResource)
|
||||
public async Task<Results<Created<SeriesResource>, NotFound>> AddSeries([FromBody] SeriesResource seriesResource, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var series = _addSeriesService.AddSeries(seriesResource.ToModel());
|
||||
var series = await _addSeriesService.AddSeriesAsync(seriesResource.ToModel(), cancellationToken);
|
||||
|
||||
return TypedCreated(series.Id);
|
||||
}
|
||||
|
|
@ -193,9 +190,9 @@ public Results<Created<SeriesResource>, NotFound> AddSeries([FromBody] SeriesRes
|
|||
[RestPutById]
|
||||
[Consumes("application/json")]
|
||||
[Produces("application/json")]
|
||||
public Results<Accepted<SeriesResource>, NotFound> UpdateSeries([FromBody] SeriesResource seriesResource, [FromQuery] bool moveFiles = false)
|
||||
public async Task<Results<Accepted<SeriesResource>, NotFound>> UpdateSeries([FromBody] SeriesResource seriesResource, [FromQuery] bool moveFiles = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var series = _seriesService.GetSeries(seriesResource.Id);
|
||||
var series = await _seriesService.GetSeriesAsync(seriesResource.Id, cancellationToken);
|
||||
|
||||
if (moveFiles)
|
||||
{
|
||||
|
|
@ -246,9 +243,9 @@ public Results<Ok<SeasonResource>, NotFound> UpdateSeasonMonitored([FromRoute] i
|
|||
}
|
||||
|
||||
[RestDeleteById]
|
||||
public NoContent DeleteSeries(int id, bool deleteFiles = false, bool addImportListExclusion = false)
|
||||
public async Task<NoContent> DeleteSeries(int id, bool deleteFiles = false, bool addImportListExclusion = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_seriesService.DeleteSeries(new List<int> { id }, deleteFiles, addImportListExclusion);
|
||||
await _seriesService.DeleteSeriesAsync([id], deleteFiles, addImportListExclusion, cancellationToken);
|
||||
|
||||
return TypedResults.NoContent();
|
||||
}
|
||||
|
|
@ -269,6 +266,24 @@ public NoContent DeleteSeries(int id, bool deleteFiles = false, bool addImportLi
|
|||
return resource;
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<NzbDrone.Core.Tv.Series> FetchSeriesAsync(int? tvdbId, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
if (tvdbId.HasValue)
|
||||
{
|
||||
if (await _seriesService.FindByTvdbIdAsync(tvdbId.Value, cancellationToken) is { } series)
|
||||
{
|
||||
yield return series;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await foreach (var series in _seriesService.GetAllSeriesAsync(cancellationToken))
|
||||
{
|
||||
yield return series;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MapCoversToLocal(params SeriesResource[] series)
|
||||
{
|
||||
foreach (var seriesResource in series)
|
||||
|
|
@ -293,8 +308,13 @@ private void LinkSeriesStatistics(List<SeriesResource> resources, Dictionary<int
|
|||
}
|
||||
}
|
||||
|
||||
private void LinkSeriesStatistics(SeriesResource resource, SeriesStatistics seriesStatistics)
|
||||
private void LinkSeriesStatistics(SeriesResource resource, SeriesStatistics? seriesStatistics)
|
||||
{
|
||||
if (seriesStatistics == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Only set last aired from statistics if it's missing from the series itself
|
||||
resource.LastAired ??= seriesStatistics.LastAired;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue