Quarantine: flip the modal row to Completed after Accept & Import
Accepting a quarantined item re-imported the file correctly, but the download modal kept showing 'Quarantined'. The re-import ran through the inner pipeline, which doesn't mark task completion (that's the verification wrapper's job), and the sidecar context had no task_id anyway (popped before quarantine). The chooser's Accept now sends the originating task_id, and the endpoint re-runs the import through the verification wrapper with that task_id (+ batch_id looked up from the task), so the task is marked completed only after the file is verified moved — the row flips to Completed on the next poll. Manager-tab approvals (no task_id, no JSON body — handled via get_json(silent=True)) keep the original inner-pipeline path. Also clear has-candidates + the quarantine dataset on every status render so a row that goes quarantined -> completed doesn't keep a stale chooser attached.
This commit is contained in:
parent
3060678f29
commit
bad3eb1fab
2 changed files with 41 additions and 10 deletions
|
|
@ -7117,14 +7117,33 @@ def approve_quarantine_item(entry_id):
|
|||
# for this one restored pass so multi-reason failures do not loop.
|
||||
context['_skip_quarantine_check'] = 'all'
|
||||
context['_approved_quarantine_trigger'] = trigger
|
||||
# Re-dispatch through the same pipeline. Run async so the HTTP
|
||||
# request returns quickly — UI polls /list to see the entry vanish.
|
||||
# If the caller (download-modal chooser) passed the originating task, run
|
||||
# the re-import through the verification WRAPPER with that task_id so the
|
||||
# task is marked completed on success — otherwise the modal row stays
|
||||
# stuck on "Quarantined" even though the file imported. The sidecar
|
||||
# context lost task_id/batch_id (the wrapper pops them before quarantine),
|
||||
# so we re-supply them here. Manager-tab approvals (no task_id) keep the
|
||||
# original inner-pipeline path.
|
||||
_req = request.get_json(silent=True) or {}
|
||||
_task_id = (_req.get('task_id') or '').strip() or None
|
||||
_batch_id = None
|
||||
if _task_id:
|
||||
with tasks_lock:
|
||||
_t = download_tasks.get(_task_id)
|
||||
if isinstance(_t, dict):
|
||||
_batch_id = _t.get('batch_id')
|
||||
context['task_id'] = _task_id
|
||||
if _batch_id:
|
||||
context['batch_id'] = _batch_id
|
||||
context_key = f"approve_{entry_id}_{int(time.time())}"
|
||||
threading.Thread(
|
||||
target=lambda: _post_process_matched_download(context_key, context, restored_path),
|
||||
daemon=True,
|
||||
).start()
|
||||
logger.info(f"[Quarantine] Approved {entry_id} (original_trigger={trigger}, bypass=all) → re-running pipeline")
|
||||
if _task_id:
|
||||
_reprocess = lambda: _post_process_matched_download_with_verification(
|
||||
context_key, context, restored_path, _task_id, _batch_id,
|
||||
)
|
||||
else:
|
||||
_reprocess = lambda: _post_process_matched_download(context_key, context, restored_path)
|
||||
threading.Thread(target=_reprocess, daemon=True).start()
|
||||
logger.info(f"[Quarantine] Approved {entry_id} (original_trigger={trigger}, bypass=all, task={_task_id}) → re-running pipeline")
|
||||
return jsonify({"success": True, "trigger_bypassed": "all", "original_trigger": trigger})
|
||||
except Exception as e:
|
||||
logger.error(f"[Quarantine] Error approving {entry_id}: {e}")
|
||||
|
|
|
|||
|
|
@ -3449,7 +3449,7 @@ function showQuarantineChooser({ entryId, taskId, reason, trackName }) {
|
|||
requestAnimationFrame(() => overlay.classList.add('visible'));
|
||||
|
||||
const acceptBtn = overlay.querySelector('#qc-accept-btn');
|
||||
if (acceptBtn) acceptBtn.addEventListener('click', () => acceptQuarantineFromChooser(acceptBtn, entryId));
|
||||
if (acceptBtn) acceptBtn.addEventListener('click', () => acceptQuarantineFromChooser(acceptBtn, entryId, taskId));
|
||||
const searchBtn = overlay.querySelector('#qc-search-btn');
|
||||
if (searchBtn) searchBtn.addEventListener('click', () => {
|
||||
closeQuarantineChooser();
|
||||
|
|
@ -3457,7 +3457,7 @@ function showQuarantineChooser({ entryId, taskId, reason, trackName }) {
|
|||
});
|
||||
}
|
||||
|
||||
async function acceptQuarantineFromChooser(button, entryId) {
|
||||
async function acceptQuarantineFromChooser(button, entryId, taskId) {
|
||||
if (!entryId) { showToast('Cannot accept — missing quarantine id.', 'error'); return; }
|
||||
const confirmed = await showConfirmDialog({
|
||||
title: 'Accept Quarantined File',
|
||||
|
|
@ -3473,7 +3473,11 @@ async function acceptQuarantineFromChooser(button, entryId) {
|
|||
// Release the preview stream first so the file isn't locked during the move.
|
||||
await _qcReleaseAudio();
|
||||
try {
|
||||
const resp = await fetch(`/api/quarantine/${encodeURIComponent(entryId)}/approve`, { method: 'POST' });
|
||||
const resp = await fetch(`/api/quarantine/${encodeURIComponent(entryId)}/approve`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ task_id: taskId || '' }),
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
showToast('Accepted. Re-running post-processing.', 'success');
|
||||
|
|
@ -3770,6 +3774,14 @@ function processModalStatusUpdate(playlistId, data) {
|
|||
statusEl.classList.remove('has-error-tooltip');
|
||||
statusEl.removeAttribute('title');
|
||||
statusEl.removeAttribute('data-error-msg');
|
||||
// Clear clickable/quarantine state each render; the failure
|
||||
// branch below re-adds it when still applicable. Without this a
|
||||
// task that flips failed/quarantined -> completed (e.g. after
|
||||
// Accept & Import) keeps a stale chooser on the cell.
|
||||
statusEl.classList.remove('has-candidates');
|
||||
delete statusEl.dataset.quarantineEntryId;
|
||||
delete statusEl.dataset.quarantineReason;
|
||||
delete statusEl.dataset.quarantineTrack;
|
||||
statusEl.textContent = statusText;
|
||||
|
||||
if ((task.status === 'failed' || task.status === 'cancelled' || task.status === 'not_found') && task.error_message) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue