Merge pull request #87 from MBR-0001/apollo

This commit is contained in:
Cody Robibero 2021-12-07 15:37:38 -07:00 committed by GitHub
commit ee797cbc81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 86 additions and 115 deletions

View File

@ -199,12 +199,10 @@ namespace Jellyfin.Plugin.OpenSubtitles
Comment = i.Attributes?.Comments,
CommunityRating = i.Attributes?.Ratings,
DownloadCount = i.Attributes?.DownloadCount,
Format = i.Attributes?.Format ?? "srt",
Format = "srt",
ProviderName = Name,
ThreeLetterISOLanguageName = request.Language,
// new API (currently) does not return the format
Id = $"{i.Attributes?.Format ?? "srt"}-{request.Language}-{i.Attributes?.Files[0].FileId}",
Id = $"srt-{request.Language}-{i.Attributes?.Files[0].FileId}",
Name = i.Attributes?.Release,
DateCreated = i.Attributes?.UploadDate,
IsHashMatch = i.Attributes?.MovieHashMatch
@ -314,7 +312,7 @@ namespace Jellyfin.Plugin.OpenSubtitles
var res = await OpenSubtitlesHandler.OpenSubtitles.DownloadSubtitleAsync(info.Data.Link, cancellationToken).ConfigureAwait(false);
if (!res.Ok || string.IsNullOrWhiteSpace(res.Data))
if (res.Code != HttpStatusCode.OK || string.IsNullOrWhiteSpace(res.Body))
{
var msg = string.Format(
CultureInfo.InvariantCulture,
@ -325,7 +323,7 @@ namespace Jellyfin.Plugin.OpenSubtitles
throw new HttpRequestException(msg);
}
return new SubtitleResponse { Format = format, Language = language, Stream = new MemoryStream(Encoding.UTF8.GetBytes(res.Data)) };
return new SubtitleResponse { Format = format, Language = language, Stream = new MemoryStream(Encoding.UTF8.GetBytes(res.Body)) };
}
private async Task Login(CancellationToken cancellationToken)

View File

@ -42,12 +42,6 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler.Models
Body = response.Reason;
}
if (typeof(T) == typeof(string))
{
Data = (T)(object)Body;
return;
}
if (!Ok)
{
// don't bother parsing json if HTTP status code is bad

View File

@ -15,12 +15,6 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler.Models.Responses
[JsonPropertyName("download_count")]
public int DownloadCount { get; set; }
/// <summary>
/// Gets or sets the subtitle format.
/// </summary>
[JsonPropertyName("format")]
public string? Format { get; set; }
/// <summary>
/// Gets or sets the subtitle rating.
/// </summary>

View File

@ -20,12 +20,6 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler.Models.Responses
[JsonPropertyName("remaining")]
public int Remaining { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
[JsonPropertyName("message")]
public string? Message { get; set; }
/// <summary>
/// Gets or sets the reset time.
/// </summary>

View File

@ -26,7 +26,7 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
public static async Task<ApiResponse<LoginInfo>> LogInAsync(string username, string password, string apiKey, CancellationToken cancellationToken)
{
var body = new { username, password };
var response = await RequestHandler.SendRequestAsync("/login", HttpMethod.Post, body, null, apiKey, cancellationToken).ConfigureAwait(false);
var response = await RequestHandler.SendRequestAsync("/login", HttpMethod.Post, body, null, apiKey, 1, cancellationToken).ConfigureAwait(false);
return new ApiResponse<LoginInfo>(response);
}
@ -47,7 +47,7 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
var headers = new Dictionary<string, string> { { "Authorization", user.Token } };
var response = await RequestHandler.SendRequestAsync("/logout", HttpMethod.Delete, null, headers, apiKey, cancellationToken).ConfigureAwait(false);
var response = await RequestHandler.SendRequestAsync("/logout", HttpMethod.Delete, null, headers, apiKey, 1, cancellationToken).ConfigureAwait(false);
return new ApiResponse<object>(response).Ok;
}
@ -68,7 +68,7 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
var headers = new Dictionary<string, string> { { "Authorization", user.Token } };
var response = await RequestHandler.SendRequestAsync("/infos/user", HttpMethod.Get, null, headers, apiKey, cancellationToken).ConfigureAwait(false);
var response = await RequestHandler.SendRequestAsync("/infos/user", HttpMethod.Get, null, headers, apiKey, 1, cancellationToken).ConfigureAwait(false);
return new ApiResponse<EncapsulatedUserInfo>(response);
}
@ -91,7 +91,7 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
var headers = new Dictionary<string, string> { { "Authorization", user.Token } };
var body = new { file_id = file };
var response = await RequestHandler.SendRequestAsync("/download", HttpMethod.Post, body, headers, apiKey, cancellationToken).ConfigureAwait(false);
var response = await RequestHandler.SendRequestAsync("/download", HttpMethod.Post, body, headers, apiKey, 1, cancellationToken).ConfigureAwait(false);
return new ApiResponse<SubtitleDownloadInfo>(response, $"file id: {file}");
}
@ -101,12 +101,21 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
/// </summary>
/// <param name="url">the subtitle url.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The subtitle string.</returns>
public static async Task<ApiResponse<string>> DownloadSubtitleAsync(string url, CancellationToken cancellationToken)
/// <returns>The Http response.</returns>
public static async Task<HttpResponse> DownloadSubtitleAsync(string url, CancellationToken cancellationToken)
{
var response = await RequestHandler.SendRequestAsync(url, HttpMethod.Get, null, null, null, cancellationToken).ConfigureAwait(false);
var response = await OpenSubtitlesRequestHelper.Instance!.SendRequestAsync(
url,
HttpMethod.Get,
null,
new Dictionary<string, string>(),
cancellationToken).ConfigureAwait(false);
return new ApiResponse<string>(response);
return new HttpResponse
{
Body = response.body,
Code = response.statusCode
};
}
/// <summary>
@ -141,7 +150,7 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
opts.Add(key.ToLower(CultureInfo.InvariantCulture), value.ToLower(CultureInfo.InvariantCulture));
}
response = await RequestHandler.SendRequestAsync($"/subtitles?{opts}", HttpMethod.Get, null, null, apiKey, cancellationToken).ConfigureAwait(false);
response = await RequestHandler.SendRequestAsync($"/subtitles?{opts}", HttpMethod.Get, null, null, apiKey, 1, cancellationToken).ConfigureAwait(false);
last = new ApiResponse<SearchResult>(response, $"query: {opts}", $"page: {current}");
@ -177,7 +186,7 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
/// <returns>The list of languages.</returns>
public static async Task<ApiResponse<EncapsulatedLanguageList>> GetLanguageList(string apiKey, CancellationToken cancellationToken)
{
var response = await RequestHandler.SendRequestAsync("/infos/languages", HttpMethod.Get, null, null, apiKey, cancellationToken).ConfigureAwait(false);
var response = await RequestHandler.SendRequestAsync("/infos/languages", HttpMethod.Get, null, null, apiKey, 1, cancellationToken).ConfigureAwait(false);
return new ApiResponse<EncapsulatedLanguageList>(response);
}

View File

@ -79,7 +79,12 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
}
}
internal async Task<(string, Dictionary<string, string>, HttpStatusCode)> SendRequestAsync(string url, HttpMethod method, object? body, Dictionary<string, string> headers, CancellationToken cancellationToken)
internal async Task<(string body, Dictionary<string, string> headers, HttpStatusCode statusCode)> SendRequestAsync(
string url,
HttpMethod method,
object? body,
Dictionary<string, string> headers,
CancellationToken cancellationToken)
{
var client = _clientFactory.CreateClient("Default");

View File

@ -30,18 +30,21 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
/// <param name="body">The request body.</param>
/// <param name="headers">The headers.</param>
/// <param name="apiKey">The api key.</param>
/// <param name="attempt">The request attempt key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The response.</returns>
/// <exception cref="ArgumentException">API Key is empty.</exception>
public static async Task<HttpResponse> SendRequestAsync(string endpoint, HttpMethod method, object? body, Dictionary<string, string>? headers, string? apiKey, CancellationToken cancellationToken)
public static async Task<HttpResponse> SendRequestAsync(
string endpoint,
HttpMethod method,
object? body,
Dictionary<string, string>? headers,
string? apiKey,
int attempt,
CancellationToken cancellationToken)
{
var url = endpoint.StartsWith('/') ? BaseApiUrl + endpoint : endpoint;
var isFullUrl = url.StartsWith(BaseApiUrl, StringComparison.OrdinalIgnoreCase);
headers ??= new Dictionary<string, string>();
if (isFullUrl)
{
if (string.IsNullOrWhiteSpace(apiKey))
{
throw new ArgumentException("Provided API key is blank", nameof(apiKey));
@ -75,51 +78,48 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
_windowStart = DateTime.UtcNow;
_requestCount = 0;
}
}
var (response, responseHeaders, httpStatusCode) = await OpenSubtitlesRequestHelper.Instance!.SendRequestAsync(url, method, body, headers, cancellationToken).ConfigureAwait(false);
if (!isFullUrl)
{
return new HttpResponse
{
Body = response,
Code = httpStatusCode
};
}
var response = await OpenSubtitlesRequestHelper.Instance!.SendRequestAsync(BaseApiUrl + endpoint, method, body, headers, cancellationToken).ConfigureAwait(false);
_requestCount++;
if (responseHeaders.TryGetValue("x-ratelimit-remaining-second", out var value))
if (response.headers.TryGetValue("x-ratelimit-remaining-second", out var value))
{
_ = int.TryParse(value, out _hRemaining);
}
if (responseHeaders.TryGetValue("ratelimit-reset", out value))
if (response.headers.TryGetValue("ratelimit-reset", out value))
{
_ = int.TryParse(value, out _hReset);
}
if (httpStatusCode != HttpStatusCode.TooManyRequests)
if (response.statusCode == HttpStatusCode.TooManyRequests && attempt <= 4)
{
if (!responseHeaders.TryGetValue("x-reason", out value))
var time = _hReset == -1 ? 5 : _hReset;
await Task.Delay(time * 1000, cancellationToken).ConfigureAwait(false);
return await SendRequestAsync(endpoint, method, body, headers, apiKey, attempt + 1, cancellationToken).ConfigureAwait(false);
}
if (response.statusCode == HttpStatusCode.BadGateway && attempt <= 3)
{
await Task.Delay(500, cancellationToken).ConfigureAwait(false);
return await SendRequestAsync(endpoint, method, body, headers, apiKey, attempt + 1, cancellationToken).ConfigureAwait(false);
}
if (!response.headers.TryGetValue("x-reason", out value))
{
value = string.Empty;
}
return new HttpResponse
{
Body = response,
Code = httpStatusCode,
Body = response.body,
Code = response.statusCode,
Reason = value
};
}
var time = _hReset == -1 ? 5 : _hReset;
await Task.Delay(time * 1000, cancellationToken).ConfigureAwait(false);
return await SendRequestAsync(endpoint, method, body, headers, apiKey, cancellationToken).ConfigureAwait(false);
}
}
}

View File

@ -1,23 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@ -18,7 +18,7 @@
## About
This is a plugin allows you to download subtitles from [Open Subtitles](https://opensubtitles.org) for your media.
This is a plugin allows you to download subtitles from [Open Subtitles](https://opensubtitles.com) for your media.
## Installation