From e30d7418df4a3388275ee11e17055f882f1d1eea Mon Sep 17 00:00:00 2001 From: claude-bot Date: Mon, 27 Jul 2026 00:11:19 +0900 Subject: [PATCH] =?UTF-8?q?feat(installer):=20=EC=B5=9C=EC=A2=85=20?= =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=ED=8C=A9=20=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=A7=84=ED=96=89=EB=A5=A0(=ED=8D=BC?= =?UTF-8?q?=EC=84=BC=ED=8A=B8)=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "예" 선택 후 최종 리소스팩 다운로드 시 퍼센트/진행바를 보여준다. - finalResourcepack:install 을 downloadFile → downloadStream 으로 바꿔 content-length 기반 진행률을 계산하고 finalResourcepack:progress 이벤트로 렌더러에 전송. - preload onFinalResourcepackProgress 구독 추가, 렌더러에 진행바+% 표시(완료 100%, 실패 시 바 숨김). 0.4.17→0.4.18. Co-Authored-By: Claude Opus 4.7 --- installer/renderer.js | 32 +++++++++++++++++++++++++++++++- package.json | 2 +- src/installer/main.ts | 14 +++++++++++++- src/installer/preload.ts | 9 +++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/installer/renderer.js b/installer/renderer.js index 87ab223..c8dbaa3 100644 --- a/installer/renderer.js +++ b/installer/renderer.js @@ -1133,20 +1133,47 @@ function downloadFinalResourcepack() { section.innerHTML = '

' + tt('resourcepack.downloadHeading') + '

' + '
' + tt('resourcepack.downloading') + '
' + + '
' + + '
' + + '
' + + '
0%
' + '' pageHost.appendChild(section) var msg = section.querySelector('#rpMsg') var actions = section.querySelector('#rpActions') + var bar = section.querySelector('#rpBar') + var barWrap = section.querySelector('#rpBarWrap') + var percentEl = section.querySelector('#rpPercent') - function proceed() { state.finalRpHandled = true; renderStep5() } + // 다운로드 진행률(퍼센트) 구독. 완료/실패 시 해제. + var unsubscribe = null + if (installerApi.onFinalResourcepackProgress) { + unsubscribe = installerApi.onFinalResourcepackProgress(function (info) { + if (info && info.percent >= 0) { + bar.style.width = info.percent + '%' + percentEl.textContent = info.percent + '%' + } else if (info) { + // 전체 크기를 모르면 받은 용량(MB)만 표시. + percentEl.textContent = (info.loaded / 1048576).toFixed(1) + ' MB' + } + }) + } + function stopProgress() { if (unsubscribe) { unsubscribe(); unsubscribe = null } } + + function proceed() { stopProgress(); state.finalRpHandled = true; renderStep5() } installerApi.installFinalResourcepack().then(function (res) { + stopProgress() if (res && res.ok) { + bar.style.width = '100%' + percentEl.textContent = '100%' msg.textContent = tt('resourcepack.downloadDone') msg.classList.add('success') actions.innerHTML = '' actions.querySelector('#rpNext').addEventListener('click', proceed) } else { + barWrap.hidden = true + percentEl.hidden = true msg.textContent = tt('resourcepack.downloadFailed', { message: (res && res.message) || 'unknown' }) msg.classList.add('error') actions.innerHTML = @@ -1157,6 +1184,9 @@ function downloadFinalResourcepack() { } actions.hidden = false }).catch(function (err) { + stopProgress() + barWrap.hidden = true + percentEl.hidden = true msg.textContent = tt('resourcepack.downloadFailed', { message: (err && err.message) ? err.message : String(err) }) msg.classList.add('error') actions.innerHTML = diff --git a/package.json b/package.json index 9572b1f..f66341c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecraft-music-quiz-installer", - "version": "0.4.17", + "version": "0.4.18", "description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트", "main": "dist/installer/main.js", "scripts": { diff --git a/src/installer/main.ts b/src/installer/main.ts index 7e4ec17..f0076cc 100644 --- a/src/installer/main.ts +++ b/src/installer/main.ts @@ -290,8 +290,20 @@ ipcMain.handle('finalResourcepack:install', async (): Promise<{ ok: boolean; pat const url = `${state.baseUrl}/file/resourcepacks/outputs/${encodeURIComponent(cleaned)}` const destDir = path.join(getAppDataDir(), getMcCustomDirName(), 'resourcepacks') const dest = path.join(destDir, cleaned) + await fsp.mkdir(destDir, { recursive: true }) sendLog(t('log.finalResourcepackDownload', { url })) - await downloadFile(url, dest) + // 진행률(퍼센트)을 렌더러로 흘려보내며 스트리밍 다운로드. + let lastPct = -1 + const controller = new AbortController() + await downloadStream(url, dest, controller.signal, (loaded, total) => { + const percent = total > 0 ? Math.min(100, Math.max(0, Math.floor((loaded / total) * 100))) : -1 + if (percent !== lastPct) { + lastPct = percent + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('finalResourcepack:progress', { loaded, total, percent }) + } + } + }) sendLog(t('log.finalResourcepackSaved', { path: dest })) return { ok: true, path: dest } } catch (error) { diff --git a/src/installer/preload.ts b/src/installer/preload.ts index 3de2071..adb0759 100644 --- a/src/installer/preload.ts +++ b/src/installer/preload.ts @@ -74,6 +74,15 @@ const api = { const listener = (_event: unknown, line: string) => handler(line) ipcRenderer.on('log', listener) return () => ipcRenderer.removeListener('log', listener) + }, + + // 최종 리소스팩 다운로드 진행률 구독. 반환값을 호출하면 구독 해제. + onFinalResourcepackProgress: ( + handler: (info: { loaded: number; total: number; percent: number }) => void + ): (() => void) => { + const listener = (_event: unknown, info: { loaded: number; total: number; percent: number }) => handler(info) + ipcRenderer.on('finalResourcepack:progress', listener) + return () => ipcRenderer.removeListener('finalResourcepack:progress', listener) } }