From 9abf6da5f1a8cfca812b7e62506b0a77a604bda6 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Wed, 22 Jul 2026 00:45:33 +0900 Subject: [PATCH] =?UTF-8?q?fix(rp):=20=EB=85=B8=EB=9E=98=20403=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=E2=80=94=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4=20?= =?UTF-8?q?=ED=81=B4=EB=9D=BC=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EB=8B=A4?= =?UTF-8?q?=EB=B3=80=ED=99=94=20+=20=EA=B3=A1=EB=B3=84=20=EC=A7=80?= =?UTF-8?q?=EC=97=B0=20=EC=9E=AC=EC=8B=9C=EB=8F=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.4.12 의 yt-dlp 내부 재시도(--retries)로도 남는 403(특정 클라이언트/스트림 URL 차단, 예: android_vr)에 대응: - yt-dlp 에 --extractor-args youtube:player_client=default,web_safari,tv,ios 추가. 한 클라이언트의 URL 이 403 이어도 다른 클라이언트 포맷으로 받는다(클라이언트명 유효성은 실제 yt-dlp 로 검증). - 실패한 곡의 순차 재시도를 1회 → 최대 3회로, 시도마다 점점 더(최대 20s) 쉬어 유튜브 rate-limit 이 풀릴 시간을 준다. 0.4.13→0.4.14. Co-Authored-By: Claude Opus 4.7 --- locales/installer-rp/ko-kr.json | 1 + package.json | 2 +- src/installer-rp/main.ts | 20 +++++++++++++++++--- src/installer-rp/music.ts | 3 +++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/locales/installer-rp/ko-kr.json b/locales/installer-rp/ko-kr.json index a6a9caf..b5601d2 100644 --- a/locales/installer-rp/ko-kr.json +++ b/locales/installer-rp/ko-kr.json @@ -94,6 +94,7 @@ "musicTrackDone": "{{idx}}번 노래 완료: {{name}}", "musicTrackSkip": "{{idx}}번 노래는 이전에 받아둠 → 건너뜀(이어받기)", "musicRefreshRetry": "{{count}}곡 다운로드 실패 → yt-dlp/ffmpeg 최신 버전으로 재설치 후 실패한 곡만 재시도", + "musicTrackRetryWait": "{{idx}}번 노래 재시도 {{attempt}}회차 — 유튜브 속도제한(403) 회피로 {{secs}}초 대기 후 다시 시도", "ytdlpReinstall": "yt-dlp.exe 최신 버전으로 강제 재설치 중…", "ffmpegReinstall": "ffmpeg.exe 최신 버전으로 강제 재설치 중…", "imageStart": "사진 다운로드 시작 ({{total}}장)", diff --git a/package.json b/package.json index 7e30ae4..2ebc062 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecraft-music-quiz-installer", - "version": "0.4.13", + "version": "0.4.14", "description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트", "main": "dist/installer/main.js", "scripts": { diff --git a/src/installer-rp/main.ts b/src/installer-rp/main.ts index 12a53ad..08b0880 100644 --- a/src/installer-rp/main.ts +++ b/src/installer-rp/main.ts @@ -493,11 +493,25 @@ ipcMain.handle('rp:install:start', async (): Promise<{ resourcepackPath: string await refreshBinariesOnce() throwIfCancelled() nextMusicStartAt = Date.now() + // 403(Forbidden)은 대개 유튜브의 일시적 rate-limit 이라, 곡마다 점점 더 오래 + // 쉬면서 여러 번 다시 시도한다(마지막 시도에서만 에러 진행률 표시). + const MAX_TRACK_RETRIES = 3 for (const i of failed) { throwIfCancelled() - await acquireMusicStartSlot() - throwIfCancelled() - const ok = await tryDownloadTrack(i, true) + let ok = false + for (let attempt = 1; attempt <= MAX_TRACK_RETRIES; attempt++) { + throwIfCancelled() + if (attempt > 1) { + const waitMs = Math.min(20000, 3000 * attempt) + sendLog(t('log.musicTrackRetryWait', { idx: i + 1, attempt, secs: Math.round(waitMs / 1000) })) + await sleep(waitMs) + throwIfCancelled() + } + await acquireMusicStartSlot() + throwIfCancelled() + ok = await tryDownloadTrack(i, attempt === MAX_TRACK_RETRIES) + if (ok) break + } if (!ok) { throwIfCancelled() const idx = i + 1 diff --git a/src/installer-rp/music.ts b/src/installer-rp/music.ts index e52c8a6..42f9018 100644 --- a/src/installer-rp/music.ts +++ b/src/installer-rp/music.ts @@ -46,6 +46,9 @@ export function downloadMusicTrack(opts: DownloadMusicOptions): Promise '--extractor-retries', '3', '--retry-sleep', 'http:exp=1:30', '--socket-timeout', '30', + // 특정 플레이어 클라이언트(예: android_vr)의 스트림 URL 이 403 되는 경우가 있어 + // 여러 클라이언트를 함께 시도한다. 하나가 막혀도 다른 클라이언트 포맷으로 받는다. + '--extractor-args', 'youtube:player_client=default,web_safari,tv,ios', // 진행률 표시 안정화 (yt-dlp 가 \r 대신 새 줄로 출력). '--newline', '--extract-audio',