fix(job-poll): disable caching and recover from fetch errors
Review P1: 진행률 폴링이 304 응답을 받으면 r.json() 이 reject 되는데
.catch() 가 없어 setTimeout 도 안 걸리고 폴링이 영구 중단됐습니다.
이게 "60fps 변환 확인 중" 에서 바가 멈춰 보이던 진짜 원인이었어요.
세 곳을 다 고침:
1) src/routes/op.ts `/op/job/:id`
- `Cache-Control: no-store, no-cache, must-revalidate` + `Pragma: no-cache`
- 브라우저가 conditional GET 으로 304 를 받지 않게 한다.
2) public/editor.js fetch
- `{ cache: 'no-store' }` 옵션. 서버 헤더 + 클라 옵션 둘 다.
3) public/editor.js pollJob
- `.catch()` 추가. 일시적 네트워크/파싱 오류여도 2 초 백오프로
폴링을 재개한다. 변환이 오래 걸려도 바가 계속 갱신됨을 보장.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -123,22 +123,31 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
function pollJob(jobId, videoId) {
|
function pollJob(jobId, videoId) {
|
||||||
fetch('/op/job/' + encodeURIComponent(jobId)).then(function (r) { return r.json() }).then(function (j) {
|
// cache: 'no-store' 로 304 가 나지 않게 강제. 304 면 body 가 비어
|
||||||
if (!j.ok) {
|
// r.json() 이 reject → 폴링 중단되는 문제 방지.
|
||||||
probeInfo.textContent = j.message || '작업을 찾을 수 없음'
|
fetch('/op/job/' + encodeURIComponent(jobId), { cache: 'no-store' })
|
||||||
return
|
.then(function (r) { return r.json() })
|
||||||
}
|
.then(function (j) {
|
||||||
var job = j.job
|
if (!j.ok) {
|
||||||
dlProgress.value = job.progress
|
probeInfo.textContent = j.message || '작업을 찾을 수 없음'
|
||||||
probeInfo.textContent = job.message
|
return
|
||||||
if (job.status === 'done') {
|
}
|
||||||
location.href = '/op/folder/' + encodeURIComponent(folder) + '/video/editor?id=' + encodeURIComponent(videoId)
|
var job = j.job
|
||||||
} else if (job.status === 'error') {
|
dlProgress.value = job.progress
|
||||||
probeInfo.textContent = '실패: ' + (job.error || '')
|
probeInfo.textContent = job.message
|
||||||
} else {
|
if (job.status === 'done') {
|
||||||
setTimeout(function () { pollJob(jobId, videoId) }, 1500)
|
location.href = '/op/folder/' + encodeURIComponent(folder) + '/video/editor?id=' + encodeURIComponent(videoId)
|
||||||
}
|
} else if (job.status === 'error') {
|
||||||
})
|
probeInfo.textContent = '실패: ' + (job.error || '')
|
||||||
|
} else {
|
||||||
|
setTimeout(function () { pollJob(jobId, videoId) }, 1500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
// 네트워크 일시 오류로 폴링이 영구 중단되지 않도록 짧게 백오프 후 재시도.
|
||||||
|
console.warn('[pollJob] fetch 실패, 재시도:', err)
|
||||||
|
setTimeout(function () { pollJob(jobId, videoId) }, 2000)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 타임라인 트림 (lossless-cut 류 핸들 UI) ─────────────────────────
|
// ── 타임라인 트림 (lossless-cut 류 핸들 UI) ─────────────────────────
|
||||||
|
|||||||
@@ -291,6 +291,10 @@ opRouter.post('/op/folder/:name/video/youtube/start', requireAuth, async (req, r
|
|||||||
})
|
})
|
||||||
|
|
||||||
opRouter.get('/op/job/:id', requireAuth, (req, res) => {
|
opRouter.get('/op/job/:id', requireAuth, (req, res) => {
|
||||||
|
// 폴링 응답이 304 로 캐싱되면 브라우저가 body 없는 응답을 돌려줘서
|
||||||
|
// 클라이언트의 r.json() 이 reject → 폴링이 중단된다. 항상 신선한 응답.
|
||||||
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate')
|
||||||
|
res.set('Pragma', 'no-cache')
|
||||||
const job = getJob(req.params.id)
|
const job = getJob(req.params.id)
|
||||||
if (!job) {
|
if (!job) {
|
||||||
res.status(404).json({ ok: false, message: '작업을 찾을 수 없습니다.' })
|
res.status(404).json({ ok: false, message: '작업을 찾을 수 없습니다.' })
|
||||||
|
|||||||
Reference in New Issue
Block a user