diff --git a/Jellyfin.Plugin.Tvdb/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.Tvdb/Configuration/PluginConfiguration.cs
index 9e8a445..d112aa0 100644
--- a/Jellyfin.Plugin.Tvdb/Configuration/PluginConfiguration.cs
+++ b/Jellyfin.Plugin.Tvdb/Configuration/PluginConfiguration.cs
@@ -8,13 +8,16 @@ namespace Jellyfin.Plugin.Tvdb.Configuration
public class PluginConfiguration : BasePluginConfiguration
{
///
- /// Gets or sets the tvdb api key for project.
+ /// Gets the tvdb api key for project.
///
- public string ProjectApiKey { get; set; } = string.Empty; // Jellyin Project API Key
+ public const string ProjectApiKey = "";
///
/// Gets or sets the tvdb api key for user.
///
- public string ApiKey { get; set; } = string.Empty; // User API PIN
+ ///
+ /// This is the subscriber's pin.
+ ///
+ public string ApiKey { get; set; } = string.Empty;
}
}
diff --git a/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs b/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs
index e6a07e6..4c3a039 100644
--- a/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs
+++ b/Jellyfin.Plugin.Tvdb/Providers/TvdbSeriesProvider.cs
@@ -471,7 +471,7 @@ private static void MapSeriesToResult(MetadataResult result, SeriesExten
series.Overview = tvdbSeries.Translations.OverviewTranslations.FirstOrDefault(x => string.Equals(x.Language, TvdbUtils.NormalizeLanguageToTvdb(info.MetadataLanguage), StringComparison.OrdinalIgnoreCase))?.Overview ?? tvdbSeries.Overview;
series.OriginalTitle = tvdbSeries.Name;
result.ResultLanguage = info.MetadataLanguage;
- series.AirDays = TvdbUtils.GetAirDays(tvdbSeries.AirsDays);
+ series.AirDays = TvdbUtils.GetAirDays(tvdbSeries.AirsDays).ToArray();
series.AirTime = tvdbSeries.AirsTime;
// series.CommunityRating = (float?)tvdbSeries.SiteRating;
// Attempts to default to USA if not found
diff --git a/Jellyfin.Plugin.Tvdb/TvdbClientManager.cs b/Jellyfin.Plugin.Tvdb/TvdbClientManager.cs
index 757ae7c..1a87780 100644
--- a/Jellyfin.Plugin.Tvdb/TvdbClientManager.cs
+++ b/Jellyfin.Plugin.Tvdb/TvdbClientManager.cs
@@ -1,451 +1,443 @@
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
+using System.Net;
using System.Net.Http;
-using System.Reflection;
-using System.Runtime.CompilerServices;
+using System.Net.Http.Headers;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
+using Jellyfin.Plugin.Tvdb.Configuration;
+using MediaBrowser.Common;
using MediaBrowser.Controller.Providers;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Tvdb.Sdk;
-namespace Jellyfin.Plugin.Tvdb
+namespace Jellyfin.Plugin.Tvdb;
+
+///
+/// Tvdb client manager.
+///
+public class TvdbClientManager
{
+ private const string TvdbHttpClient = "TvdbHttpClient";
+ private static readonly SemaphoreSlim _tokenUpdateLock = new SemaphoreSlim(1, 1);
+
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly IServiceProvider _serviceProvider;
+ private readonly SdkClientSettings _sdkClientSettings;
+
+ private DateTime _tokenUpdatedAt;
+
///
- /// Tvdb client manager.
+ /// Initializes a new instance of the class.
///
- public class TvdbClientManager
+ /// Instance of the interface.
+ public TvdbClientManager(IApplicationHost applicationHost)
{
- private readonly IMemoryCache _cache;
- private readonly ServiceProvider _serviceProvider;
-
- private static SemaphoreSlim _tokenUpdateLock = new SemaphoreSlim(1, 1);
- private DateTime _tokenUpdatedAt;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Instance of the interface.
- /// Instance of the interface.
- public TvdbClientManager(IMemoryCache memoryCache, IHttpClientFactory httpClientFactory)
- {
- _cache = memoryCache;
- _tokenUpdatedAt = DateTime.MinValue;
- _serviceProvider = ConfigureService();
- }
+ _serviceProvider = ConfigureService(applicationHost);
+ _httpClientFactory = _serviceProvider.GetRequiredService();
+ _sdkClientSettings = _serviceProvider.GetRequiredService();
- private static string? ProjectApiKey => TvdbPlugin.Instance?.Configuration.ProjectApiKey;
+ _tokenUpdatedAt = DateTime.MinValue;
+ }
- private static string? ApiKey => TvdbPlugin.Instance?.Configuration.ApiKey;
+ private static string? UserPin => TvdbPlugin.Instance?.Configuration.ApiKey;
- ///
- /// Logs in or refresh login to the tvdb api when needed.
- ///
- private async Task LoginAsync()
+ ///
+ /// Logs in or refresh login to the tvdb api when needed.
+ ///
+ private async Task LoginAsync()
+ {
+ var loginClient = _serviceProvider.GetRequiredService();
+ if (string.IsNullOrEmpty(UserPin))
{
- var loginClient = _serviceProvider.GetRequiredService();
- var sdkClientSettings = _serviceProvider.GetRequiredService();
+ throw new InvalidOperationException("Subscriber PIN not set");
+ }
- // First time authenticating if the token was never updated or if it's empty in the client
- if (_tokenUpdatedAt == DateTime.MinValue || string.IsNullOrEmpty(sdkClientSettings.AccessToken))
+ // Ensure we have a recent token.
+ if (IsTokenInvalid())
+ {
+ await _tokenUpdateLock.WaitAsync().ConfigureAwait(false);
+ try
{
- await _tokenUpdateLock.WaitAsync().ConfigureAwait(false);
- try
+ if (IsTokenInvalid())
{
- if (string.IsNullOrEmpty(sdkClientSettings.AccessToken))
+ var loginResponse = await loginClient.LoginAsync(new Body
{
- var loginResponse = await loginClient.LoginAsync(new Body
- {
- Apikey = ProjectApiKey,
- Pin = ApiKey
- }).ConfigureAwait(false);
- _tokenUpdatedAt = DateTime.UtcNow;
- sdkClientSettings.AccessToken = loginResponse.Data.Token;
- }
- }
- finally
- {
- _tokenUpdateLock.Release();
+ Apikey = PluginConfiguration.ProjectApiKey,
+ Pin = UserPin
+ }).ConfigureAwait(false);
+
+ _tokenUpdatedAt = DateTime.UtcNow;
+ _sdkClientSettings.AccessToken = loginResponse.Data.Token;
}
}
-
- // Refresh if necessary
- if (_tokenUpdatedAt < DateTime.UtcNow.Subtract(TimeSpan.FromDays(25)))
+ finally
{
- try
- {
- await _tokenUpdateLock.WaitAsync().ConfigureAwait(false);
- if (_tokenUpdatedAt < DateTime.UtcNow.Subtract(TimeSpan.FromDays(25)))
- {
- var loginResponse = await loginClient.LoginAsync(new Body
- {
- Apikey = ProjectApiKey,
- Pin = ApiKey
- }).ConfigureAwait(false);
- _tokenUpdatedAt = DateTime.UtcNow;
- sdkClientSettings.AccessToken = loginResponse.Data.Token;
- }
- }
- finally
- {
- _tokenUpdateLock.Release();
- }
+ _tokenUpdateLock.Release();
}
}
- ///
- /// Get series by name.
- ///
- /// Series name.
- /// Metadata language.
- /// Cancellation token.
- /// The series search result.
- public async Task> GetSeriesByNameAsync(
- string name,
- string language,
- CancellationToken cancellationToken)
- {
- var searchClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var searchResult = await searchClient.GetSearchResultsAsync(query: name, type: "series", limit: 5, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return searchResult.Data;
- }
+ return;
- ///
- /// Get series by id.
- ///
- /// The series tvdb id.
- /// Metadata language.
- /// Cancellation token.
- /// The series response.
- public async Task GetSeriesByIdAsync(
- int tvdbId,
- string language,
- CancellationToken cancellationToken)
- {
- var seriesClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var seriesResult = await seriesClient.GetSeriesBaseAsync(id: tvdbId, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return seriesResult.Data;
- }
+ bool IsTokenInvalid() =>
+ _tokenUpdatedAt == DateTime.MinValue
+ || string.IsNullOrEmpty(_sdkClientSettings.AccessToken)
+ || _tokenUpdatedAt < DateTime.UtcNow.Subtract(TimeSpan.FromDays(25));
+ }
- ///
- /// Get series by id.
- ///
- /// The series tvdb id.
- /// Metadata language.
- /// Cancellation token.
- /// episodes or translations.
- /// Payload size. True for smaller payload.
- /// The series response.
- public async Task GetSeriesExtendedByIdAsync(
- int tvdbId,
- string language,
- CancellationToken cancellationToken,
- Meta4? meta = null,
- bool? small = null)
- {
- var seriesClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var seriesResult = await seriesClient.GetSeriesExtendedAsync(id: tvdbId, meta: meta, @short: small, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return seriesResult.Data;
- }
+ ///
+ /// Get series by name.
+ ///
+ /// Series name.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The series search result.
+ public async Task> GetSeriesByNameAsync(
+ string name,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var searchClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var searchResult = await searchClient.GetSearchResultsAsync(query: name, type: "series", limit: 5, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return searchResult.Data;
+ }
- ///
- /// Get all episodes of series.
- ///
- /// The series tvdb id.
- /// Metadata language.
- /// Season type: default, dvd, absolute etc.
- /// Cancellation token.
- /// All episodes of series.
- public async Task GetSeriesEpisodesAsync(
- int tvdbId,
- string language,
- string seasonType,
- CancellationToken cancellationToken)
- {
- var seriesClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var seriesResult = await seriesClient.GetSeriesEpisodesAsync(id: tvdbId, season_type: seasonType, cancellationToken: cancellationToken, page: 0)
- .ConfigureAwait(false);
- return seriesResult.Data;
- }
+ ///
+ /// Get series by id.
+ ///
+ /// The series tvdb id.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The series response.
+ public async Task GetSeriesByIdAsync(
+ int tvdbId,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var seriesClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var seriesResult = await seriesClient.GetSeriesBaseAsync(id: tvdbId, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return seriesResult.Data;
+ }
- ///
- /// Get Season record.
- ///
- /// The season tvdb id.
- /// Metadata language.
- /// Cancellation token.
- /// The episode record.
- public async Task GetSeasonByIdAsync(
- int seasonTvdbId,
- string language,
- CancellationToken cancellationToken)
- {
- var seasonClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var seasonResult = await seasonClient.GetSeasonExtendedAsync(id: seasonTvdbId, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return seasonResult.Data;
- }
+ ///
+ /// Get series by id.
+ ///
+ /// The series tvdb id.
+ /// Metadata language.
+ /// Cancellation token.
+ /// episodes or translations.
+ /// Payload size. True for smaller payload.
+ /// The series response.
+ public async Task GetSeriesExtendedByIdAsync(
+ int tvdbId,
+ string language,
+ CancellationToken cancellationToken,
+ Meta4? meta = null,
+ bool? small = null)
+ {
+ var seriesClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var seriesResult = await seriesClient.GetSeriesExtendedAsync(id: tvdbId, meta: meta, @short: small, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return seriesResult.Data;
+ }
- ///
- /// Get episode record.
- ///
- /// The episode tvdb id.
- /// Metadata language.
- /// Cancellation token.
- /// The episode record.
- public async Task GetEpisodesAsync(
- int episodeTvdbId,
- string language,
- CancellationToken cancellationToken)
- {
- var episodeClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var episodeResult = await episodeClient.GetEpisodeExtendedAsync(id: episodeTvdbId, meta: Meta.Translations, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return episodeResult.Data;
- }
+ ///
+ /// Get all episodes of series.
+ ///
+ /// The series tvdb id.
+ /// Metadata language.
+ /// Season type: default, dvd, absolute etc.
+ /// Cancellation token.
+ /// All episodes of series.
+ public async Task GetSeriesEpisodesAsync(
+ int tvdbId,
+ string language,
+ string seasonType,
+ CancellationToken cancellationToken)
+ {
+ var seriesClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var seriesResult = await seriesClient.GetSeriesEpisodesAsync(id: tvdbId, season_type: seasonType, cancellationToken: cancellationToken, page: 0)
+ .ConfigureAwait(false);
+ return seriesResult.Data;
+ }
- ///
- /// Get series by remoteId.
- ///
- /// The remote id. Supported RemoteIds are: IMDB, TMDB, Zap2It, TV Maze and EIDR.
- /// Metadata language.
- /// Cancellation token.
- /// The series search result.
- public async Task> GetSeriesByRemoteIdAsync(
- string remoteId,
- string language,
- CancellationToken cancellationToken)
- {
- var searchClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var searchResult = await searchClient.GetSearchResultsByRemoteIdAsync(remoteId: remoteId, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return searchResult.Data;
- }
+ ///
+ /// Get Season record.
+ ///
+ /// The season tvdb id.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The episode record.
+ public async Task GetSeasonByIdAsync(
+ int seasonTvdbId,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var seasonClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var seasonResult = await seasonClient.GetSeasonExtendedAsync(id: seasonTvdbId, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return seasonResult.Data;
+ }
- ///
- /// Get actors by tvdb id.
- ///
- /// People Tvdb id.
- /// Metadata language.
- /// Cancellation token.
- /// The actors attached to the id.
- public async Task GetActorAsync(
- int tvdbId,
- string language,
- CancellationToken cancellationToken)
- {
- var peopleClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var peopleResult = await peopleClient.GetPeopleBaseAsync(id: tvdbId, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return peopleResult.Data;
- }
+ ///
+ /// Get episode record.
+ ///
+ /// The episode tvdb id.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The episode record.
+ public async Task GetEpisodesAsync(
+ int episodeTvdbId,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var episodeClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var episodeResult = await episodeClient.GetEpisodeExtendedAsync(id: episodeTvdbId, meta: Meta.Translations, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return episodeResult.Data;
+ }
- ///
- /// Get image by image tvdb id.
- ///
- /// Tvdb id.
- /// Metadata language.
- /// Cancellation token.
- /// The images attached to the id.
- public async Task GetImageAsync(
- int imageTvdbId,
- string language,
- CancellationToken cancellationToken)
- {
- var artworkClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var artworkResult = await artworkClient.GetArtworkExtendedAsync(id: imageTvdbId, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return artworkResult.Data;
- }
+ ///
+ /// Get series by remoteId.
+ ///
+ /// The remote id. Supported RemoteIds are: IMDB, TMDB, Zap2It, TV Maze and EIDR.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The series search result.
+ public async Task> GetSeriesByRemoteIdAsync(
+ string remoteId,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var searchClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var searchResult = await searchClient.GetSearchResultsByRemoteIdAsync(remoteId: remoteId, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return searchResult.Data;
+ }
- ///
- /// Get image by series tvdb id.
- ///
- /// Tvdb id.
- /// Metadata language.
- /// Cancellation token.
- /// The images attached to the id.
- public async Task GetSeriesImagesAsync(
- int tvdbId,
- string language,
- CancellationToken cancellationToken)
- {
- var seriesClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var seriesResult = await seriesClient.GetSeriesArtworksAsync(id: tvdbId, cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return seriesResult.Data;
- }
+ ///
+ /// Get actors by tvdb id.
+ ///
+ /// People Tvdb id.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The actors attached to the id.
+ public async Task GetActorAsync(
+ int tvdbId,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var peopleClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var peopleResult = await peopleClient.GetPeopleBaseAsync(id: tvdbId, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return peopleResult.Data;
+ }
- ///
- /// Get all tvdb languages.
- ///
- /// Cancellation token.
- /// All tvdb languages.
- public async Task> GetLanguagesAsync(CancellationToken cancellationToken)
- {
- var languagesClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var languagesResult = await languagesClient.GetAllLanguagesAsync(cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return languagesResult.Data;
- }
+ ///
+ /// Get image by image tvdb id.
+ ///
+ /// Tvdb id.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The images attached to the id.
+ public async Task GetImageAsync(
+ int imageTvdbId,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var artworkClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var artworkResult = await artworkClient.GetArtworkExtendedAsync(id: imageTvdbId, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return artworkResult.Data;
+ }
- ///
- /// Gets all tvdb artwork types.
- ///
- /// Cancellation Token.
- /// All tvdb artwork types.
- public async Task> GetArtworkTypeAsync(CancellationToken cancellationToken)
- {
- var artwork_TypesClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- var artwork_TypesResult = await artwork_TypesClient.GetAllArtworkTypesAsync(cancellationToken: cancellationToken)
- .ConfigureAwait(false);
- return artwork_TypesResult.Data;
- }
+ ///
+ /// Get image by series tvdb id.
+ ///
+ /// Tvdb id.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The images attached to the id.
+ public async Task GetSeriesImagesAsync(
+ int tvdbId,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var seriesClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var seriesResult = await seriesClient.GetSeriesArtworksAsync(id: tvdbId, cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return seriesResult.Data;
+ }
- ///
- /// Get an episode's tvdb id.
- ///
- /// Episode search info.
- /// Metadata language.
- /// Cancellation token.
- /// The tvdb id.
- public async Task GetEpisodeTvdbId(
- EpisodeInfo searchInfo,
- string language,
- CancellationToken cancellationToken)
+ ///
+ /// Get all tvdb languages.
+ ///
+ /// Cancellation token.
+ /// All tvdb languages.
+ public async Task> GetLanguagesAsync(CancellationToken cancellationToken)
+ {
+ var languagesClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var languagesResult = await languagesClient.GetAllLanguagesAsync(cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return languagesResult.Data;
+ }
+
+ ///
+ /// Gets all tvdb artwork types.
+ ///
+ /// Cancellation Token.
+ /// All tvdb artwork types.
+ public async Task> GetArtworkTypeAsync(CancellationToken cancellationToken)
+ {
+ var artworkTypesClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ var artworkTypesResult = await artworkTypesClient.GetAllArtworkTypesAsync(cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ return artworkTypesResult.Data;
+ }
+
+ ///
+ /// Get an episode's tvdb id.
+ ///
+ /// Episode search info.
+ /// Metadata language.
+ /// Cancellation token.
+ /// The tvdb id.
+ public async Task GetEpisodeTvdbId(
+ EpisodeInfo searchInfo,
+ string language,
+ CancellationToken cancellationToken)
+ {
+ var seriesClient = _serviceProvider.GetRequiredService();
+ await LoginAsync().ConfigureAwait(false);
+ searchInfo.SeriesProviderIds.TryGetValue(TvdbPlugin.ProviderId, out var seriesTvdbId);
+ int? episodeNumber = null;
+ int? seasonNumber = null;
+ string? airDate = null;
+ bool special = false;
+ // Prefer SxE over premiere date as it is more robust
+ if (searchInfo.IndexNumber.HasValue && searchInfo.ParentIndexNumber.HasValue)
{
- var seriesClient = _serviceProvider.GetRequiredService();
- await LoginAsync().ConfigureAwait(false);
- searchInfo.SeriesProviderIds.TryGetValue(TvdbPlugin.ProviderId, out var seriesTvdbId);
- int? episodeNumber = null;
- int? seasonNumber = null;
- string? airDate = null;
- bool special = false;
- // Prefer SxE over premiere date as it is more robust
- if (searchInfo.IndexNumber.HasValue && searchInfo.ParentIndexNumber.HasValue)
- {
- switch (searchInfo.SeriesDisplayOrder)
- {
- case "dvd":
- episodeNumber = searchInfo.IndexNumber.Value;
- seasonNumber = searchInfo.ParentIndexNumber.Value;
- break;
- case "absolute":
- if (searchInfo.ParentIndexNumber.Value == 0) // check if special
- {
- special = true;
- seasonNumber = 0;
- }
- else
- {
- seasonNumber = 1; // absolute order is always season 1
- }
-
- episodeNumber = searchInfo.IndexNumber.Value;
- break;
- default:
- // aired order
- episodeNumber = searchInfo.IndexNumber.Value;
- seasonNumber = searchInfo.ParentIndexNumber.Value;
- break;
- }
- }
- else if (searchInfo.PremiereDate.HasValue)
+ switch (searchInfo.SeriesDisplayOrder)
{
- // tvdb expects yyyy-mm-dd format
- airDate = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
- }
+ case "dvd":
+ episodeNumber = searchInfo.IndexNumber.Value;
+ seasonNumber = searchInfo.ParentIndexNumber.Value;
+ break;
+ case "absolute":
+ if (searchInfo.ParentIndexNumber.Value == 0) // check if special
+ {
+ special = true;
+ seasonNumber = 0;
+ }
+ else
+ {
+ seasonNumber = 1; // absolute order is always season 1
+ }
- Response56 seriesResponse;
- if (!special)
- {
- switch (searchInfo.SeriesDisplayOrder)
- {
- case "dvd":
- case "absolute":
- seriesResponse = await seriesClient.GetSeriesEpisodesAsync(page: 0, id: Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), season_type: searchInfo.SeriesDisplayOrder, season: seasonNumber, episodeNumber: episodeNumber, airDate: airDate, cancellationToken: cancellationToken).ConfigureAwait(false);
- break;
- default:
- seriesResponse = await seriesClient.GetSeriesEpisodesAsync(page: 0, id: Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), season_type: "default", season: seasonNumber, episodeNumber: episodeNumber, airDate: airDate, cancellationToken: cancellationToken).ConfigureAwait(false);
- break;
- }
+ episodeNumber = searchInfo.IndexNumber.Value;
+ break;
+ default:
+ // aired order
+ episodeNumber = searchInfo.IndexNumber.Value;
+ seasonNumber = searchInfo.ParentIndexNumber.Value;
+ break;
}
- else // when special use default order
- {
- seriesResponse = await seriesClient.GetSeriesEpisodesAsync(page: 0, id: Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), season_type: "default", season: seasonNumber, episodeNumber: episodeNumber, airDate: airDate, cancellationToken: cancellationToken).ConfigureAwait(false);
- }
-
- Data2 seriesData = seriesResponse.Data;
+ }
+ else if (searchInfo.PremiereDate.HasValue)
+ {
+ // tvdb expects yyyy-mm-dd format
+ airDate = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
+ }
- if (seriesData == null || seriesData.Episodes == null || seriesData.Episodes.Count == 0)
- {
- return null;
- }
- else
+ Response56 seriesResponse;
+ if (!special)
+ {
+ switch (searchInfo.SeriesDisplayOrder)
{
- return seriesData.Episodes[0].Id.ToString(CultureInfo.InvariantCulture);
+ case "dvd":
+ case "absolute":
+ seriesResponse = await seriesClient.GetSeriesEpisodesAsync(page: 0, id: Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), season_type: searchInfo.SeriesDisplayOrder, season: seasonNumber, episodeNumber: episodeNumber, airDate: airDate, cancellationToken: cancellationToken).ConfigureAwait(false);
+ break;
+ default:
+ seriesResponse = await seriesClient.GetSeriesEpisodesAsync(page: 0, id: Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), season_type: "default", season: seasonNumber, episodeNumber: episodeNumber, airDate: airDate, cancellationToken: cancellationToken).ConfigureAwait(false);
+ break;
}
}
-
- private static ServiceProvider ConfigureService()
+ else // when special use default order
{
- var services = new ServiceCollection();
- static HttpMessageHandler DefaultHttpClientHandlerDelegate(IServiceProvider service)
- => new SocketsHttpHandler
- {
- AutomaticDecompression = System.Net.DecompressionMethods.All,
- RequestHeaderEncodingSelector = (_, _) => System.Text.Encoding.UTF8
- };
- services.AddSingleton();
-
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
-
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
-
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
+ seriesResponse = await seriesClient.GetSeriesEpisodesAsync(page: 0, id: Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), season_type: "default", season: seasonNumber, episodeNumber: episodeNumber, airDate: airDate, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
+ Data2 seriesData = seriesResponse.Data;
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
+ if (seriesData == null || seriesData.Episodes == null || seriesData.Episodes.Count == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return seriesData.Episodes[0].Id.ToString(CultureInfo.InvariantCulture);
+ }
+ }
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
+ ///
+ /// Create an independent ServiceProvider because registering HttpClients directly into Jellyfin
+ /// causes issues upstream.
+ ///
+ /// Instance of the .
+ /// The service provider.
+ private ServiceProvider ConfigureService(IApplicationHost applicationHost)
+ {
+ var productHeader = ProductInfoHeaderValue.Parse(applicationHost.ApplicationUserAgent);
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
+ var assembly = typeof(TvdbPlugin).Assembly.GetName();
+ var pluginHeader = new ProductInfoHeaderValue(
+ assembly.Name!.Replace(' ', '-').Replace('.', '-'),
+ assembly.Version!.ToString(3));
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
+ var contactHeader = new ProductInfoHeaderValue($"({applicationHost.ApplicationUserAgentAddress})");
- services.AddHttpClient()
- .ConfigurePrimaryHttpMessageHandler(DefaultHttpClientHandlerDelegate);
+ var services = new ServiceCollection();
- return services.BuildServiceProvider();
- }
+ services.AddSingleton();
+ services.AddHttpClient(TvdbHttpClient, c =>
+ {
+ c.DefaultRequestHeaders.UserAgent.Add(productHeader);
+ c.DefaultRequestHeaders.UserAgent.Add(pluginHeader);
+ c.DefaultRequestHeaders.UserAgent.Add(contactHeader);
+ })
+ .ConfigurePrimaryHttpMessageHandler(_ => new SocketsHttpHandler
+ {
+ AutomaticDecompression = DecompressionMethods.All,
+ RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
+ });
+
+ services.AddTransient(_ => new LoginClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new SearchClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new SeriesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new SeasonsClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new EpisodesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new PeopleClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new ArtworkClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new Artwork_TypesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+ services.AddTransient(_ => new LanguagesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
+
+ return services.BuildServiceProvider();
}
}
diff --git a/Jellyfin.Plugin.Tvdb/TvdbUtils.cs b/Jellyfin.Plugin.Tvdb/TvdbUtils.cs
index 92524ec..94400c9 100644
--- a/Jellyfin.Plugin.Tvdb/TvdbUtils.cs
+++ b/Jellyfin.Plugin.Tvdb/TvdbUtils.cs
@@ -1,5 +1,5 @@
-using System;
-using System.Linq;
+using System;
+using System.Collections.Generic;
using MediaBrowser.Model.Entities;
using Tvdb.Sdk;
@@ -15,11 +15,6 @@ public static class TvdbUtils
///
public const string TvdbBaseUrl = "https://www.thetvdb.com/";
- ///
- /// Base url for banners.
- ///
- public const string BannerUrl = TvdbBaseUrl + "banners/";
-
///
/// Get image type from key type.
///
@@ -40,7 +35,7 @@ public static ImageType GetImageTypeFromKeyType(string? keyType)
}
}
- throw new ArgumentException($"Null keytype");
+ throw new ArgumentException("Null keytype");
}
///
@@ -104,21 +99,42 @@ public static ImageType GetImageTypeFromKeyType(string? keyType)
///
/// SeriesAirDays.
/// List{DayOfWeek}.
- public static DayOfWeek[] GetAirDays(SeriesAirsDays seriesAirsDays)
+ public static IEnumerable GetAirDays(SeriesAirsDays seriesAirsDays)
{
- // Convert to DayOfWeek? array and remove nulls
- var airdays = new[]
+ if (seriesAirsDays.Sunday)
+ {
+ yield return DayOfWeek.Sunday;
+ }
+
+ if (seriesAirsDays.Monday)
+ {
+ yield return DayOfWeek.Monday;
+ }
+
+ if (seriesAirsDays.Tuesday)
+ {
+ yield return DayOfWeek.Tuesday;
+ }
+
+ if (seriesAirsDays.Wednesday)
+ {
+ yield return DayOfWeek.Wednesday;
+ }
+
+ if (seriesAirsDays.Thursday)
{
- seriesAirsDays.Monday ? DayOfWeek.Monday : (DayOfWeek?)null,
- seriesAirsDays.Tuesday ? DayOfWeek.Tuesday : (DayOfWeek?)null,
- seriesAirsDays.Wednesday ? DayOfWeek.Wednesday : (DayOfWeek?)null,
- seriesAirsDays.Thursday ? DayOfWeek.Thursday : (DayOfWeek?)null,
- seriesAirsDays.Friday ? DayOfWeek.Friday : (DayOfWeek?)null,
- seriesAirsDays.Saturday ? DayOfWeek.Saturday : (DayOfWeek?)null,
- seriesAirsDays.Sunday ? DayOfWeek.Sunday : (DayOfWeek?)null
- }.Where(i => i.HasValue).ToArray();
- // Convert to DayOfWeek array. Nulls are converted to 0 but all should be removed by now.
- return Array.ConvertAll(airdays, i => i ?? 0);
+ yield return DayOfWeek.Thursday;
+ }
+
+ if (seriesAirsDays.Friday)
+ {
+ yield return DayOfWeek.Friday;
+ }
+
+ if (seriesAirsDays.Saturday)
+ {
+ yield return DayOfWeek.Saturday;
+ }
}
}
}