1
Fork 0
mirror of https://github.com/thegeneralist01/archivr synced 2026-07-22 03:05:32 +02:00

fix(frontend): block playlist submission unless probe is done

Previous guard only blocked while playlistProbeState==='probing'.
Two remaining bypass paths:
- idle: 800ms debounce not yet fired after URL typed
- error: probe failed — no per-video quality data available

Change anyProbing and handleArchive guard to:
  isPlaylistSource(locator) && playlistProbeState !== 'done'

This means idle/probing/error all block submission for playlist items.
error is intentionally blocking — without quality data the per-video
requirement can't be satisfied; user must retry or remove the URL.
This commit is contained in:
TheGeneralist 2026-07-20 22:57:34 +02:00
parent dcdfa78073
commit 1cc5f73ae1
Signed by: thegeneralist01
SSH key fingerprint: SHA256:pp9qddbCNmVNoSjevdvQvM5z0DHN7LTa8qBMbcMq/R4
3 changed files with 16 additions and 10 deletions

View file

@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Archivr</title> <title>Archivr</title>
<script type="module" crossorigin src="/assets/index-fBW6UZq7.js"></script> <script type="module" crossorigin src="/assets/index-DZAStbJ2.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-vCRGeW0A.css"> <link rel="stylesheet" crossorigin href="/assets/index-vCRGeW0A.css">
</head> </head>
<body> <body>

View file

@ -392,7 +392,8 @@ export default function CaptureDialog({ open, archiveId, onClose, onCaptured, on
const toSubmit = items.filter(it => it.locator.trim()) const toSubmit = items.filter(it => it.locator.trim())
if (toSubmit.length === 0) return if (toSubmit.length === 0) return
if (toSubmit.some(it => hasConflict(it))) return if (toSubmit.some(it => hasConflict(it))) return
if (toSubmit.some(it => it.playlistProbeState === 'probing' || it.probeState === 'probing')) return if (toSubmit.some(it => it.probeState === 'probing' ||
(isPlaylistSource(it.locator) && it.playlistProbeState !== 'done'))) return
const batchId = toSubmit.length > 1 const batchId = toSubmit.length > 1
? (crypto.randomUUID?.() ?? `batch-${Date.now()}`) ? (crypto.randomUUID?.() ?? `batch-${Date.now()}`)
: null : null
@ -517,7 +518,12 @@ export default function CaptureDialog({ open, archiveId, onClose, onCaptured, on
const pendingCount = items.filter(it => it.locator.trim()).length const pendingCount = items.filter(it => it.locator.trim()).length
const anyConflict = items.some(it => hasConflict(it)) const anyConflict = items.some(it => hasConflict(it))
const anyProbing = items.some(it => it.playlistProbeState === 'probing' || it.probeState === 'probing') const anyProbing = items.some(it =>
it.probeState === 'probing' ||
// For playlist sources block unless probe completed successfully:
// idle = debounce not yet fired; probing = in flight; error = no quality data.
(isPlaylistSource(it.locator) && it.playlistProbeState !== 'done')
)
return ( return (
<dialog ref={dialogRef} className="capture-dialog"> <dialog ref={dialogRef} className="capture-dialog">