Add automatic invalidation of credentials (#128)

* Add ability to invalidate credentials

* Add 403 handling, disable save btn while sending

* Add 1s delay on 4xx

* Add 400 handling

* Update Jellyfin.Plugin.OpenSubtitles/OpenSubtitleDownloader.cs

Co-authored-by: Cody Robibero <cody@robibe.ro>

* Fix

---------

Co-authored-by: Cody Robibero <cody@robibe.ro>
This commit is contained in:
MBR-0001 2023-07-02 19:29:39 +02:00 committed by GitHub
parent 81f5bb9562
commit 338c7f6737
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 3 deletions

View File

@ -21,5 +21,10 @@ namespace Jellyfin.Plugin.OpenSubtitles.Configuration
/// Gets or sets the custom API Key.
/// </summary>
public string CustomApiKey { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether the credentials are invalid.
/// </summary>
public bool CredentialsInvalid { get; set; } = false;
}
}

View File

@ -89,6 +89,13 @@ namespace Jellyfin.Plugin.OpenSubtitles
await Login(cancellationToken).ConfigureAwait(false);
if (request.IsAutomated && _login is null)
{
// Login attempt failed, since this is a task to download subtitles there's no point in continuing
_logger.LogDebug("Returning empty results because login failed");
return Enumerable.Empty<RemoteSubtitleInfo>();
}
if (request.IsAutomated && _login?.User?.RemainingDownloads <= 0)
{
if (_lastRatelimitLog == null || DateTime.UtcNow.Subtract(_lastRatelimitLog.Value).TotalSeconds > 60)
@ -346,6 +353,12 @@ namespace Jellyfin.Plugin.OpenSubtitles
throw new AuthenticationException("Account username and/or password are not set up");
}
if (options.CredentialsInvalid)
{
_logger.LogDebug("Skipping login due to credentials being invalid");
return;
}
var loginResponse = await OpenSubtitlesHandler.OpenSubtitles.LogInAsync(
options.Username,
options.Password,
@ -354,7 +367,20 @@ namespace Jellyfin.Plugin.OpenSubtitles
if (!loginResponse.Ok)
{
_logger.LogError("Login failed: {Code} - {Body}", loginResponse.Code, loginResponse.Body);
// 400 = Using email, 401 = invalid credentials, 403 = invalid api key
if ((loginResponse.Code == HttpStatusCode.BadRequest && options.Username.Contains('@', StringComparison.OrdinalIgnoreCase))
|| loginResponse.Code == HttpStatusCode.Unauthorized
|| (loginResponse.Code == HttpStatusCode.Forbidden && ApiKey == options.CustomApiKey))
{
_logger.LogError("Login failed due to invalid credentials/API key, invalidating them ({Code} - {Body})", loginResponse.Code, loginResponse.Body);
options.CredentialsInvalid = true;
OpenSubtitlesPlugin.Instance!.SaveConfiguration(options);
}
else
{
_logger.LogError("Login failed: {Code} - {Body}", loginResponse.Code, loginResponse.Body);
}
throw new AuthenticationException("Authentication to OpenSubtitles failed.");
}

View File

@ -117,6 +117,12 @@ namespace Jellyfin.Plugin.OpenSubtitles.OpenSubtitlesHandler
value = string.Empty;
}
if ((int)response.statusCode >= 400 && (int)response.statusCode <= 499)
{
// Wait 1s after a 4xx response
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
}
return new HttpResponse
{
Body = response.body,

View File

@ -1,8 +1,12 @@
<div id="OpenSubtitlesConfigPage" data-role="page" class="page type-interior pluginConfigurationPage" data-controller="__plugin/opensubtitlesjs">
<div data-role="content">
<div class="content-primary">
<h3 class="sectionTitle sectionTitle-cards"><b>In order for this plugin to work you need to enter your OpenSubtitles.com account info below</b></h3>
<form id="OpenSubtitlesConfigForm">
<div style="display: none;" id="expiredCredentialsWarning" class="padded-left padded-right padded-bottom emby-select-withcolor">
<h2 style="color: #ff2828;"><strong>Warning!</strong></h2>
<p>Your login credentials seem to have changed, please update them in order for the plugin to work.</p>
</div>
<h3 class="sectionTitle sectionTitle-cards"><b>In order for this plugin to work you need to enter your OpenSubtitles.com account info below</b></h3>
<div class="inputContainer">
<input is="emby-input" type="text" id="username" label="${LabelUsername}" />
<div class="fieldDescription">
@ -26,7 +30,7 @@
</div>
<div class="fieldDescription" id="ossresponse"></div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block">
<button id="save-button" is="emby-button" type="submit" class="raised button-submit block">
<span>${Save}</span>
</button>
</div>

View File

@ -3,13 +3,20 @@
};
export default function (view, params) {
let credentialsWarning;
view.addEventListener('viewshow', function () {
Dashboard.showLoadingMsg();
const page = this;
credentialsWarning = page.querySelector("#expiredCredentialsWarning");
ApiClient.getPluginConfiguration(OpenSubtitlesConfig.pluginUniqueId).then(function (config) {
page.querySelector('#username').value = config.Username || '';
page.querySelector('#password').value = config.Password || '';
page.querySelector('#apikey').value = config.CustomApiKey || '';
if (config.CredentialsInvalid) {
credentialsWarning.style.display = null;
}
Dashboard.hideLoadingMsg();
});
});
@ -29,19 +36,23 @@ export default function (view, params) {
}
const el = form.querySelector('#ossresponse');
const saveButton = form.querySelector('#save-button');
const data = JSON.stringify({ Username: username, Password: password, CustomApiKey: apiKey });
const url = ApiClient.getUrl('Jellyfin.Plugin.OpenSubtitles/ValidateLoginInfo');
const handler = response => response.json().then(res => {
saveButton.disabled = false;
if (response.ok) {
el.innerText = `Login info validated, this account can download ${res.Downloads} subtitles per day`;
config.Username = username;
config.Password = password;
config.CustomApiKey = apiKey;
config.CredentialsInvalid = false;
ApiClient.updatePluginConfiguration(OpenSubtitlesConfig.pluginUniqueId, config).then(function (result) {
credentialsWarning.style.display = 'none';
Dashboard.processPluginConfigurationUpdateResult(result);
});
}
@ -56,6 +67,7 @@ export default function (view, params) {
}
});
saveButton.disabled = true;
ApiClient.ajax({ type: 'POST', url, data, contentType: 'application/json'}).then(handler).catch(handler);
});
return false;