mirror of
https://github.com/Felitendo/jellyfin-plugin-lyrics.git
synced 2026-11-01 17:23:25 +01:00
Adds a DisableRateLimit option that skips the request gap entirely. It only takes effect when the base URL points at a self-hosted instance; requests to lrclib.net and any URL the provider would fall back from stay rate limited. The limiter now tracks its watermark with Stopwatch instead of DateTime.UtcNow, so a wall-clock jump backwards can no longer stall every request for the length of the jump.
242 lines
14 KiB
HTML
242 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en-US">
|
|
<head>
|
|
<title>Lyrics</title>
|
|
</head>
|
|
<body>
|
|
<div id="lyricsConfigurationPage" data-role="page" class="page type-interior pluginConfigurationPage">
|
|
<div data-role="content">
|
|
<div class="content-primary">
|
|
<form class="gotifyConfigurationForm">
|
|
<div class="verticalSection verticalSection-extrabottompadding">
|
|
<div class="sectionTitleContainer flex align-items-center">
|
|
<h2 class="sectionTitle">Lyrics Settings:</h2>
|
|
</div>
|
|
<label class="checkboxContainer">
|
|
<input is="emby-checkbox" type="checkbox" id="useStrictSearch" />
|
|
<span>Use strict search.</span>
|
|
</label>
|
|
<label class="checkboxContainer" id="excludeArtistNameLabel">
|
|
<input is="emby-checkbox" type="checkbox" id="excludeArtistName" />
|
|
<span>Exclude artist name from the search parameters.</span>
|
|
</label>
|
|
<label class="checkboxContainer" id="excludeAlbumNameLabel">
|
|
<input is="emby-checkbox" type="checkbox" id="excludeAlbumName" />
|
|
<span>Exclude album name from the search parameters.</span>
|
|
</label>
|
|
<label class="checkboxContainer">
|
|
<input is="emby-checkbox" type="checkbox" id="enableDurationFilter" />
|
|
<span>Filter matches by song length (recommended)</span>
|
|
</label>
|
|
<div id="durationFilterSettings" style="margin-left: 2.2em;">
|
|
<p style="margin: 0 0 1em 0; font-size: 0.95em; opacity: 0.8;">
|
|
Skip lyrics whose song length is too different from your file. Turn this off to accept any match regardless of length.
|
|
</p>
|
|
<label>
|
|
Duration tolerance (seconds)
|
|
<input is="emby-input" type="number" id="durationToleranceSeconds" min="1" step="1" />
|
|
</label>
|
|
<p style="margin: 0 0 1em 0; font-size: 0.95em; opacity: 0.8;">
|
|
How close the song length must be to a lyrics match for the match to count. If wrong lyrics show up on instrumental or interlude tracks, lower this value. If lyrics are missing for songs that should have them, raise it. Default: 15.
|
|
</p>
|
|
</div>
|
|
<label class="checkboxContainer">
|
|
<input is="emby-checkbox" type="checkbox" id="enableAdaptiveRetryBackoff" />
|
|
<span>Skip repeated misses (recommended)</span>
|
|
</label>
|
|
<div id="adaptiveRetrySettings" style="margin-left: 2.2em;">
|
|
<p style="margin: 0 0 1em 0; font-size: 0.95em; opacity: 0.8;">
|
|
If a song has no lyrics today, the plugin waits longer before trying again.
|
|
</p>
|
|
<label>
|
|
Retry after days (comma separated)
|
|
<input is="emby-input" type="text" id="backoffScheduleDays" placeholder="1,3,7,30 (example)" />
|
|
</label>
|
|
<label>
|
|
Forget old failed-song history after (days)
|
|
<input is="emby-input" type="number" id="failureStateTtlDays" min="1" step="1" />
|
|
</label>
|
|
</div>
|
|
<label class="checkboxContainer">
|
|
<input is="emby-checkbox" type="checkbox" id="enableRunCap" />
|
|
<span>Limit work per run (recommended for large libraries)</span>
|
|
</label>
|
|
<div id="runCapSettings" style="margin-left: 2.2em;">
|
|
<p style="margin: 0 0 1em 0; font-size: 0.95em; opacity: 0.8;">
|
|
Stops after this many songs per run, then continues next scheduled run.
|
|
</p>
|
|
<label>
|
|
Max songs to check each run
|
|
<input is="emby-input" type="number" id="maxTracksPerRun" min="100" step="1" />
|
|
</label>
|
|
</div>
|
|
<div class="inputContainer">
|
|
<label class="inputLabel inputLabelUnfocused" for="lrclibBaseUrl">LRCLIB server URL (optional, for advanced users)</label>
|
|
<input is="emby-input" type="text" id="lrclibBaseUrl" placeholder="https://lrclib.net" />
|
|
<p style="margin: 0.4em 0 0 0; font-size: 0.95em; opacity: 0.8;">
|
|
Only change this if you run your own LRCLIB instance and want the plugin to query it instead.
|
|
</p>
|
|
</div>
|
|
<label class="checkboxContainer">
|
|
<input is="emby-checkbox" type="checkbox" id="disableRateLimit" />
|
|
<span>Disable rate limiting</span>
|
|
</label>
|
|
<div class="fieldDescription checkboxFieldDescription" style="margin-left: 2.2em;">
|
|
<p style="margin: 0 0 1em 0; font-size: 0.95em; opacity: 0.8;">
|
|
Removes the delay between requests, which speeds up large libraries considerably. This
|
|
only takes effect with your own LRCLIB server configured above — requests to the
|
|
public lrclib.net stay rate limited either way.
|
|
</p>
|
|
</div>
|
|
<br />
|
|
<div>
|
|
<button is="emby-button" type="submit" data-theme="b" class="raised button-submit block">
|
|
<span>Save</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<script type="text/javascript">
|
|
var LyricsPluginConfiguration = {
|
|
uniquePluginId: "f4a1b2c3-d4e5-6f7a-8b9c-0d1e2f3a4b5c",
|
|
|
|
loadConfiguration: function () {
|
|
Dashboard.showLoadingMsg();
|
|
|
|
ApiClient.getPluginConfiguration(LyricsPluginConfiguration.uniquePluginId).then(function (config) {
|
|
document.querySelector('#lrclibBaseUrl').value = config.LrclibBaseUrl || '';
|
|
document.querySelector('#disableRateLimit').checked = config.DisableRateLimit === true;
|
|
document.querySelector('#useStrictSearch').checked = config.UseStrictSearch;
|
|
|
|
if (config.UseStrictSearch) {
|
|
document.querySelector('#excludeArtistNameLabel').classList.add('hide');
|
|
document.querySelector('#excludeAlbumNameLabel').classList.add('hide');
|
|
}
|
|
|
|
document.querySelector('#excludeArtistName').checked = config.ExcludeArtistName;
|
|
document.querySelector('#excludeAlbumName').checked = config.ExcludeAlbumName;
|
|
document.querySelector('#enableDurationFilter').checked = config.EnableDurationFilter !== false;
|
|
document.querySelector('#durationToleranceSeconds').value = Math.max(parseInt(config.DurationToleranceSeconds, 10) || 15, 1);
|
|
|
|
document.querySelector('#enableAdaptiveRetryBackoff').checked = config.EnableAdaptiveRetryBackoff !== false;
|
|
document.querySelector('#enableRunCap').checked = config.EnableRunCap !== false;
|
|
document.querySelector('#maxTracksPerRun').value = Math.max(config.MaxTracksPerRun || 2000, 100);
|
|
document.querySelector('#failureStateTtlDays').value = Math.max(config.FailureStateTtlDays || 90, 1);
|
|
|
|
var backoffDays = Array.isArray(config.BackoffScheduleDays) && config.BackoffScheduleDays.length > 0
|
|
? config.BackoffScheduleDays
|
|
: [1, 3, 7, 30];
|
|
document.querySelector('#backoffScheduleDays').value = backoffDays.join(',');
|
|
|
|
LyricsPluginConfiguration.toggleStrictSearchVisibility();
|
|
LyricsPluginConfiguration.toggleBackoffVisibility();
|
|
LyricsPluginConfiguration.toggleRunCapVisibility();
|
|
LyricsPluginConfiguration.toggleDurationFilterVisibility();
|
|
Dashboard.hideLoadingMsg();
|
|
});
|
|
},
|
|
|
|
saveConfiguration: function () {
|
|
Dashboard.showLoadingMsg();
|
|
ApiClient.getPluginConfiguration(LyricsPluginConfiguration.uniquePluginId).then(function (config) {
|
|
|
|
config.LrclibBaseUrl = (document.querySelector('#lrclibBaseUrl').value || '').trim();
|
|
config.DisableRateLimit = document.querySelector('#disableRateLimit').checked;
|
|
config.UseStrictSearch = document.querySelector('#useStrictSearch').checked;
|
|
config.ExcludeArtistName = document.querySelector('#excludeArtistName').checked;
|
|
config.ExcludeAlbumName = document.querySelector('#excludeAlbumName').checked;
|
|
|
|
config.EnableDurationFilter = document.querySelector('#enableDurationFilter').checked;
|
|
var durationToleranceValue = parseInt(document.querySelector('#durationToleranceSeconds').value || '15', 10);
|
|
config.DurationToleranceSeconds = Number.isFinite(durationToleranceValue) ? Math.max(durationToleranceValue, 1) : 15;
|
|
config.EnableAdaptiveRetryBackoff = document.querySelector('#enableAdaptiveRetryBackoff').checked;
|
|
config.EnableRunCap = document.querySelector('#enableRunCap').checked;
|
|
|
|
var maxTracksPerRunValue = parseInt(document.querySelector('#maxTracksPerRun').value || '2000', 10);
|
|
config.MaxTracksPerRun = Number.isFinite(maxTracksPerRunValue) ? Math.max(maxTracksPerRunValue, 100) : 2000;
|
|
|
|
var failureStateTtlDaysValue = parseInt(document.querySelector('#failureStateTtlDays').value || '90', 10);
|
|
config.FailureStateTtlDays = Number.isFinite(failureStateTtlDaysValue) ? Math.max(failureStateTtlDaysValue, 1) : 90;
|
|
|
|
var parsedBackoffDays = (document.querySelector('#backoffScheduleDays').value || '')
|
|
.split(',')
|
|
.map(function (part) { return parseInt(part.trim(), 10); })
|
|
.filter(function (value) { return Number.isFinite(value) && value > 0; });
|
|
config.BackoffScheduleDays = parsedBackoffDays.length > 0 ? parsedBackoffDays : [1, 3, 7, 30];
|
|
|
|
ApiClient.updatePluginConfiguration(LyricsPluginConfiguration.uniquePluginId, config).then(function (result) {
|
|
Dashboard.processPluginConfigurationUpdateResult(result);
|
|
});
|
|
});
|
|
},
|
|
|
|
toggleStrictSearchVisibility: function () {
|
|
if (document.getElementById('useStrictSearch').checked) {
|
|
document.getElementById('excludeArtistNameLabel').classList.add('hide');
|
|
document.getElementById('excludeAlbumNameLabel').classList.add('hide');
|
|
}
|
|
else {
|
|
document.getElementById('excludeArtistNameLabel').classList.remove('hide');
|
|
document.getElementById('excludeAlbumNameLabel').classList.remove('hide');
|
|
}
|
|
},
|
|
|
|
toggleBackoffVisibility: function () {
|
|
if (document.getElementById('enableAdaptiveRetryBackoff').checked) {
|
|
document.getElementById('adaptiveRetrySettings').classList.remove('hide');
|
|
}
|
|
else {
|
|
document.getElementById('adaptiveRetrySettings').classList.add('hide');
|
|
}
|
|
},
|
|
|
|
toggleRunCapVisibility: function () {
|
|
if (document.getElementById('enableRunCap').checked) {
|
|
document.getElementById('runCapSettings').classList.remove('hide');
|
|
}
|
|
else {
|
|
document.getElementById('runCapSettings').classList.add('hide');
|
|
}
|
|
},
|
|
|
|
toggleDurationFilterVisibility: function () {
|
|
if (document.getElementById('enableDurationFilter').checked) {
|
|
document.getElementById('durationFilterSettings').classList.remove('hide');
|
|
}
|
|
else {
|
|
document.getElementById('durationFilterSettings').classList.add('hide');
|
|
}
|
|
}
|
|
};
|
|
|
|
document.getElementById('lyricsConfigurationPage').addEventListener('pageshow', function () {
|
|
LyricsPluginConfiguration.loadConfiguration();
|
|
});
|
|
|
|
document.getElementById('lyricsConfigurationPage').addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
LyricsPluginConfiguration.saveConfiguration();
|
|
});
|
|
|
|
document.getElementById('useStrictSearch').addEventListener('change', function () {
|
|
LyricsPluginConfiguration.toggleStrictSearchVisibility();
|
|
});
|
|
|
|
document.getElementById('enableAdaptiveRetryBackoff').addEventListener('change', function () {
|
|
LyricsPluginConfiguration.toggleBackoffVisibility();
|
|
});
|
|
|
|
document.getElementById('enableRunCap').addEventListener('change', function () {
|
|
LyricsPluginConfiguration.toggleRunCapVisibility();
|
|
});
|
|
|
|
document.getElementById('enableDurationFilter').addEventListener('change', function () {
|
|
LyricsPluginConfiguration.toggleDurationFilterVisibility();
|
|
});
|
|
</script>
|
|
</div>
|
|
</body>
|
|
</html>
|