/manifest/<key>.json 의 resourcepackPath 가 비어있지 않으면
/file/resourcepacks/<path> 의 zip 을 받아 임시 폴더에 풀고, 그 위에
음악·사진·sounds.json·pack.mcmeta 를 얹어 최종 zip 을 만든다.
- types.ts: RpFetchedPack 에 resourcepackPath 필드 추가
- main.ts: 로드 시 normalizePackDefinition 으로 resourcepackPath 캡처,
설치 시 베이스 zip 다운로드 → tempRoot/base.zip → buildResourcepackZip 에 전달
- pack.ts: baseZipPath 옵션 추가. extract-zip 으로 베이스 압축 해제 후 위에 얹기.
sounds.json 은 기존 항목과 병합, pack.mcmeta 는 mcVersion 에 맞춰 항상 덮어씀.
베이스가 없으면(빈 문자열) 기존처럼 새 리소스팩을 처음부터 생성.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
동시 N개를 모두 t=0 에 띄우면 카드들이 0% 에서 같이 멈춰있다가
한꺼번에 100% 가 되는 "정지된 듯한" 구간이 보였음.
이제 새 다운로드 시작 사이에 최소 2.5초 간격을 두어, 어떤 카드는 70%,
어떤 카드는 30% 식으로 항상 진행 흐름이 이어지게 만든다.
- musicStartChain 으로 acquire 직렬화 → race-free
- nextMusicStartAt 으로 마지막 시작 시점 추적
- 동시성(코어 수 기반) 자체는 그대로 유지, 시작 시점만 분산
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
os.cpus().length 기준 동시 다운로드 수를 자동 결정:
- 2 코어 이하 → 2 동시
- 3~4 코어 → 3 동시
- 5~8 코어 → 4 동시
- 9 코어 이상 → 5 동시 (YouTube throttle 때문에 상한)
환경변수 MUSIC_CONCURRENCY 로 강제 오버라이드 가능(상한 8).
설치 로그에 감지된 코어 수와 선택된 동시성 노출.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- yt-dlp 인자에 --concurrent-fragments 5 추가 (HLS/DASH 청크 병렬 다운로드)
- yt-dlp 인자에 --newline 추가 (진행률 라인 안정화)
- 음악 다운로드 루프를 단일 순차 → worker pool 3개 동시 처리로 전환
- state.currentChild (단일) → state.activeChildren (Set) 으로 확장,
취소 시 실행 중인 모든 자식 프로세스 kill
- UI 는 카드 그리드라 병렬 진행 상태가 그대로 표시됨
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2단계 페이지 진입 즉시 설치를 시작하고, 음악·사진을 1번부터 카드 그리드로
한눈에 볼 수 있게 만든다. 다운로드는 % 게이지로, 완료/실패는 색상으로 표시.
- main: prep/item/package phase 의 ProgressEvent 를 renderer 로 송신
- music.ts: yt-dlp stdout 의 [download] X% 라인을 파싱해 onProgress 호출
- preload: onProgress 채널 구독 함수 노출
- renderer: 다음 버튼 제거, prep chip + music/image 카드 그리드 + 빌드 상태
- styles: progressCard / prepChip / progressGrid 스타일 추가
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous hardcoded pack_format 34 + supported_formats 34..75
covered 1.21 through 1.21.11 only, so a pack generated for the
current latest (26.1.2 → format 84) was rejected as outdated.
Add src/installer-rp/packFormat.ts with a 1.21 → 26.2 lookup table
from the Minecraft wiki and resolveResourcePackFormat() that returns
{matched, format}. Unknown mcVersion falls back to the table's most
recent entry, with a log line warning the user.
Plumb mcVersion through the load → install flow:
- rp:packs:load now also fetches /manifest/<key>.json alongside
/file/list/<key>.json and runs it through the existing
normalizePackDefinition so the editor and the installer agree on
the mcVersion shape. Pack manifest load failures fall back to an
empty mcVersion (which then triggers the latest-format fallback).
- RpFetchedPack carries mcVersion; the install handler hands it to
buildResourcepackZip.
- buildResourcepackZip drops the constant pack_format / supported_
formats and uses the resolved format both as pack_format and as
the {min,max} of supported_formats. Each pack is thus pinned to
exactly the MC version it was authored for.
- The renderer's pack card now shows "마인크래프트 <version>" in
the small line so the user can confirm before installing.
Verified locally: pack.mcmeta generated for mcVersion "1.21",
"1.21.6", "26.1.2", and the bogus "99.9.9" produce pack_format
34 / 63 / 84 / 86 (last falls back to the table tail) respectively.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add archiver dep (v7 — v8 dropped the function-style default export
that the @types still describe) and a new src/installer-rp/pack.ts
that assembles the resource pack tree under tempDir/resourcepack/
and zips it to %appdata%/.minecraft/resourcepacks/<key>_musicquiz.zip.
The tree matches Minecraft 1.21+ painting variant + custom sound
conventions:
pack.mcmeta pack_format 34,
supported 34..75
assets/musicquiz/sounds.json stream:true per track
assets/musicquiz/sounds/track_NN.ogg from tempDir/music
assets/musicquiz/textures/painting/cover_NN.png from tempDir/painting
Music NN.ogg is renamed to track_NN.ogg at copy time so the sound
event ids stay readable. The painting_variant JSON definitions are
intentionally NOT generated here — those live in the data pack and
are owned by /op/datapack on the website.
Wire step 2-4 of the install IPC to call buildResourcepackZip with
the now-populated music/painting temp dirs. Step 2-5 is now just a
log line since buildResourcepackZip writes directly to the final
path.
Verified by a node smoke test: tempDir of two stub ogg files plus
two 256x256 PNGs produces a valid zip with the expected entries
and UTF-8 Korean strings in pack.mcmeta description.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add sharp dep (libvips bindings) — fastest option for the per-image
center-crop + Lanczos resize step. Pure-JS alternatives (jimp) and
spawning ffmpeg per image were both ~5-10x slower in this hot loop.
Add src/installer-rp/images.ts:
- ytIdFromUrl: extracts the video ID from watch?v=, youtu.be/, and
/shorts|embed/ URL forms
- downloadImage: for YouTube URLs tries i.ytimg.com/vi/<id>/
maxresdefault.jpg first, falls back to hqdefault.jpg; plain image
URLs go through a generic HTTP/HTTPS GET that follows 302s
- normalizeToCover: center-crop to min(w,h), Lanczos resize down to
1024x1024 when larger, never upscales, writes PNG
- coverFileName: returns cover_NN.png with zero-padded NN
Wire step 2-3 of the install handler to download + normalize each
image into <tempDir>/painting/cover_NN.png. Zip build (step 2-4)
will pick those up next.
Verified with synthetic 1200x800 and 2000x1500 buffers: small
input stays 800x800 (no upscale), large input becomes 1024x1024.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add src/installer-rp/ffmpeg.ts that downloads BtbN/FFmpeg-Builds
win64-gpl zip into %appdata%/.mc_custom/, extracts ffmpeg.exe out
of bin/, drops it at %appdata%/.mc_custom/ffmpeg.exe and verifies
with `ffmpeg -version`. Reuses existing extract-zip dep.
Add src/installer-rp/music.ts that spawns yt-dlp with
--extract-audio --audio-format vorbis --ffmpeg-location <ffmpeg.exe>
to produce <tempDir>/NN.ogg per track. Streams yt-dlp stdout to
the log channel and reports stderr on non-zero exit.
Wire both into the install IPC handler: step 2-1 now preps both
binaries, step 2-2 iterates the music list and downloads each
track. Track the currently running child process in state so the
cancel button can kill it instead of waiting for it to finish.
Image / zip / place steps remain stubbed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add src/installer-rp/ytdlp.ts that downloads
https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe
into %appdata%/.mc_custom/yt-dlp.exe (fixed path, Windows-only since
the installer is shipped as .exe). If the file is already there and
--version works it is reused; otherwise it is re-downloaded and
verified. The server's existing OS-aware ensureYtDlp stays intact.
The install IPC handler now calls ensureYtDlpExe() in step 2-1 and
logs the resolved path. Music / image / zip / place steps are still
stubbed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a second Electron entry under src/installer-rp/ + installer-rp/
launched by `npm run installer:rp`. It is structurally separate from
the music quiz installer (own tsconfig, own preload, own renderer),
shares the existing styles via a relative link, and exposes a
three-step UI: pick a 음악퀴즈, run the install, then a 완료 page that
can open the resourcepacks folder or quit.
The install IPC handler currently scaffolds the flow per docs/add.md
(yt-dlp prep → music download → image normalize → zip build → place
at %appdata%/.minecraft/resourcepacks/) but the actual work is still
TODO. Cancel/cleanup of %appdata%/.mc_custom/.temp/ is wired up so
that future iterations can plug each step in without rewiring.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>