diff --git a/src/installer-rp/main.ts b/src/installer-rp/main.ts index 72aaeb9..a2767e0 100644 --- a/src/installer-rp/main.ts +++ b/src/installer-rp/main.ts @@ -4,6 +4,7 @@ import https from 'node:https' import path from 'node:path' import fs from 'node:fs' import fsp from 'node:fs/promises' +import os from 'node:os' import { URL } from 'node:url' import type { ChildProcess } from 'node:child_process' import type { Manifest, PackDefinition, PackList } from '../shared/types.js' @@ -27,8 +28,23 @@ interface RpInstallerState { activeChildren: Set } -/** 동시 yt-dlp 프로세스 수. 너무 높이면 유튜브가 throttle. */ -const MUSIC_CONCURRENCY = 3 +/** + * 동시 yt-dlp 프로세스 수를 CPU 코어 수로 자동 결정. + * - yt-dlp + ffmpeg 변환이 CPU 바운드라 코어 수가 가장 좋은 프록시. + * - 유튜브가 IP 단위로 throttle 걸기 때문에 5 이상은 효과 없음 → 상한 5. + * - 환경변수 MUSIC_CONCURRENCY 로 강제 오버라이드 가능. + */ +function pickMusicConcurrency(): number { + const override = Number(process.env.MUSIC_CONCURRENCY) + if (Number.isFinite(override) && override >= 1) { + return Math.min(8, Math.floor(override)) + } + const cores = os.cpus()?.length ?? 4 + if (cores <= 2) return 2 + if (cores <= 4) return 3 + if (cores <= 8) return 4 + return 5 +} const DEFAULT_MANIFEST_URL = process.env.MANIFEST_URL ?? 'http://127.0.0.1:3000/manifest.json' @@ -199,10 +215,13 @@ ipcMain.handle('rp:install:start', async (): Promise<{ resourcepackPath: string sendProgress({ phase: 'prep', message: '준비 완료', done: true }) throwIfCancelled() - // 2-2. 음악 다운로드 (MUSIC_CONCURRENCY 개씩 병렬, ogg 변환) + // 2-2. 음악 다운로드 (CPU 코어 수 기반 자동 동시 다운로드, ogg 변환) const musicDir = path.join(tempRoot, 'music') await fsp.mkdir(musicDir, { recursive: true }) - sendLog(`음악 다운로드 시작 (${musicTotal}곡, 동시 ${MUSIC_CONCURRENCY}개)`) + const concurrency = pickMusicConcurrency() + const cpuCount = os.cpus()?.length ?? 0 + sendLog(`CPU 코어 ${cpuCount}개 감지 → 동시 다운로드 ${concurrency}개`) + sendLog(`음악 다운로드 시작 (${musicTotal}곡, 동시 ${concurrency}개)`) // 클로저 안에서 narrowing 이 풀리지 않도록 로컬 alias. const musicList = pack.list.music @@ -252,7 +271,7 @@ ipcMain.handle('rp:install:start', async (): Promise<{ resourcepackPath: string } } - const workerCount = Math.min(MUSIC_CONCURRENCY, musicTotal) + const workerCount = Math.min(concurrency, musicTotal) const workers: Promise[] = [] for (let w = 0; w < workerCount; w++) workers.push(musicWorker()) await Promise.all(workers)