Skip to content

Commit

Permalink
Use custom season client
Browse files Browse the repository at this point in the history
  • Loading branch information
scampower3 committed Jul 17, 2024
1 parent 1b18035 commit 63a5371
Show file tree
Hide file tree
Showing 7 changed files with 348 additions and 8 deletions.
7 changes: 4 additions & 3 deletions Jellyfin.Plugin.Tvdb/Providers/TvdbSeasonProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.Tvdb.SeasonClient;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
Expand Down Expand Up @@ -112,7 +113,7 @@ public async Task<MetadataResult<Season>> GetMetadata(SeasonInfo info, Cancellat
return MapSeasonToResult(info, seasonInfo);
}

private MetadataResult<Season> MapSeasonToResult(SeasonInfo id, SeasonExtendedRecord season)
private MetadataResult<Season> MapSeasonToResult(SeasonInfo id, CustomSeasonExtendedRecord season)
{
var result = new MetadataResult<Season>
{
Expand All @@ -122,7 +123,7 @@ private MetadataResult<Season> MapSeasonToResult(SeasonInfo id, SeasonExtendedRe
IndexNumber = id.IndexNumber,
// Tvdb uses 3 letter code for language (prob ISO 639-2)
// Reverts to OriginalName if no translation is found
// Overview = season.Translations.GetTranslatedOverviewOrDefault(id.MetadataLanguage),
Overview = season.Translations.GetTranslatedOverviewOrDefault(id.MetadataLanguage),
}
};

Expand All @@ -131,7 +132,7 @@ private MetadataResult<Season> MapSeasonToResult(SeasonInfo id, SeasonExtendedRe

if (ImportSeasonName)
{
// item.Name = season.Translations.GetTranslatedNamedOrDefault(id.MetadataLanguage) ?? TvdbUtils.ReturnOriginalLanguageOrDefault(season.Name);
item.Name = season.Translations.GetTranslatedNamedOrDefaultIgnoreAlias(id.MetadataLanguage) ?? TvdbUtils.ReturnOriginalLanguageOrDefault(season.Name);
item.OriginalTitle = season.Name;
}

Expand Down
72 changes: 72 additions & 0 deletions Jellyfin.Plugin.Tvdb/SeasonClient/CustomSeasonExtendedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CA2227 // Collection properties should be read only

using System.Text.Json.Serialization;
using Tvdb.Sdk;

namespace Jellyfin.Plugin.Tvdb.SeasonClient
{
public sealed class CustomSeasonExtendedRecord
{
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = default!;

[JsonPropertyName("artwork")]
public System.Collections.Generic.IReadOnlyList<ArtworkBaseRecord> Artwork { get; set; } = default!;

[JsonPropertyName("companies")]

public Companies Companies { get; set; } = default!;

[JsonPropertyName("episodes")]
public System.Collections.Generic.IReadOnlyList<EpisodeBaseRecord> Episodes { get; set; } = default!;

[JsonPropertyName("id")]
public int? Id { get; set; } = default!;

[JsonPropertyName("image")]
public string Image { get; set; } = default!;

[JsonPropertyName("imageType")]
public int? ImageType { get; set; }

[JsonPropertyName("lastUpdated")]
public string LastUpdated { get; set; } = default!;

[JsonPropertyName("name")]
public string Name { get; set; } = default!;

[JsonPropertyName("nameTranslations")]
public System.Collections.Generic.IReadOnlyList<string> NameTranslations { get; set; } = default!;

[JsonPropertyName("number")]
public long? Number { get; set; }

[JsonPropertyName("overviewTranslations")]
public System.Collections.Generic.IReadOnlyList<string> OverviewTranslations { get; set; } = default!;

[JsonPropertyName("seriesId")]
public long? SeriesId { get; set; }

[JsonPropertyName("trailers")]
public System.Collections.Generic.IReadOnlyList<Trailer> Trailers { get; set; } = default!;

[JsonPropertyName("type")]
public SeasonType Type { get; set; } = default!;

[JsonPropertyName("tagOptions")]
public System.Collections.Generic.IReadOnlyList<TagOption> TagOptions { get; set; } = default!;

[JsonPropertyName("translations")]
public TranslationExtended Translations { get; set; } = default!;

[JsonPropertyName("year")]
public string Year { get; set; } = default!;

[JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
set { _additionalProperties = value; }
}
}
}
198 changes: 198 additions & 0 deletions Jellyfin.Plugin.Tvdb/SeasonClient/ExtendedSeasonClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods

using System.Text.Json.Serialization;
using Tvdb.Sdk;

namespace Jellyfin.Plugin.Tvdb.SeasonClient
{
/// <summary>
/// Extended season client.
/// </summary>
public sealed partial class ExtendedSeasonClient : SeasonsClient, IExtendedSeasonClient
{
private System.Net.Http.HttpClient _httpClient;

Check notice

Code scanning / CodeQL

Missed 'readonly' opportunity Note

Field '_httpClient' can be 'readonly'.

/// <summary>
/// Initializes a new instance of the <see cref="ExtendedSeasonClient"/> class.
/// </summary>
/// <param name="configuration">Instance of <see cref="SdkClientSettings"/>.</param>
/// <param name="httpClient">Instance of <see cref="System.Net.Http.HttpClient"/>.</param>
public ExtendedSeasonClient(SdkClientSettings configuration, System.Net.Http.HttpClient httpClient) : base(configuration, httpClient)
{
_httpClient = httpClient;
}

private static System.Text.Json.JsonSerializerOptions CreateSerializerSettings()
{
var settings = new System.Text.Json.JsonSerializerOptions();
var converters = new JsonConverter[] { new JsonStringEnumConverter() };
foreach (var converter in converters)
{
settings.Converters.Add(converter);
}

UpdateJsonSerializerSettings(settings);
return settings;
}

static partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings);

/// <inheritdoc/>
public async System.Threading.Tasks.Task<Response99> GetSeasonExtendedWithTranslationsAsync(double id, System.Threading.CancellationToken cancellationToken = default)
{
var client_ = _httpClient;

Check notice

Code scanning / CodeQL

Missed 'using' opportunity Note

This variable is manually
disposed
in a
finally block
- consider a C# using statement as a preferable resource management technique.
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));

var urlBuilder_ = new System.Text.StringBuilder();

// Operation Path: "seasons/{id}/extended"
urlBuilder_.Append("seasons/");
urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture)));
urlBuilder_.Append("/extended?meta=translations");

await PrepareRequestAsync(client_, request_, urlBuilder_, cancellationToken).ConfigureAwait(false);

var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);

await PrepareRequestAsync(client_, request_, url_, cancellationToken).ConfigureAwait(false);

var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);

Check notice

Code scanning / CodeQL

Missed 'using' opportunity Note

This variable is manually
disposed
in a
finally block
- consider a C# using statement as a preferable resource management technique.
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
{
headers_[item_.Key] = item_.Value;
}

if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
{
headers_[item_.Key] = item_.Value;
}
}

await ProcessResponseAsync(client_, response_, cancellationToken).ConfigureAwait(false);

var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<Response99>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new SeasonsException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}

return objectResponse_.Object;
}
else
if (status_ == 400)
{
var responseText_ = response_.Content == null ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new SeasonsException("Invalid seasons id", status_, responseText_, headers_, null);
}
else
if (status_ == 401)
{
var responseText_ = response_.Content == null ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new SeasonsException("Unauthorized", status_, responseText_, headers_, null);
}
else
if (status_ == 404)
{
var responseText_ = response_.Content == null ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new SeasonsException("Season not found", status_, responseText_, headers_, null);
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new SeasonsException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
{
response_.Dispose();
}
}
}
}
finally
{
if (disposeClient_)
{
client_.Dispose();
}
}
}

private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null)
{
return string.Empty;
}

if (value is System.Enum)
{
var name = System.Enum.GetName(value.GetType(), value);
if (name != null)
{
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
return attribute.Value != null ? attribute.Value : name;
}
}

#pragma warning disable CA1305 // Specify IFormatProvider
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
#pragma warning restore CA1305 // Specify IFormatProvider
return converted == null ? string.Empty : converted;
}
}
else if (value is bool)
{
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[])value);
}
else if (value is string[])
{
return string.Join(",", (string[])value);
}
else if (value.GetType().IsArray)
{
var valueArray = (System.Array)value;
var valueTextArray = new string[valueArray.Length];
for (var i = 0; i < valueArray.Length; i++)
{
#pragma warning disable CS8604 // Possible null reference argument.
valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo);
#pragma warning restore CS8604 // Possible null reference argument.
}

return string.Join(",", valueTextArray);
}

var result = System.Convert.ToString(value, cultureInfo);
return result == null ? string.Empty : result;
}
}
}
22 changes: 22 additions & 0 deletions Jellyfin.Plugin.Tvdb/SeasonClient/IExtendedSeasonClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Jellyfin.Plugin.Tvdb.SeasonClient
{
/// <summary>
/// Interface IExtendedSeasonClient.
/// </summary>
public interface IExtendedSeasonClient
{
/// <summary>
/// Gets the season extended with translations.
/// </summary>
/// <param name="id">Season Id.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>response.</returns>
System.Threading.Tasks.Task<Response99> GetSeasonExtendedWithTranslationsAsync(double id, System.Threading.CancellationToken cancellationToken = default);
}
}
26 changes: 26 additions & 0 deletions Jellyfin.Plugin.Tvdb/SeasonClient/Response99.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CA2227 // Collection properties should be read only

using System.Text.Json.Serialization;
using Tvdb.Sdk;

namespace Jellyfin.Plugin.Tvdb.SeasonClient
{
public sealed class Response99
{
private System.Collections.Generic.IDictionary<string, object> _additionalProperties = default!;

[JsonPropertyName("data")]
public CustomSeasonExtendedRecord Data { get; set; } = default!;

[JsonPropertyName("status")]
public string Status { get; set; } = default!;

[JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
set { _additionalProperties = value; }
}
}
}
11 changes: 6 additions & 5 deletions Jellyfin.Plugin.Tvdb/TvdbClientManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.Tvdb.Configuration;
using Jellyfin.Plugin.Tvdb.SeasonClient;
using MediaBrowser.Common;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Globalization;
Expand Down Expand Up @@ -296,21 +297,21 @@ public async Task<Data2> GetSeriesEpisodesAsync(
/// <param name="language">Metadata language.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The episode record.</returns>
public async Task<SeasonExtendedRecord> GetSeasonByIdAsync(
public async Task<CustomSeasonExtendedRecord> GetSeasonByIdAsync(
int seasonTvdbId,
string language,
CancellationToken cancellationToken)
{
var key = $"TvdbSeason_{seasonTvdbId.ToString(CultureInfo.InvariantCulture)}";
if (_memoryCache.TryGetValue(key, out SeasonExtendedRecord? season)
if (_memoryCache.TryGetValue(key, out CustomSeasonExtendedRecord? season)
&& season is not null)
{
return season;
}

var seasonClient = _serviceProvider.GetRequiredService<ISeasonsClient>();
var seasonClient = _serviceProvider.GetRequiredService<IExtendedSeasonClient>();
await LoginAsync().ConfigureAwait(false);
var seasonResult = await seasonClient.GetSeasonExtendedAsync(id: seasonTvdbId, cancellationToken: cancellationToken)
var seasonResult = await seasonClient.GetSeasonExtendedWithTranslationsAsync(id: seasonTvdbId, cancellationToken: cancellationToken)
.ConfigureAwait(false);
_memoryCache.Set(key, seasonResult.Data, TimeSpan.FromHours(CacheDurationInHours));
return seasonResult.Data;
Expand Down Expand Up @@ -689,7 +690,7 @@ private ServiceProvider ConfigureService(IApplicationHost applicationHost)
services.AddTransient<ILoginClient>(_ => new LoginClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<ISearchClient>(_ => new SearchClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<ISeriesClient>(_ => new SeriesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<ISeasonsClient>(_ => new SeasonsClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<IExtendedSeasonClient>(_ => new ExtendedSeasonClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<IEpisodesClient>(_ => new EpisodesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<IPeopleClient>(_ => new PeopleClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<IArtworkClient>(_ => new ArtworkClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
Expand Down
Loading

0 comments on commit 63a5371

Please sign in to comment.