From b7ae32220b61c616b29d3aa60b520bb2d05ad7e3 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Mon, 15 Jun 2026 14:27:25 -0700 Subject: [PATCH] video: coverage-card click re-queues failed matches (match music manager) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The video worker manager's coverage cards only switched the view — they never re-queued failed items, so not_found/error items sat in the retry cooldown and the worker reported 'all matched' / idle while failures piled up. Clicking a Movies/Shows coverage card now pins that group AND resets its failed matches (not_found/error -> pending) so the worker sweeps them, mirroring the music worker manager. (Episodes are a sync cascade, not a match queue, so unchanged.) --- .../static/video/video-enrichment-manager.js | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/webui/static/video/video-enrichment-manager.js b/webui/static/video/video-enrichment-manager.js index 495d848c..cb48df24 100644 --- a/webui/static/video/video-enrichment-manager.js +++ b/webui/static/video/video-enrichment-manager.js @@ -281,7 +281,33 @@ function switchKind(kind) { state.kind = kind; state.page = 0; renderCards(); - loadUnmatched().then(function () { renderControls(); renderList(); }); + // Like the music worker manager: clicking a coverage group also "pins" it + // and RE-QUEUES its previously-failed items (not_found/error -> pending) so + // the worker sweeps ALL unmatched, not just never-tried ones — otherwise + // failed items sit forever (in the retry cooldown) and the worker reports + // "all matched". (Episodes are a sync cascade, not a match queue.) + if (kind === 'movie' || kind === 'show') { + setPriority(kind); + requeueFailed(kind); + } else { + loadUnmatched().then(function () { renderControls(); renderList(); }); + } + } + function requeueFailed(kind) { + fetch('/api/video/enrichment/' + state.selected + '/retry', { + method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, + body: JSON.stringify({ kind: kind, scope: 'failed' }), + }).then(function (r) { return r.ok ? r.json() : null; }) + .then(function (d) { + var n = (d && d.reset) || 0; + if (n && typeof showToast === 'function') { + showToast('Re-queued ' + n + ' previously-failed ' + + (KIND_LABEL[kind] || kind).toLowerCase(), 'success'); + } + return Promise.all([loadBreakdown(state.selected), loadUnmatched()]); + }) + .then(function () { renderCards(); renderControls(); renderList(); }) + .catch(function () { loadUnmatched().then(function () { renderControls(); renderList(); }); }); } function togglePause() { var s = state.statuses[state.selected] || {};