update media server scanning and paging (#1770)
* update media server scanning and paging * remove unused types
This commit is contained in:
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Changed
|
||||||
|
- Remove some unnecessary API calls related to media server scanning and paging
|
||||||
|
|
||||||
## [0.8.7-beta] - 2024-06-26
|
## [0.8.7-beta] - 2024-06-26
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace ErsatzTV.Core.Emby;
|
|
||||||
|
|
||||||
public static class EmbyItemType
|
|
||||||
{
|
|
||||||
public static readonly string Movie = "Movie";
|
|
||||||
public static readonly string Show = "Series";
|
|
||||||
public static readonly string Season = "Season";
|
|
||||||
public static readonly string Episode = "Episode";
|
|
||||||
public static readonly string Collection = "BoxSet";
|
|
||||||
public static readonly string CollectionItems = "Movie,Series,Season,Episode";
|
|
||||||
}
|
|
||||||
@@ -8,32 +8,26 @@ public interface IEmbyApiClient
|
|||||||
Task<Either<BaseError, EmbyServerInformation>> GetServerInformation(string address, string apiKey);
|
Task<Either<BaseError, EmbyServerInformation>> GetServerInformation(string address, string apiKey);
|
||||||
Task<Either<BaseError, List<EmbyLibrary>>> GetLibraries(string address, string apiKey);
|
Task<Either<BaseError, List<EmbyLibrary>>> GetLibraries(string address, string apiKey);
|
||||||
|
|
||||||
IAsyncEnumerable<EmbyMovie> GetMovieLibraryItems(string address, string apiKey, EmbyLibrary library);
|
IAsyncEnumerable<Tuple<EmbyMovie, int>> GetMovieLibraryItems(string address, string apiKey, EmbyLibrary library);
|
||||||
|
|
||||||
IAsyncEnumerable<EmbyShow> GetShowLibraryItems(string address, string apiKey, EmbyLibrary library);
|
IAsyncEnumerable<Tuple<EmbyShow, int>> GetShowLibraryItems(string address, string apiKey, EmbyLibrary library);
|
||||||
|
|
||||||
IAsyncEnumerable<EmbySeason> GetSeasonLibraryItems(
|
IAsyncEnumerable<Tuple<EmbySeason, int>> GetSeasonLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
EmbyLibrary library,
|
EmbyLibrary library,
|
||||||
string showId);
|
string showId);
|
||||||
|
|
||||||
IAsyncEnumerable<EmbyEpisode> GetEpisodeLibraryItems(
|
IAsyncEnumerable<Tuple<EmbyEpisode, int>> GetEpisodeLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
EmbyLibrary library,
|
EmbyLibrary library,
|
||||||
string showId,
|
string showId,
|
||||||
string seasonId);
|
string seasonId);
|
||||||
|
|
||||||
IAsyncEnumerable<EmbyCollection> GetCollectionLibraryItems(string address, string apiKey);
|
IAsyncEnumerable<Tuple<EmbyCollection, int>> GetCollectionLibraryItems(string address, string apiKey);
|
||||||
|
|
||||||
IAsyncEnumerable<MediaItem> GetCollectionItems(string address, string apiKey, string collectionId);
|
IAsyncEnumerable<Tuple<MediaItem, int>> GetCollectionItems(string address, string apiKey, string collectionId);
|
||||||
|
|
||||||
Task<Either<BaseError, int>> GetLibraryItemCount(
|
|
||||||
string address,
|
|
||||||
string apiKey,
|
|
||||||
string parentId,
|
|
||||||
string includeItemTypes);
|
|
||||||
|
|
||||||
Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
||||||
string address,
|
string address,
|
||||||
|
|||||||
@@ -9,38 +9,30 @@ public interface IJellyfinApiClient
|
|||||||
Task<Either<BaseError, List<JellyfinLibrary>>> GetLibraries(string address, string apiKey);
|
Task<Either<BaseError, List<JellyfinLibrary>>> GetLibraries(string address, string apiKey);
|
||||||
Task<Either<BaseError, string>> GetAdminUserId(string address, string apiKey);
|
Task<Either<BaseError, string>> GetAdminUserId(string address, string apiKey);
|
||||||
|
|
||||||
IAsyncEnumerable<JellyfinMovie> GetMovieLibraryItems(string address, string apiKey, JellyfinLibrary library);
|
IAsyncEnumerable<Tuple<JellyfinMovie, int>> GetMovieLibraryItems(string address, string apiKey, JellyfinLibrary library);
|
||||||
|
|
||||||
IAsyncEnumerable<JellyfinShow> GetShowLibraryItems(string address, string apiKey, JellyfinLibrary library);
|
IAsyncEnumerable<Tuple<JellyfinShow, int>> GetShowLibraryItems(string address, string apiKey, JellyfinLibrary library);
|
||||||
|
|
||||||
IAsyncEnumerable<JellyfinSeason> GetSeasonLibraryItems(
|
IAsyncEnumerable<Tuple<JellyfinSeason, int>> GetSeasonLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
JellyfinLibrary library,
|
JellyfinLibrary library,
|
||||||
string showId);
|
string showId);
|
||||||
|
|
||||||
IAsyncEnumerable<JellyfinEpisode> GetEpisodeLibraryItems(
|
IAsyncEnumerable<Tuple<JellyfinEpisode, int>> GetEpisodeLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
JellyfinLibrary library,
|
JellyfinLibrary library,
|
||||||
string seasonId);
|
string seasonId);
|
||||||
|
|
||||||
IAsyncEnumerable<JellyfinCollection> GetCollectionLibraryItems(string address, string apiKey, int mediaSourceId);
|
IAsyncEnumerable<Tuple<JellyfinCollection, int>> GetCollectionLibraryItems(string address, string apiKey, int mediaSourceId);
|
||||||
|
|
||||||
IAsyncEnumerable<MediaItem> GetCollectionItems(
|
IAsyncEnumerable<Tuple<MediaItem, int>> GetCollectionItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
int mediaSourceId,
|
int mediaSourceId,
|
||||||
string collectionId);
|
string collectionId);
|
||||||
|
|
||||||
Task<Either<BaseError, int>> GetLibraryItemCount(
|
|
||||||
string address,
|
|
||||||
string apiKey,
|
|
||||||
JellyfinLibrary library,
|
|
||||||
string parentId,
|
|
||||||
string includeItemTypes,
|
|
||||||
bool excludeFolders);
|
|
||||||
|
|
||||||
Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
|
|||||||
@@ -13,33 +13,23 @@ public interface IPlexServerApiClient
|
|||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token);
|
PlexServerAuthToken token);
|
||||||
|
|
||||||
IAsyncEnumerable<PlexMovie> GetMovieLibraryContents(
|
IAsyncEnumerable<Tuple<PlexMovie, int>> GetMovieLibraryContents(
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token);
|
PlexServerAuthToken token);
|
||||||
|
|
||||||
IAsyncEnumerable<PlexShow> GetShowLibraryContents(
|
IAsyncEnumerable<Tuple<PlexShow, int>> GetShowLibraryContents(
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token);
|
PlexServerAuthToken token);
|
||||||
|
|
||||||
Task<Either<BaseError, int>> CountShowSeasons(
|
IAsyncEnumerable<Tuple<PlexSeason, int>> GetShowSeasons(
|
||||||
PlexShow show,
|
|
||||||
PlexConnection connection,
|
|
||||||
PlexServerAuthToken token);
|
|
||||||
|
|
||||||
IAsyncEnumerable<PlexSeason> GetShowSeasons(
|
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexShow show,
|
PlexShow show,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token);
|
PlexServerAuthToken token);
|
||||||
|
|
||||||
Task<Either<BaseError, int>> CountSeasonEpisodes(
|
IAsyncEnumerable<Tuple<PlexEpisode, int>> GetSeasonEpisodes(
|
||||||
PlexSeason season,
|
|
||||||
PlexConnection connection,
|
|
||||||
PlexServerAuthToken token);
|
|
||||||
|
|
||||||
IAsyncEnumerable<PlexEpisode> GetSeasonEpisodes(
|
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexSeason season,
|
PlexSeason season,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
@@ -63,17 +53,12 @@ public interface IPlexServerApiClient
|
|||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token);
|
PlexServerAuthToken token);
|
||||||
|
|
||||||
Task<Either<BaseError, int>> GetLibraryItemCount(
|
IAsyncEnumerable<Tuple<PlexCollection, int>> GetAllCollections(
|
||||||
PlexLibrary library,
|
|
||||||
PlexConnection connection,
|
|
||||||
PlexServerAuthToken token);
|
|
||||||
|
|
||||||
IAsyncEnumerable<PlexCollection> GetAllCollections(
|
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
IAsyncEnumerable<MediaItem> GetCollectionItems(
|
IAsyncEnumerable<Tuple<MediaItem, int>> GetCollectionItems(
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
string key,
|
string key,
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace ErsatzTV.Core.Jellyfin;
|
|
||||||
|
|
||||||
public static class JellyfinItemType
|
|
||||||
{
|
|
||||||
public static readonly string Movie = "Movie";
|
|
||||||
public static readonly string Show = "Series";
|
|
||||||
public static readonly string Season = "Season";
|
|
||||||
public static readonly string Episode = "Episode";
|
|
||||||
public static readonly string Collection = "BoxSet";
|
|
||||||
public static readonly string CollectionItems = "Movie,Series,Season,Episode";
|
|
||||||
}
|
|
||||||
@@ -69,13 +69,11 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<EmbyMovie> GetMovieLibraryItems(string address, string apiKey, EmbyLibrary library)
|
public IAsyncEnumerable<Tuple<EmbyMovie, int>> GetMovieLibraryItems(string address, string apiKey, EmbyLibrary library)
|
||||||
=> GetPagedLibraryContents(
|
=> GetPagedLibraryContents(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
library.ItemId,
|
library.ItemId,
|
||||||
EmbyItemType.Movie,
|
|
||||||
(service, itemId, skip, pageSize) => service.GetMovieLibraryItems(
|
(service, itemId, skip, pageSize) => service.GetMovieLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
itemId,
|
itemId,
|
||||||
@@ -83,13 +81,11 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToMovie(lib, item)).Flatten());
|
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToMovie(lib, item)).Flatten());
|
||||||
|
|
||||||
public IAsyncEnumerable<EmbyShow> GetShowLibraryItems(string address, string apiKey, EmbyLibrary library)
|
public IAsyncEnumerable<Tuple<EmbyShow, int>> GetShowLibraryItems(string address, string apiKey, EmbyLibrary library)
|
||||||
=> GetPagedLibraryContents(
|
=> GetPagedLibraryContents(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
library.ItemId,
|
library.ItemId,
|
||||||
EmbyItemType.Show,
|
|
||||||
(service, itemId, skip, pageSize) => service.GetShowLibraryItems(
|
(service, itemId, skip, pageSize) => service.GetShowLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
itemId,
|
itemId,
|
||||||
@@ -97,16 +93,14 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(_, item) => ProjectToShow(item));
|
(_, item) => ProjectToShow(item));
|
||||||
|
|
||||||
public IAsyncEnumerable<EmbySeason> GetSeasonLibraryItems(
|
public IAsyncEnumerable<Tuple<EmbySeason, int>> GetSeasonLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
EmbyLibrary library,
|
EmbyLibrary library,
|
||||||
string showId) => GetPagedLibraryContents(
|
string showId) => GetPagedLibraryContents(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
showId,
|
showId,
|
||||||
EmbyItemType.Season,
|
|
||||||
(service, itemId, skip, pageSize) => service.GetSeasonLibraryItems(
|
(service, itemId, skip, pageSize) => service.GetSeasonLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
itemId,
|
itemId,
|
||||||
@@ -114,17 +108,15 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(_, item) => ProjectToSeason(item));
|
(_, item) => ProjectToSeason(item));
|
||||||
|
|
||||||
public IAsyncEnumerable<EmbyEpisode> GetEpisodeLibraryItems(
|
public IAsyncEnumerable<Tuple<EmbyEpisode, int>> GetEpisodeLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
EmbyLibrary library,
|
EmbyLibrary library,
|
||||||
string showId,
|
string showId,
|
||||||
string seasonId) => GetPagedLibraryContents(
|
string seasonId) => GetPagedLibraryContents(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
seasonId,
|
seasonId,
|
||||||
EmbyItemType.Episode,
|
|
||||||
(service, _, skip, pageSize) => service.GetEpisodeLibraryItems(
|
(service, _, skip, pageSize) => service.GetEpisodeLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
showId,
|
showId,
|
||||||
@@ -133,7 +125,7 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToEpisode(lib, item)).Flatten());
|
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToEpisode(lib, item)).Flatten());
|
||||||
|
|
||||||
public IAsyncEnumerable<EmbyCollection> GetCollectionLibraryItems(string address, string apiKey)
|
public IAsyncEnumerable<Tuple<EmbyCollection, int>> GetCollectionLibraryItems(string address, string apiKey)
|
||||||
{
|
{
|
||||||
// TODO: should we enumerate collection libraries here?
|
// TODO: should we enumerate collection libraries here?
|
||||||
|
|
||||||
@@ -141,10 +133,8 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
{
|
{
|
||||||
return GetPagedLibraryContents(
|
return GetPagedLibraryContents(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
None,
|
None,
|
||||||
itemId,
|
itemId,
|
||||||
EmbyItemType.Collection,
|
|
||||||
(service, _, skip, pageSize) => service.GetCollectionLibraryItems(
|
(service, _, skip, pageSize) => service.GetCollectionLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
itemId,
|
itemId,
|
||||||
@@ -153,19 +143,17 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
(_, item) => ProjectToCollection(item));
|
(_, item) => ProjectToCollection(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
return AsyncEnumerable.Empty<EmbyCollection>();
|
return AsyncEnumerable.Empty<Tuple<EmbyCollection, int>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<MediaItem> GetCollectionItems(
|
public IAsyncEnumerable<Tuple<MediaItem, int>> GetCollectionItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
string collectionId) =>
|
string collectionId) =>
|
||||||
GetPagedLibraryContents(
|
GetPagedLibraryContents(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
None,
|
None,
|
||||||
collectionId,
|
collectionId,
|
||||||
EmbyItemType.CollectionItems,
|
|
||||||
(service, _, skip, pageSize) => service.GetCollectionItems(
|
(service, _, skip, pageSize) => service.GetCollectionItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
collectionId,
|
collectionId,
|
||||||
@@ -173,25 +161,6 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(_, item) => ProjectToCollectionMediaItem(item));
|
(_, item) => ProjectToCollectionMediaItem(item));
|
||||||
|
|
||||||
public async Task<Either<BaseError, int>> GetLibraryItemCount(
|
|
||||||
string address,
|
|
||||||
string apiKey,
|
|
||||||
string parentId,
|
|
||||||
string includeItemTypes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
IEmbyApi service = RestService.For<IEmbyApi>(address);
|
|
||||||
EmbyLibraryItemsResponse items = await service.GetLibraryStats(apiKey, parentId, includeItemTypes);
|
|
||||||
return items.TotalRecordCount;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error getting Emby library item count");
|
|
||||||
return BaseError.New(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
public async Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
@@ -212,36 +181,31 @@ public class EmbyApiClient : IEmbyApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async IAsyncEnumerable<TItem> GetPagedLibraryContents<TItem>(
|
private static async IAsyncEnumerable<Tuple<TItem, int>> GetPagedLibraryContents<TItem>(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
|
||||||
Option<EmbyLibrary> maybeLibrary,
|
Option<EmbyLibrary> maybeLibrary,
|
||||||
string parentId,
|
string parentId,
|
||||||
string itemType,
|
|
||||||
Func<IEmbyApi, string, int, int, Task<EmbyLibraryItemsResponse>> getItems,
|
Func<IEmbyApi, string, int, int, Task<EmbyLibraryItemsResponse>> getItems,
|
||||||
Func<Option<EmbyLibrary>, EmbyLibraryItemResponse, Option<TItem>> mapper)
|
Func<Option<EmbyLibrary>, EmbyLibraryItemResponse, Option<TItem>> mapper)
|
||||||
{
|
{
|
||||||
IEmbyApi service = RestService.For<IEmbyApi>(address);
|
IEmbyApi service = RestService.For<IEmbyApi>(address);
|
||||||
int size = await service
|
|
||||||
.GetLibraryStats(apiKey, parentId, itemType)
|
|
||||||
.Map(r => r.TotalRecordCount);
|
|
||||||
|
|
||||||
const int PAGE_SIZE = 10;
|
const int PAGE_SIZE = 10;
|
||||||
|
|
||||||
int pages = (size - 1) / PAGE_SIZE + 1;
|
int pages = int.MaxValue;
|
||||||
|
|
||||||
for (var i = 0; i < pages; i++)
|
for (var i = 0; i < pages; i++)
|
||||||
{
|
{
|
||||||
int skip = i * PAGE_SIZE;
|
int skip = i * PAGE_SIZE;
|
||||||
|
|
||||||
Task<IEnumerable<TItem>> result = getItems(service, parentId, skip, PAGE_SIZE)
|
EmbyLibraryItemsResponse result = await getItems(service, parentId, skip, PAGE_SIZE);
|
||||||
.Map(items => items.Items.Map(item => mapper(maybeLibrary, item)).Somes());
|
|
||||||
|
// update page count
|
||||||
|
pages = Math.Min(pages, (result.TotalRecordCount - 1) / PAGE_SIZE + 1);
|
||||||
|
|
||||||
#pragma warning disable VSTHRD003
|
#pragma warning disable VSTHRD003
|
||||||
foreach (TItem item in await result)
|
foreach (TItem item in result.Items.Map(item => mapper(maybeLibrary, item)).Somes())
|
||||||
#pragma warning restore VSTHRD003
|
#pragma warning restore VSTHRD003
|
||||||
{
|
{
|
||||||
yield return item;
|
yield return new Tuple<TItem, int>(item, result.TotalRecordCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,21 +17,6 @@ public interface IEmbyApi
|
|||||||
[Header("X-Emby-Token")]
|
[Header("X-Emby-Token")]
|
||||||
string apiKey);
|
string apiKey);
|
||||||
|
|
||||||
[Get("/Items")]
|
|
||||||
public Task<EmbyLibraryItemsResponse> GetLibraryStats(
|
|
||||||
[Header("X-Emby-Token")]
|
|
||||||
string apiKey,
|
|
||||||
[Query]
|
|
||||||
string parentId,
|
|
||||||
[Query]
|
|
||||||
string includeItemTypes,
|
|
||||||
[Query]
|
|
||||||
bool recursive = true,
|
|
||||||
[Query]
|
|
||||||
int startIndex = 0,
|
|
||||||
[Query]
|
|
||||||
int limit = 0);
|
|
||||||
|
|
||||||
[Get("/Items?sortOrder=Ascending&sortBy=SortName")]
|
[Get("/Items?sortOrder=Ascending&sortBy=SortName")]
|
||||||
public Task<EmbyLibraryItemsResponse> GetMovieLibraryItems(
|
public Task<EmbyLibraryItemsResponse> GetMovieLibraryItems(
|
||||||
[Header("X-Emby-Token")]
|
[Header("X-Emby-Token")]
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace ErsatzTV.Infrastructure.Emby.Models;
|
||||||
|
|
||||||
|
public class EmbyItemsCountsResponse
|
||||||
|
{
|
||||||
|
public int MovieCount { get; set; }
|
||||||
|
public int SeriesCount { get; set; }
|
||||||
|
public int EpisodeCount { get; set; }
|
||||||
|
}
|
||||||
@@ -22,25 +22,6 @@ public interface IJellyfinApi
|
|||||||
[Header("X-Emby-Token")]
|
[Header("X-Emby-Token")]
|
||||||
string apiKey);
|
string apiKey);
|
||||||
|
|
||||||
[Get("/Items")]
|
|
||||||
public Task<JellyfinLibraryItemsResponse> GetLibraryStats(
|
|
||||||
[Header("X-Emby-Token")]
|
|
||||||
string apiKey,
|
|
||||||
[Query]
|
|
||||||
string userId,
|
|
||||||
[Query]
|
|
||||||
string parentId,
|
|
||||||
[Query]
|
|
||||||
string includeItemTypes,
|
|
||||||
[Query]
|
|
||||||
bool recursive = true,
|
|
||||||
[Query]
|
|
||||||
string filters = "IsNotFolder",
|
|
||||||
[Query]
|
|
||||||
int startIndex = 0,
|
|
||||||
[Query]
|
|
||||||
int limit = 0);
|
|
||||||
|
|
||||||
[Get("/Items?sortOrder=Ascending&sortBy=SortName")]
|
[Get("/Items?sortOrder=Ascending&sortBy=SortName")]
|
||||||
public Task<JellyfinLibraryItemsResponse> GetMovieLibraryItems(
|
public Task<JellyfinLibraryItemsResponse> GetMovieLibraryItems(
|
||||||
[Header("X-Emby-Token")]
|
[Header("X-Emby-Token")]
|
||||||
|
|||||||
@@ -93,17 +93,15 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<JellyfinMovie> GetMovieLibraryItems(
|
public IAsyncEnumerable<Tuple<JellyfinMovie, int>> GetMovieLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
JellyfinLibrary library) =>
|
JellyfinLibrary library) =>
|
||||||
GetPagedLibraryItems(
|
GetPagedLibraryItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
library.MediaSourceId,
|
library.MediaSourceId,
|
||||||
library.ItemId,
|
library.ItemId,
|
||||||
JellyfinItemType.Movie,
|
|
||||||
(service, userId, itemId, skip, pageSize) => service.GetMovieLibraryItems(
|
(service, userId, itemId, skip, pageSize) => service.GetMovieLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
userId,
|
userId,
|
||||||
@@ -112,17 +110,15 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToMovie(lib, item)).Flatten());
|
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToMovie(lib, item)).Flatten());
|
||||||
|
|
||||||
public IAsyncEnumerable<JellyfinShow> GetShowLibraryItems(
|
public IAsyncEnumerable<Tuple<JellyfinShow, int>> GetShowLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
JellyfinLibrary library) =>
|
JellyfinLibrary library) =>
|
||||||
GetPagedLibraryItems(
|
GetPagedLibraryItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
library.MediaSourceId,
|
library.MediaSourceId,
|
||||||
library.ItemId,
|
library.ItemId,
|
||||||
JellyfinItemType.Show,
|
|
||||||
(service, userId, itemId, skip, pageSize) => service.GetShowLibraryItems(
|
(service, userId, itemId, skip, pageSize) => service.GetShowLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
userId,
|
userId,
|
||||||
@@ -131,18 +127,16 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(_, item) => ProjectToShow(item));
|
(_, item) => ProjectToShow(item));
|
||||||
|
|
||||||
public IAsyncEnumerable<JellyfinSeason> GetSeasonLibraryItems(
|
public IAsyncEnumerable<Tuple<JellyfinSeason, int>> GetSeasonLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
JellyfinLibrary library,
|
JellyfinLibrary library,
|
||||||
string showId) =>
|
string showId) =>
|
||||||
GetPagedLibraryItems(
|
GetPagedLibraryItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
library.MediaSourceId,
|
library.MediaSourceId,
|
||||||
showId,
|
showId,
|
||||||
JellyfinItemType.Season,
|
|
||||||
(service, userId, _, skip, pageSize) => service.GetSeasonLibraryItems(
|
(service, userId, _, skip, pageSize) => service.GetSeasonLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
userId,
|
userId,
|
||||||
@@ -151,18 +145,16 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(_, item) => ProjectToSeason(item));
|
(_, item) => ProjectToSeason(item));
|
||||||
|
|
||||||
public IAsyncEnumerable<JellyfinEpisode> GetEpisodeLibraryItems(
|
public IAsyncEnumerable<Tuple<JellyfinEpisode, int>> GetEpisodeLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
JellyfinLibrary library,
|
JellyfinLibrary library,
|
||||||
string seasonId) =>
|
string seasonId) =>
|
||||||
GetPagedLibraryItems(
|
GetPagedLibraryItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
library,
|
library,
|
||||||
library.MediaSourceId,
|
library.MediaSourceId,
|
||||||
seasonId,
|
seasonId,
|
||||||
JellyfinItemType.Episode,
|
|
||||||
(service, userId, _, skip, pageSize) => service.GetEpisodeLibraryItems(
|
(service, userId, _, skip, pageSize) => service.GetEpisodeLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
userId,
|
userId,
|
||||||
@@ -171,7 +163,7 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToEpisode(lib, item)).Flatten());
|
(maybeLibrary, item) => maybeLibrary.Map(lib => ProjectToEpisode(lib, item)).Flatten());
|
||||||
|
|
||||||
public IAsyncEnumerable<JellyfinCollection> GetCollectionLibraryItems(
|
public IAsyncEnumerable<Tuple<JellyfinCollection, int>> GetCollectionLibraryItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
int mediaSourceId)
|
int mediaSourceId)
|
||||||
@@ -182,11 +174,9 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
{
|
{
|
||||||
return GetPagedLibraryItems(
|
return GetPagedLibraryItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
None,
|
None,
|
||||||
mediaSourceId,
|
mediaSourceId,
|
||||||
itemId,
|
itemId,
|
||||||
JellyfinItemType.Collection,
|
|
||||||
(service, userId, _, skip, pageSize) => service.GetCollectionLibraryItems(
|
(service, userId, _, skip, pageSize) => service.GetCollectionLibraryItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
userId,
|
userId,
|
||||||
@@ -196,21 +186,19 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
(_, item) => ProjectToCollection(item));
|
(_, item) => ProjectToCollection(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
return AsyncEnumerable.Empty<JellyfinCollection>();
|
return AsyncEnumerable.Empty<Tuple<JellyfinCollection, int>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<MediaItem> GetCollectionItems(
|
public IAsyncEnumerable<Tuple<MediaItem, int>> GetCollectionItems(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
int mediaSourceId,
|
int mediaSourceId,
|
||||||
string collectionId) =>
|
string collectionId) =>
|
||||||
GetPagedLibraryItems(
|
GetPagedLibraryItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
|
||||||
None,
|
None,
|
||||||
mediaSourceId,
|
mediaSourceId,
|
||||||
collectionId,
|
collectionId,
|
||||||
JellyfinItemType.CollectionItems,
|
|
||||||
(service, userId, _, skip, pageSize) => service.GetCollectionItems(
|
(service, userId, _, skip, pageSize) => service.GetCollectionItems(
|
||||||
apiKey,
|
apiKey,
|
||||||
userId,
|
userId,
|
||||||
@@ -219,37 +207,6 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
limit: pageSize),
|
limit: pageSize),
|
||||||
(_, item) => ProjectToCollectionMediaItem(item));
|
(_, item) => ProjectToCollectionMediaItem(item));
|
||||||
|
|
||||||
public async Task<Either<BaseError, int>> GetLibraryItemCount(
|
|
||||||
string address,
|
|
||||||
string apiKey,
|
|
||||||
JellyfinLibrary library,
|
|
||||||
string parentId,
|
|
||||||
string includeItemTypes,
|
|
||||||
bool excludeFolders)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_memoryCache.TryGetValue($"jellyfin_admin_user_id.{library.MediaSourceId}", out string userId))
|
|
||||||
{
|
|
||||||
IJellyfinApi service = RestService.For<IJellyfinApi>(address);
|
|
||||||
JellyfinLibraryItemsResponse items = await service.GetLibraryStats(
|
|
||||||
apiKey,
|
|
||||||
userId,
|
|
||||||
parentId,
|
|
||||||
includeItemTypes,
|
|
||||||
filters: excludeFolders ? "IsNotFolder" : null);
|
|
||||||
return items.TotalRecordCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BaseError.New("Jellyfin admin user id is not available");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error getting jellyfin library item count");
|
|
||||||
return BaseError.New(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
public async Task<Either<BaseError, MediaVersion>> GetPlaybackInfo(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
string apiKey,
|
||||||
@@ -275,40 +232,33 @@ public class JellyfinApiClient : IJellyfinApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async IAsyncEnumerable<TItem> GetPagedLibraryItems<TItem>(
|
private async IAsyncEnumerable<Tuple<TItem, int>> GetPagedLibraryItems<TItem>(
|
||||||
string address,
|
string address,
|
||||||
string apiKey,
|
|
||||||
Option<JellyfinLibrary> maybeLibrary,
|
Option<JellyfinLibrary> maybeLibrary,
|
||||||
int mediaSourceId,
|
int mediaSourceId,
|
||||||
string parentId,
|
string parentId,
|
||||||
string itemType,
|
|
||||||
Func<IJellyfinApi, string, string, int, int, Task<JellyfinLibraryItemsResponse>> getItems,
|
Func<IJellyfinApi, string, string, int, int, Task<JellyfinLibraryItemsResponse>> getItems,
|
||||||
Func<Option<JellyfinLibrary>, JellyfinLibraryItemResponse, Option<TItem>> mapper)
|
Func<Option<JellyfinLibrary>, JellyfinLibraryItemResponse, Option<TItem>> mapper)
|
||||||
{
|
{
|
||||||
if (_memoryCache.TryGetValue($"jellyfin_admin_user_id.{mediaSourceId}", out string userId))
|
if (_memoryCache.TryGetValue($"jellyfin_admin_user_id.{mediaSourceId}", out string userId))
|
||||||
{
|
{
|
||||||
IJellyfinApi service = RestService.For<IJellyfinApi>(address);
|
IJellyfinApi service = RestService.For<IJellyfinApi>(address);
|
||||||
string filters = itemType == JellyfinItemType.Movie || itemType == JellyfinItemType.Episode
|
|
||||||
? "IsNotFolder"
|
|
||||||
: null;
|
|
||||||
int size = await service
|
|
||||||
.GetLibraryStats(apiKey, userId, parentId, itemType, filters: filters)
|
|
||||||
.Map(r => r.TotalRecordCount);
|
|
||||||
|
|
||||||
const int PAGE_SIZE = 10;
|
const int PAGE_SIZE = 10;
|
||||||
|
|
||||||
int pages = (size - 1) / PAGE_SIZE + 1;
|
int pages = int.MaxValue;
|
||||||
|
|
||||||
for (var i = 0; i < pages; i++)
|
for (var i = 0; i < pages; i++)
|
||||||
{
|
{
|
||||||
int skip = i * PAGE_SIZE;
|
int skip = i * PAGE_SIZE;
|
||||||
|
|
||||||
Task<IEnumerable<TItem>> result = getItems(service, userId, parentId, skip, PAGE_SIZE)
|
JellyfinLibraryItemsResponse result = await getItems(service, userId, parentId, skip, PAGE_SIZE);
|
||||||
.Map(items => items.Items.Map(item => mapper(maybeLibrary, item)).Somes());
|
|
||||||
|
|
||||||
foreach (TItem item in await result)
|
// update page count
|
||||||
|
pages = Math.Min(pages, (result.TotalRecordCount - 1) / PAGE_SIZE + 1);
|
||||||
|
|
||||||
|
foreach (TItem item in result.Items.Map(item => mapper(maybeLibrary, item)).Somes())
|
||||||
{
|
{
|
||||||
yield return item;
|
yield return new Tuple<TItem, int>(item, result.TotalRecordCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<PlexMovie> GetMovieLibraryContents(
|
public IAsyncEnumerable<Tuple<PlexMovie, int>> GetMovieLibraryContents(
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token)
|
PlexServerAuthToken token)
|
||||||
@@ -89,7 +89,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
return GetPagedLibraryContents(connection, CountItems, GetItems);
|
return GetPagedLibraryContents(connection, CountItems, GetItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<PlexShow> GetShowLibraryContents(
|
public IAsyncEnumerable<Tuple<PlexShow, int>> GetShowLibraryContents(
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token)
|
PlexServerAuthToken token)
|
||||||
@@ -110,24 +110,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
return GetPagedLibraryContents(connection, CountItems, GetItems);
|
return GetPagedLibraryContents(connection, CountItems, GetItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Either<BaseError, int>> CountShowSeasons(
|
public IAsyncEnumerable<Tuple<PlexSeason, int>> GetShowSeasons(
|
||||||
PlexShow show,
|
|
||||||
PlexConnection connection,
|
|
||||||
PlexServerAuthToken token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string showMetadataKey = show.Key.Split("/").Reverse().Skip(1).Head();
|
|
||||||
IPlexServerApi service = XmlServiceFor(connection.Uri);
|
|
||||||
return await service.CountShowChildren(showMetadataKey, token.AuthToken).Map(r => r.TotalSize);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return BaseError.New(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAsyncEnumerable<PlexSeason> GetShowSeasons(
|
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexShow show,
|
PlexShow show,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
@@ -150,24 +133,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
return GetPagedLibraryContents(connection, CountItems, GetItems);
|
return GetPagedLibraryContents(connection, CountItems, GetItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Either<BaseError, int>> CountSeasonEpisodes(
|
public IAsyncEnumerable<Tuple<PlexEpisode, int>> GetSeasonEpisodes(
|
||||||
PlexSeason season,
|
|
||||||
PlexConnection connection,
|
|
||||||
PlexServerAuthToken token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string seasonMetadataKey = season.Key.Split("/").Reverse().Skip(1).Head();
|
|
||||||
IPlexServerApi service = XmlServiceFor(connection.Uri);
|
|
||||||
return await service.CountSeasonChildren(seasonMetadataKey, token.AuthToken).Map(r => r.TotalSize);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return BaseError.New(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAsyncEnumerable<PlexEpisode> GetSeasonEpisodes(
|
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexSeason season,
|
PlexSeason season,
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
@@ -276,23 +242,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Either<BaseError, int>> GetLibraryItemCount(
|
public IAsyncEnumerable<Tuple<PlexCollection, int>> GetAllCollections(
|
||||||
PlexLibrary library,
|
|
||||||
PlexConnection connection,
|
|
||||||
PlexServerAuthToken token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
IPlexServerApi service = XmlServiceFor(connection.Uri);
|
|
||||||
return await service.GetLibrarySection(library.Key, token.AuthToken).Map(r => r.TotalSize);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return BaseError.New(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAsyncEnumerable<PlexCollection> GetAllCollections(
|
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
@@ -313,7 +263,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<MediaItem> GetCollectionItems(
|
public IAsyncEnumerable<Tuple<MediaItem, int>> GetCollectionItems(
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
string key,
|
string key,
|
||||||
@@ -335,7 +285,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async IAsyncEnumerable<TItem> GetPagedLibraryContents<TItem>(
|
private static async IAsyncEnumerable<Tuple<TItem, int>> GetPagedLibraryContents<TItem>(
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
Func<IPlexServerApi, Task<PlexXmlMediaContainerStatsResponse>> countItems,
|
Func<IPlexServerApi, Task<PlexXmlMediaContainerStatsResponse>> countItems,
|
||||||
Func<IPlexServerApi, IPlexServerApi, int, int, Task<IEnumerable<TItem>>> getItems)
|
Func<IPlexServerApi, IPlexServerApi, int, int, Task<IEnumerable<TItem>>> getItems)
|
||||||
@@ -357,7 +307,7 @@ public class PlexServerApiClient : IPlexServerApiClient
|
|||||||
|
|
||||||
foreach (TItem item in await result)
|
foreach (TItem item in await result)
|
||||||
{
|
{
|
||||||
yield return item;
|
yield return new Tuple<TItem, int>(item, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class EmbyCollectionScanner : IEmbyCollectionScanner
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// need to call get libraries to find library that contains collections (box sets)
|
// need to call get libraries to find library that contains collections (box sets)
|
||||||
await _embyApiClient.GetLibraries(address, apiKey);
|
await _embyApiClient.GetLibraries(address, apiKey);
|
||||||
|
|
||||||
var incomingItemIds = new List<string>();
|
var incomingItemIds = new List<string>();
|
||||||
@@ -38,7 +38,7 @@ public class EmbyCollectionScanner : IEmbyCollectionScanner
|
|||||||
// get all collections from db (item id, etag)
|
// get all collections from db (item id, etag)
|
||||||
List<EmbyCollection> existingCollections = await _embyCollectionRepository.GetCollections();
|
List<EmbyCollection> existingCollections = await _embyCollectionRepository.GetCollections();
|
||||||
|
|
||||||
await foreach (EmbyCollection collection in _embyApiClient.GetCollectionLibraryItems(address, apiKey))
|
await foreach ((EmbyCollection collection, int _) in _embyApiClient.GetCollectionLibraryItems(address, apiKey))
|
||||||
{
|
{
|
||||||
incomingItemIds.Add(collection.ItemId);
|
incomingItemIds.Add(collection.ItemId);
|
||||||
|
|
||||||
@@ -88,13 +88,13 @@ public class EmbyCollectionScanner : IEmbyCollectionScanner
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// get collection items from Emby
|
// get collection items from Emby
|
||||||
IAsyncEnumerable<MediaItem> items = _embyApiClient.GetCollectionItems(address, apiKey, collection.ItemId);
|
IAsyncEnumerable<Tuple<MediaItem, int>> items = _embyApiClient.GetCollectionItems(address, apiKey, collection.ItemId);
|
||||||
|
|
||||||
List<int> removedIds = await _embyCollectionRepository.RemoveAllTags(collection);
|
List<int> removedIds = await _embyCollectionRepository.RemoveAllTags(collection);
|
||||||
|
|
||||||
// sync tags on items
|
// sync tags on items
|
||||||
var addedIds = new List<int>();
|
var addedIds = new List<int>();
|
||||||
await foreach (MediaItem item in items)
|
await foreach ((MediaItem item, int _) in items)
|
||||||
{
|
{
|
||||||
addedIds.Add(await _embyCollectionRepository.AddTag(item, collection));
|
addedIds.Add(await _embyCollectionRepository.AddTag(item, collection));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,16 +75,7 @@ public class EmbyMovieLibraryScanner :
|
|||||||
protected override string MediaServerItemId(EmbyMovie movie) => movie.ItemId;
|
protected override string MediaServerItemId(EmbyMovie movie) => movie.ItemId;
|
||||||
protected override string MediaServerEtag(EmbyMovie movie) => movie.Etag;
|
protected override string MediaServerEtag(EmbyMovie movie) => movie.Etag;
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountMovieLibraryItems(
|
protected override IAsyncEnumerable<Tuple<EmbyMovie, int>> GetMovieLibraryItems(
|
||||||
EmbyConnectionParameters connectionParameters,
|
|
||||||
EmbyLibrary library) =>
|
|
||||||
_embyApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
library.ItemId,
|
|
||||||
EmbyItemType.Movie);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<EmbyMovie> GetMovieLibraryItems(
|
|
||||||
EmbyConnectionParameters connectionParameters,
|
EmbyConnectionParameters connectionParameters,
|
||||||
EmbyLibrary library) =>
|
EmbyLibrary library) =>
|
||||||
_embyApiClient.GetMovieLibraryItems(
|
_embyApiClient.GetMovieLibraryItems(
|
||||||
|
|||||||
@@ -72,16 +72,7 @@ public class EmbyTelevisionLibraryScanner : MediaServerTelevisionLibraryScanner<
|
|||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountShowLibraryItems(
|
protected override IAsyncEnumerable<Tuple<EmbyShow, int>> GetShowLibraryItems(
|
||||||
EmbyConnectionParameters connectionParameters,
|
|
||||||
EmbyLibrary library)
|
|
||||||
=> _embyApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
library.ItemId,
|
|
||||||
EmbyItemType.Show);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<EmbyShow> GetShowLibraryItems(
|
|
||||||
EmbyConnectionParameters connectionParameters,
|
EmbyConnectionParameters connectionParameters,
|
||||||
EmbyLibrary library) =>
|
EmbyLibrary library) =>
|
||||||
_embyApiClient.GetShowLibraryItems(connectionParameters.Address, connectionParameters.ApiKey, library);
|
_embyApiClient.GetShowLibraryItems(connectionParameters.Address, connectionParameters.ApiKey, library);
|
||||||
@@ -94,17 +85,7 @@ public class EmbyTelevisionLibraryScanner : MediaServerTelevisionLibraryScanner<
|
|||||||
protected override string MediaServerEtag(EmbySeason season) => season.Etag;
|
protected override string MediaServerEtag(EmbySeason season) => season.Etag;
|
||||||
protected override string MediaServerEtag(EmbyEpisode episode) => episode.Etag;
|
protected override string MediaServerEtag(EmbyEpisode episode) => episode.Etag;
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountSeasonLibraryItems(
|
protected override IAsyncEnumerable<Tuple<EmbySeason, int>> GetSeasonLibraryItems(
|
||||||
EmbyConnectionParameters connectionParameters,
|
|
||||||
EmbyLibrary library,
|
|
||||||
EmbyShow show) =>
|
|
||||||
_embyApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
show.ItemId,
|
|
||||||
EmbyItemType.Season);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<EmbySeason> GetSeasonLibraryItems(
|
|
||||||
EmbyLibrary library,
|
EmbyLibrary library,
|
||||||
EmbyConnectionParameters connectionParameters,
|
EmbyConnectionParameters connectionParameters,
|
||||||
EmbyShow show) =>
|
EmbyShow show) =>
|
||||||
@@ -114,17 +95,7 @@ public class EmbyTelevisionLibraryScanner : MediaServerTelevisionLibraryScanner<
|
|||||||
library,
|
library,
|
||||||
show.ItemId);
|
show.ItemId);
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountEpisodeLibraryItems(
|
protected override IAsyncEnumerable<Tuple<EmbyEpisode, int>> GetEpisodeLibraryItems(
|
||||||
EmbyConnectionParameters connectionParameters,
|
|
||||||
EmbyLibrary library,
|
|
||||||
EmbySeason season) =>
|
|
||||||
_embyApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
season.ItemId,
|
|
||||||
EmbyItemType.Episode);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<EmbyEpisode> GetEpisodeLibraryItems(
|
|
||||||
EmbyLibrary library,
|
EmbyLibrary library,
|
||||||
EmbyConnectionParameters connectionParameters,
|
EmbyConnectionParameters connectionParameters,
|
||||||
EmbyShow show,
|
EmbyShow show,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class JellyfinCollectionScanner : IJellyfinCollectionScanner
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to call get libraries to find library that contains collections (box sets)
|
// need to call get libraries to find library that contains collections (box sets)
|
||||||
await _jellyfinApiClient.GetLibraries(address, apiKey);
|
await _jellyfinApiClient.GetLibraries(address, apiKey);
|
||||||
|
|
||||||
var incomingItemIds = new List<string>();
|
var incomingItemIds = new List<string>();
|
||||||
@@ -51,7 +51,7 @@ public class JellyfinCollectionScanner : IJellyfinCollectionScanner
|
|||||||
List<JellyfinCollection> existingCollections = await _jellyfinCollectionRepository.GetCollections();
|
List<JellyfinCollection> existingCollections = await _jellyfinCollectionRepository.GetCollections();
|
||||||
|
|
||||||
// loop over collections
|
// loop over collections
|
||||||
await foreach (JellyfinCollection collection in _jellyfinApiClient.GetCollectionLibraryItems(
|
await foreach ((JellyfinCollection collection, int _) in _jellyfinApiClient.GetCollectionLibraryItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
apiKey,
|
||||||
mediaSourceId))
|
mediaSourceId))
|
||||||
@@ -105,7 +105,7 @@ public class JellyfinCollectionScanner : IJellyfinCollectionScanner
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// get collection items from JF
|
// get collection items from JF
|
||||||
IAsyncEnumerable<MediaItem> items = _jellyfinApiClient.GetCollectionItems(
|
IAsyncEnumerable<Tuple<MediaItem, int>> items = _jellyfinApiClient.GetCollectionItems(
|
||||||
address,
|
address,
|
||||||
apiKey,
|
apiKey,
|
||||||
mediaSourceId,
|
mediaSourceId,
|
||||||
@@ -115,7 +115,7 @@ public class JellyfinCollectionScanner : IJellyfinCollectionScanner
|
|||||||
|
|
||||||
// sync tags on items
|
// sync tags on items
|
||||||
var addedIds = new List<int>();
|
var addedIds = new List<int>();
|
||||||
await foreach (MediaItem item in items)
|
await foreach ((MediaItem item, int _) in items)
|
||||||
{
|
{
|
||||||
addedIds.Add(await _jellyfinCollectionRepository.AddTag(item, collection));
|
addedIds.Add(await _jellyfinCollectionRepository.AddTag(item, collection));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,18 +76,7 @@ public class JellyfinMovieLibraryScanner :
|
|||||||
|
|
||||||
protected override string MediaServerEtag(JellyfinMovie movie) => movie.Etag;
|
protected override string MediaServerEtag(JellyfinMovie movie) => movie.Etag;
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountMovieLibraryItems(
|
protected override IAsyncEnumerable<Tuple<JellyfinMovie, int>> GetMovieLibraryItems(
|
||||||
JellyfinConnectionParameters connectionParameters,
|
|
||||||
JellyfinLibrary library) =>
|
|
||||||
_jellyfinApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
library,
|
|
||||||
library.ItemId,
|
|
||||||
JellyfinItemType.Movie,
|
|
||||||
true);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<JellyfinMovie> GetMovieLibraryItems(
|
|
||||||
JellyfinConnectionParameters connectionParameters,
|
JellyfinConnectionParameters connectionParameters,
|
||||||
JellyfinLibrary library) =>
|
JellyfinLibrary library) =>
|
||||||
_jellyfinApiClient.GetMovieLibraryItems(
|
_jellyfinApiClient.GetMovieLibraryItems(
|
||||||
|
|||||||
@@ -73,18 +73,7 @@ public class JellyfinTelevisionLibraryScanner : MediaServerTelevisionLibraryScan
|
|||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountShowLibraryItems(
|
protected override IAsyncEnumerable<Tuple<JellyfinShow, int>> GetShowLibraryItems(
|
||||||
JellyfinConnectionParameters connectionParameters,
|
|
||||||
JellyfinLibrary library)
|
|
||||||
=> _jellyfinApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
library,
|
|
||||||
library.ItemId,
|
|
||||||
JellyfinItemType.Show,
|
|
||||||
false);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<JellyfinShow> GetShowLibraryItems(
|
|
||||||
JellyfinConnectionParameters connectionParameters,
|
JellyfinConnectionParameters connectionParameters,
|
||||||
JellyfinLibrary library) =>
|
JellyfinLibrary library) =>
|
||||||
_jellyfinApiClient.GetShowLibraryItems(connectionParameters.Address, connectionParameters.ApiKey, library);
|
_jellyfinApiClient.GetShowLibraryItems(connectionParameters.Address, connectionParameters.ApiKey, library);
|
||||||
@@ -97,19 +86,7 @@ public class JellyfinTelevisionLibraryScanner : MediaServerTelevisionLibraryScan
|
|||||||
protected override string MediaServerEtag(JellyfinSeason season) => season.Etag;
|
protected override string MediaServerEtag(JellyfinSeason season) => season.Etag;
|
||||||
protected override string MediaServerEtag(JellyfinEpisode episode) => episode.Etag;
|
protected override string MediaServerEtag(JellyfinEpisode episode) => episode.Etag;
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountSeasonLibraryItems(
|
protected override IAsyncEnumerable<Tuple<JellyfinSeason, int>> GetSeasonLibraryItems(
|
||||||
JellyfinConnectionParameters connectionParameters,
|
|
||||||
JellyfinLibrary library,
|
|
||||||
JellyfinShow show) =>
|
|
||||||
_jellyfinApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
library,
|
|
||||||
show.ItemId,
|
|
||||||
JellyfinItemType.Season,
|
|
||||||
false);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<JellyfinSeason> GetSeasonLibraryItems(
|
|
||||||
JellyfinLibrary library,
|
JellyfinLibrary library,
|
||||||
JellyfinConnectionParameters connectionParameters,
|
JellyfinConnectionParameters connectionParameters,
|
||||||
JellyfinShow show) =>
|
JellyfinShow show) =>
|
||||||
@@ -119,19 +96,7 @@ public class JellyfinTelevisionLibraryScanner : MediaServerTelevisionLibraryScan
|
|||||||
library,
|
library,
|
||||||
show.ItemId);
|
show.ItemId);
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountEpisodeLibraryItems(
|
protected override IAsyncEnumerable<Tuple<JellyfinEpisode, int>> GetEpisodeLibraryItems(
|
||||||
JellyfinConnectionParameters connectionParameters,
|
|
||||||
JellyfinLibrary library,
|
|
||||||
JellyfinSeason season) =>
|
|
||||||
_jellyfinApiClient.GetLibraryItemCount(
|
|
||||||
connectionParameters.Address,
|
|
||||||
connectionParameters.ApiKey,
|
|
||||||
library,
|
|
||||||
season.ItemId,
|
|
||||||
JellyfinItemType.Episode,
|
|
||||||
true);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<JellyfinEpisode> GetEpisodeLibraryItems(
|
|
||||||
JellyfinLibrary library,
|
JellyfinLibrary library,
|
||||||
JellyfinConnectionParameters connectionParameters,
|
JellyfinConnectionParameters connectionParameters,
|
||||||
JellyfinShow show,
|
JellyfinShow show,
|
||||||
|
|||||||
@@ -48,27 +48,14 @@ public abstract class MediaServerMovieLibraryScanner<TConnectionParameters, TLib
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Either<BaseError, int> maybeCount = await CountMovieLibraryItems(connectionParameters, library);
|
return await ScanLibrary(
|
||||||
foreach (BaseError error in maybeCount.LeftToSeq())
|
movieRepository,
|
||||||
{
|
connectionParameters,
|
||||||
return error;
|
library,
|
||||||
}
|
getLocalPath,
|
||||||
|
GetMovieLibraryItems(connectionParameters, library),
|
||||||
foreach (int count in maybeCount.RightToSeq())
|
deepScan,
|
||||||
{
|
cancellationToken);
|
||||||
return await ScanLibrary(
|
|
||||||
movieRepository,
|
|
||||||
connectionParameters,
|
|
||||||
library,
|
|
||||||
getLocalPath,
|
|
||||||
GetMovieLibraryItems(connectionParameters, library),
|
|
||||||
count,
|
|
||||||
deepScan,
|
|
||||||
cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this won't happen
|
|
||||||
return Unit.Default;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
|
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -81,8 +68,7 @@ public abstract class MediaServerMovieLibraryScanner<TConnectionParameters, TLib
|
|||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
TLibrary library,
|
TLibrary library,
|
||||||
Func<TMovie, string> getLocalPath,
|
Func<TMovie, string> getLocalPath,
|
||||||
IAsyncEnumerable<TMovie> movieEntries,
|
IAsyncEnumerable<Tuple<TMovie, int>> movieEntries,
|
||||||
int totalMovieCount,
|
|
||||||
bool deepScan,
|
bool deepScan,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -90,7 +76,7 @@ public abstract class MediaServerMovieLibraryScanner<TConnectionParameters, TLib
|
|||||||
IReadOnlyDictionary<string, TEtag> existingMovies = (await movieRepository.GetExistingMovies(library))
|
IReadOnlyDictionary<string, TEtag> existingMovies = (await movieRepository.GetExistingMovies(library))
|
||||||
.ToImmutableDictionary(e => e.MediaServerItemId, e => e);
|
.ToImmutableDictionary(e => e.MediaServerItemId, e => e);
|
||||||
|
|
||||||
await foreach (TMovie incoming in movieEntries.WithCancellation(cancellationToken))
|
await foreach ((TMovie incoming, int totalMovieCount) in movieEntries.WithCancellation(cancellationToken))
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -237,11 +223,7 @@ public abstract class MediaServerMovieLibraryScanner<TConnectionParameters, TLib
|
|||||||
protected abstract string MediaServerItemId(TMovie movie);
|
protected abstract string MediaServerItemId(TMovie movie);
|
||||||
protected abstract string MediaServerEtag(TMovie movie);
|
protected abstract string MediaServerEtag(TMovie movie);
|
||||||
|
|
||||||
protected abstract Task<Either<BaseError, int>> CountMovieLibraryItems(
|
protected abstract IAsyncEnumerable<Tuple<TMovie, int>> GetMovieLibraryItems(
|
||||||
TConnectionParameters connectionParameters,
|
|
||||||
TLibrary library);
|
|
||||||
|
|
||||||
protected abstract IAsyncEnumerable<TMovie> GetMovieLibraryItems(
|
|
||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
TLibrary library);
|
TLibrary library);
|
||||||
|
|
||||||
|
|||||||
@@ -50,29 +50,14 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Either<BaseError, int> maybeCount = await CountShowLibraryItems(connectionParameters, library);
|
return await ScanLibrary(
|
||||||
foreach (BaseError error in maybeCount.LeftToSeq())
|
televisionRepository,
|
||||||
{
|
connectionParameters,
|
||||||
return error;
|
library,
|
||||||
}
|
getLocalPath,
|
||||||
|
GetShowLibraryItems(connectionParameters, library),
|
||||||
foreach (int count in maybeCount.RightToSeq())
|
deepScan,
|
||||||
{
|
cancellationToken);
|
||||||
_logger.LogDebug("Library {Library} contains {Count} shows", library.Name, count);
|
|
||||||
|
|
||||||
return await ScanLibrary(
|
|
||||||
televisionRepository,
|
|
||||||
connectionParameters,
|
|
||||||
library,
|
|
||||||
getLocalPath,
|
|
||||||
GetShowLibraryItems(connectionParameters, library),
|
|
||||||
count,
|
|
||||||
deepScan,
|
|
||||||
cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this won't happen
|
|
||||||
return Unit.Default;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
|
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -80,11 +65,7 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Task<Either<BaseError, int>> CountShowLibraryItems(
|
protected abstract IAsyncEnumerable<Tuple<TShow, int>> GetShowLibraryItems(
|
||||||
TConnectionParameters connectionParameters,
|
|
||||||
TLibrary library);
|
|
||||||
|
|
||||||
protected abstract IAsyncEnumerable<TShow> GetShowLibraryItems(
|
|
||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
TLibrary library);
|
TLibrary library);
|
||||||
|
|
||||||
@@ -100,15 +81,14 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
TLibrary library,
|
TLibrary library,
|
||||||
Func<TEpisode, string> getLocalPath,
|
Func<TEpisode, string> getLocalPath,
|
||||||
IAsyncEnumerable<TShow> showEntries,
|
IAsyncEnumerable<Tuple<TShow, int>> showEntries,
|
||||||
int totalShowCount,
|
|
||||||
bool deepScan,
|
bool deepScan,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var incomingItemIds = new List<string>();
|
var incomingItemIds = new List<string>();
|
||||||
List<TEtag> existingShows = await televisionRepository.GetExistingShows(library);
|
List<TEtag> existingShows = await televisionRepository.GetExistingShows(library);
|
||||||
|
|
||||||
await foreach (TShow incoming in showEntries.WithCancellation(cancellationToken))
|
await foreach ((TShow incoming, int totalShowCount) in showEntries.WithCancellation(cancellationToken))
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -146,23 +126,6 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
|
|
||||||
foreach (MediaItemScanResult<TShow> result in maybeShow.RightToSeq())
|
foreach (MediaItemScanResult<TShow> result in maybeShow.RightToSeq())
|
||||||
{
|
{
|
||||||
Either<BaseError, int> maybeCount = await CountSeasonLibraryItems(
|
|
||||||
connectionParameters,
|
|
||||||
library,
|
|
||||||
result.Item);
|
|
||||||
foreach (BaseError error in maybeCount.LeftToSeq())
|
|
||||||
{
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int count in maybeCount.RightToSeq())
|
|
||||||
{
|
|
||||||
_logger.LogDebug(
|
|
||||||
"Show {Title} contains {Count} seasons",
|
|
||||||
result.Item.ShowMetadata.Head().Title,
|
|
||||||
count);
|
|
||||||
}
|
|
||||||
|
|
||||||
Either<BaseError, Unit> scanResult = await ScanSeasons(
|
Either<BaseError, Unit> scanResult = await ScanSeasons(
|
||||||
televisionRepository,
|
televisionRepository,
|
||||||
library,
|
library,
|
||||||
@@ -220,22 +183,12 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
return Unit.Default;
|
return Unit.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Task<Either<BaseError, int>> CountSeasonLibraryItems(
|
protected abstract IAsyncEnumerable<Tuple<TSeason, int>> GetSeasonLibraryItems(
|
||||||
TConnectionParameters connectionParameters,
|
|
||||||
TLibrary library,
|
|
||||||
TShow show);
|
|
||||||
|
|
||||||
protected abstract IAsyncEnumerable<TSeason> GetSeasonLibraryItems(
|
|
||||||
TLibrary library,
|
TLibrary library,
|
||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
TShow show);
|
TShow show);
|
||||||
|
|
||||||
protected abstract Task<Either<BaseError, int>> CountEpisodeLibraryItems(
|
protected abstract IAsyncEnumerable<Tuple<TEpisode, int>> GetEpisodeLibraryItems(
|
||||||
TConnectionParameters connectionParameters,
|
|
||||||
TLibrary library,
|
|
||||||
TSeason season);
|
|
||||||
|
|
||||||
protected abstract IAsyncEnumerable<TEpisode> GetEpisodeLibraryItems(
|
|
||||||
TLibrary library,
|
TLibrary library,
|
||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
TShow show,
|
TShow show,
|
||||||
@@ -293,14 +246,14 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
TShow show,
|
TShow show,
|
||||||
bool showIsUpdated,
|
bool showIsUpdated,
|
||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
IAsyncEnumerable<TSeason> seasonEntries,
|
IAsyncEnumerable<Tuple<TSeason, int>> seasonEntries,
|
||||||
bool deepScan,
|
bool deepScan,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var incomingItemIds = new List<string>();
|
var incomingItemIds = new List<string>();
|
||||||
List<TEtag> existingSeasons = await televisionRepository.GetExistingSeasons(library, show);
|
List<TEtag> existingSeasons = await televisionRepository.GetExistingSeasons(library, show);
|
||||||
|
|
||||||
await foreach (TSeason incoming in seasonEntries.WithCancellation(cancellationToken))
|
await foreach ((TSeason incoming, int _) in seasonEntries.WithCancellation(cancellationToken))
|
||||||
{
|
{
|
||||||
incoming.ShowId = show.Id;
|
incoming.ShowId = show.Id;
|
||||||
|
|
||||||
@@ -331,24 +284,6 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
|
|
||||||
foreach (MediaItemScanResult<TSeason> result in maybeSeason.RightToSeq())
|
foreach (MediaItemScanResult<TSeason> result in maybeSeason.RightToSeq())
|
||||||
{
|
{
|
||||||
Either<BaseError, int> maybeCount = await CountEpisodeLibraryItems(
|
|
||||||
connectionParameters,
|
|
||||||
library,
|
|
||||||
result.Item);
|
|
||||||
foreach (BaseError error in maybeCount.LeftToSeq())
|
|
||||||
{
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int count in maybeCount.RightToSeq())
|
|
||||||
{
|
|
||||||
_logger.LogDebug(
|
|
||||||
"Show {Title} season {Season} contains {Count} episodes",
|
|
||||||
show.ShowMetadata.Head().Title,
|
|
||||||
result.Item.SeasonNumber,
|
|
||||||
count);
|
|
||||||
}
|
|
||||||
|
|
||||||
Either<BaseError, Unit> scanResult = await ScanEpisodes(
|
Either<BaseError, Unit> scanResult = await ScanEpisodes(
|
||||||
televisionRepository,
|
televisionRepository,
|
||||||
library,
|
library,
|
||||||
@@ -408,14 +343,14 @@ public abstract class MediaServerTelevisionLibraryScanner<TConnectionParameters,
|
|||||||
bool showIsUpdated,
|
bool showIsUpdated,
|
||||||
TSeason season,
|
TSeason season,
|
||||||
TConnectionParameters connectionParameters,
|
TConnectionParameters connectionParameters,
|
||||||
IAsyncEnumerable<TEpisode> episodeEntries,
|
IAsyncEnumerable<Tuple<TEpisode, int>> episodeEntries,
|
||||||
bool deepScan,
|
bool deepScan,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var incomingItemIds = new List<string>();
|
var incomingItemIds = new List<string>();
|
||||||
List<TEtag> existingEpisodes = await televisionRepository.GetExistingEpisodes(library, season);
|
List<TEtag> existingEpisodes = await televisionRepository.GetExistingEpisodes(library, season);
|
||||||
|
|
||||||
await foreach (TEpisode incoming in episodeEntries.WithCancellation(cancellationToken))
|
await foreach ((TEpisode incoming, int _) in episodeEntries.WithCancellation(cancellationToken))
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class PlexCollectionScanner : IPlexCollectionScanner
|
|||||||
// get all collections from db (key, etag)
|
// get all collections from db (key, etag)
|
||||||
List<PlexCollection> existingCollections = await _plexCollectionRepository.GetCollections();
|
List<PlexCollection> existingCollections = await _plexCollectionRepository.GetCollections();
|
||||||
|
|
||||||
await foreach (PlexCollection collection in _plexServerApiClient.GetAllCollections(
|
await foreach ((PlexCollection collection, int _) in _plexServerApiClient.GetAllCollections(
|
||||||
connection,
|
connection,
|
||||||
token,
|
token,
|
||||||
cancellationToken))
|
cancellationToken))
|
||||||
@@ -93,7 +93,7 @@ public class PlexCollectionScanner : IPlexCollectionScanner
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// get collection items from Plex
|
// get collection items from Plex
|
||||||
IAsyncEnumerable<MediaItem> items = _plexServerApiClient.GetCollectionItems(
|
IAsyncEnumerable<Tuple<MediaItem, int>> items = _plexServerApiClient.GetCollectionItems(
|
||||||
connection,
|
connection,
|
||||||
token,
|
token,
|
||||||
collection.Key,
|
collection.Key,
|
||||||
@@ -103,7 +103,7 @@ public class PlexCollectionScanner : IPlexCollectionScanner
|
|||||||
|
|
||||||
// sync tags on items
|
// sync tags on items
|
||||||
var addedIds = new List<int>();
|
var addedIds = new List<int>();
|
||||||
await foreach (MediaItem item in items)
|
await foreach ((MediaItem item, int _) in items)
|
||||||
{
|
{
|
||||||
addedIds.Add(await _plexCollectionRepository.AddTag(item, collection));
|
addedIds.Add(await _plexCollectionRepository.AddTag(item, collection));
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@@ -82,15 +82,7 @@ public class PlexMovieLibraryScanner :
|
|||||||
|
|
||||||
protected override string MediaServerEtag(PlexMovie movie) => movie.Etag;
|
protected override string MediaServerEtag(PlexMovie movie) => movie.Etag;
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountMovieLibraryItems(
|
protected override IAsyncEnumerable<Tuple<PlexMovie, int>> GetMovieLibraryItems(
|
||||||
PlexConnectionParameters connectionParameters,
|
|
||||||
PlexLibrary library)
|
|
||||||
=> _plexServerApiClient.GetLibraryItemCount(
|
|
||||||
library,
|
|
||||||
connectionParameters.Connection,
|
|
||||||
connectionParameters.Token);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<PlexMovie> GetMovieLibraryItems(
|
|
||||||
PlexConnectionParameters connectionParameters,
|
PlexConnectionParameters connectionParameters,
|
||||||
PlexLibrary library) =>
|
PlexLibrary library) =>
|
||||||
_plexServerApiClient.GetMovieLibraryContents(
|
_plexServerApiClient.GetMovieLibraryContents(
|
||||||
|
|||||||
@@ -130,15 +130,7 @@ public class PlexTelevisionLibraryScanner :
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountShowLibraryItems(
|
protected override IAsyncEnumerable<Tuple<PlexShow, int>> GetShowLibraryItems(
|
||||||
PlexConnectionParameters connectionParameters,
|
|
||||||
PlexLibrary library) =>
|
|
||||||
_plexServerApiClient.GetLibraryItemCount(
|
|
||||||
library,
|
|
||||||
connectionParameters.Connection,
|
|
||||||
connectionParameters.Token);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<PlexShow> GetShowLibraryItems(
|
|
||||||
PlexConnectionParameters connectionParameters,
|
PlexConnectionParameters connectionParameters,
|
||||||
PlexLibrary library) =>
|
PlexLibrary library) =>
|
||||||
_plexServerApiClient.GetShowLibraryContents(
|
_plexServerApiClient.GetShowLibraryContents(
|
||||||
@@ -146,16 +138,7 @@ public class PlexTelevisionLibraryScanner :
|
|||||||
connectionParameters.Connection,
|
connectionParameters.Connection,
|
||||||
connectionParameters.Token);
|
connectionParameters.Token);
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountSeasonLibraryItems(
|
protected override IAsyncEnumerable<Tuple<PlexSeason, int>> GetSeasonLibraryItems(
|
||||||
PlexConnectionParameters connectionParameters,
|
|
||||||
PlexLibrary library,
|
|
||||||
PlexShow show) =>
|
|
||||||
_plexServerApiClient.CountShowSeasons(
|
|
||||||
show,
|
|
||||||
connectionParameters.Connection,
|
|
||||||
connectionParameters.Token);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<PlexSeason> GetSeasonLibraryItems(
|
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexConnectionParameters connectionParameters,
|
PlexConnectionParameters connectionParameters,
|
||||||
PlexShow show) =>
|
PlexShow show) =>
|
||||||
@@ -165,16 +148,7 @@ public class PlexTelevisionLibraryScanner :
|
|||||||
connectionParameters.Connection,
|
connectionParameters.Connection,
|
||||||
connectionParameters.Token);
|
connectionParameters.Token);
|
||||||
|
|
||||||
protected override Task<Either<BaseError, int>> CountEpisodeLibraryItems(
|
protected override IAsyncEnumerable<Tuple<PlexEpisode, int>> GetEpisodeLibraryItems(
|
||||||
PlexConnectionParameters connectionParameters,
|
|
||||||
PlexLibrary library,
|
|
||||||
PlexSeason season) =>
|
|
||||||
_plexServerApiClient.CountSeasonEpisodes(
|
|
||||||
season,
|
|
||||||
connectionParameters.Connection,
|
|
||||||
connectionParameters.Token);
|
|
||||||
|
|
||||||
protected override IAsyncEnumerable<PlexEpisode> GetEpisodeLibraryItems(
|
|
||||||
PlexLibrary library,
|
PlexLibrary library,
|
||||||
PlexConnectionParameters connectionParameters,
|
PlexConnectionParameters connectionParameters,
|
||||||
PlexShow show,
|
PlexShow show,
|
||||||
|
|||||||
Reference in New Issue
Block a user