docs: README/docs 재정리 — 프로젝트 개요·사용방법 정리

- README.md 를 개발 명세에서 프로젝트 개요/구성/빠른 시작 형식으로 재작성
- docs/installer.md (음악퀴즈 간편설치기 단계별 사용 가이드) 신설
- docs/admin-site.md (관리 사이트 운영자 가이드) 신설
- docs/resourcepack-installer.md (리소스팩 간편설치기 동작 명세) 신설
- 중복된 docs/add.md 제거 (resourcepack-installer.md 로 이전)
This commit is contained in:
2026-05-13 02:44:39 +09:00
parent 475bf924a0
commit 894a86a117
5 changed files with 440 additions and 458 deletions

461
README.md
View File

@@ -1,356 +1,189 @@
# 마인크래프트 음악퀴즈 간편설치기 + 관리 사이트 개발 명세서 # 마인크래프트 음악퀴즈 통합 패키지
## 프로젝트 개요 마인크래프트 음악퀴즈를 한 번에 배포·관리할 수 있도록 만든 통합 프로젝트입니다.
마인크래프트 음악퀴즈를 `.exe` 하나로 간편하게 설치할 수 있는 설치기와, 음악퀴즈 정보를 관리하는 웹사이트를 개발한다. - **관리 사이트** — 음악퀴즈 정보(JSON)와 음악·사진 목록, 데이터팩 출력을 한 곳에서 운영.
- **음악퀴즈 간편설치기 (`.exe`)** — `manifest.json` 기반으로 사용자가 마인크래프트 본체·서버·모드를 자동 설치.
- **리소스팩 간편설치기 (`.exe`)** — 음악퀴즈 음악·표지를 yt-dlp 로 받아 painting variant 텍스처 리소스팩으로 패키징.
### 핵심 컨셉 ---
- 이 프로젝트는 `%appdata%\.mc_custom` 폴더를 생성하여 모드 적용 및 서버 실행을 독립적으로 관리한다.
- 음악퀴즈 정보는 **`manifest.json`** 으로 중앙 관리한다.
### .mc_custom 폴더 구조 ## 무엇이 들어 있나
| 디렉터리 | 역할 | 진입점 |
| --- | --- | --- |
| `src/server/` | 음악퀴즈 관리 웹사이트 (Express + EJS) | `bun start` 또는 `npm start` |
| `src/installer/` | 음악퀴즈 간편설치기 (Electron) | `npm run installer` |
| `src/installer-rp/` | 리소스팩 간편설치기 (Electron) | `npm run installer:rp` |
| `src/shared/` | 두 설치기와 서버가 공유하는 타입·스토어 | — |
| `views/` | EJS 템플릿 (관리 사이트) | — |
| `manifest/` | 음악퀴즈별 정의 JSON | — |
| `file/list/` | 음악퀴즈별 음악·사진 목록 JSON | — |
| `file/` | 정적 파일(서버 zip / 맵 zip / 모드 jar 등) 호스팅 | — |
---
## 핵심 컨셉
설치기는 사용자의 `%APPDATA%\.minecraft` 를 더럽히지 않기 위해 **`.mc_custom`** 을 별도 게임 디렉터리로 사용합니다.
``` ```
%appdata%\.mc_custom\ %APPDATA%\
├── mods/ ← 모드 (.jar) 저장 및 자동 적용 ├─ .minecraft\ ← 원래 마인크래프트 폴더(공용 자원: assets, libraries, versions, runtime)
├── resourcepacks/ ← 리소스팩 (.zip) 저장 및 자동 적용 └─ .mc_custom\ ← 음악퀴즈 전용 게임 폴더(설치기가 자동 생성)
├── saves/ ← 월드 저장 ├─ mods\ ← 음악퀴즈가 지정한 모드(.jar)
├── config/ ← 모드 설정 파일 ├─ resourcepacks\ ← 리소스팩(.zip)
├─ screenshots/ ← 스크린샷 ├─ saves\ ← 단일 맵 .zip 압축 해제 결과
└── options.txt ← 게임 설정 ├─ assets\ (junction → .minecraft\assets)
├─ libraries\ (junction → .minecraft\libraries)
├─ versions\ (junction → .minecraft\versions)
├─ options.txt 등 ← `.minecraft` 의 최상위 설정 파일을 복사해 사용
└─ launcher_profiles ← 실제 파일은 `.minecraft\launcher_profiles.json` 을 수정해 gameDir=.mc_custom 으로 지정
``` ```
- 마인크래프트 런처 프로필의 `gameDir``%appdata%\.mc_custom`으로 설정하면, 마인크래프트가 이 폴더를 기준으로 모든 파일을 읽고 저장한다. 이렇게 분리해 두면 사용자가 평소 쓰던 마인크래프트와 음악퀴즈 설정이 섞이지 않고, 음악퀴즈만 삭제해도 본체에는 영향이 없습니다.
- 버전 파일과 assets는 기존 `%appdata%\.minecraft`를 그대로 사용한다.
--- ---
## 파트 1. 간편설치기 (`.exe`) ## 빠른 시작
> 설치기는 아래 단계를 순서대로 페이지 단위로 진행한다. 각 번호 = 1페이지. 전제: Node.js 18+, npm. 윈도우 빌드를 만들 때만 추가로 Electron 의 PE 서명·아이콘 도구가 필요합니다.
### 1단계: 음악퀴즈 선택 ```bash
# 의존성 설치
npm install
- 음악퀴즈사이트(아래 파트 2 참고)에서 `manifest.json`을 가져와 등록된 음악퀴즈 목록을 표시한다. # 1) 관리 사이트 개발 실행 (http://localhost:3000)
- 사용자가 설치할 음악퀴즈를 선택한다. npm start
--- # 2) 음악퀴즈 간편설치기를 Electron 으로 실행해 보기
npm run installer
### 2단계: 싱글 / 멀티 선택 # 3) 리소스팩 간편설치기를 Electron 으로 실행해 보기
npm run installer:rp
- 싱글 또는 멀티 중 하나를 선택하는 화면을 표시한다. # 4) 음악퀴즈 간편설치기 윈도우 .exe 빌드
- **멀티 선택 시**: 3단계(서버 설치)를 거친 후 4단계로 진행한다. npm run dist:win
- **싱글 선택 시**: 3단계를 건너뛰고 4단계로 바로 진행한다.
---
### 3단계: 서버 관련 설정
- 2단계에서 **멀티**를 선택한 경우에만 진입한다. 싱글 선택 시 이 단계 전체를 건너뛴다.
- 3단계의 각 소항목(3-1 ~ 3-5)은 완료되어도 자동으로 다음으로 넘어가지 않으며, 사용자가 **확인 버튼**을 눌러야 다음 소항목으로 진행된다.
#### 3-1. 서버 설치 경로 설정
- 서버를 생성할 폴더 경로를 사용자가 직접 지정한다.
- **경로에 한글이 포함되면 안 된다.** 한글 포함 시 경고 메시지를 표시하고 다음 단계로 진행 불가.
#### 3-2. JDK 확인 / 설치
- 폴더 선택 UI로 JDK 경로를 지정할 수 있다.
- JDK 자동 탐색 우선순위:
1. 시스템 환경변수 (`JAVA_HOME` 등)
2. 로컬 환경변수
3. `C:\Program Files\Java` (JDK 기본 설치 경로)
- 위 경로에서 JDK가 발견되면 해당 경로를 기본값으로 자동 설정한다.
- JDK가 없으면 설치를 안내한다.
#### 3-3. 서버 다운로드 및 설치
- 처음 파일 다운로드는 자동으로 시작
##### 3-3-1. 파일 다운로드
- JSON의 `packPath` 필드 값을 `도메인/file/` 뒤에 붙여 서버 파일 다운로드 URL을 구성한다.
- 예: `packPath``music-quiz/files`이면 → `도메인/file/music-quiz/files`
- 해당 경로의 모든 파일을 순서대로 다운로드한다.
##### 3-3-2. 설치 로그
- 다른 프로그램 설치 화면처럼 실시간 로그를 표시하는 로그 뷰어를 제공한다.
##### 3-3-3. EULA 동의
- 설치 중간에 Minecraft EULA 동의 화면을 표시하고, 사용자가 직접 동의해야 다음 단계로 진행된다.
- 음악퀴즈 내에 `eula.txt`가 포함되어 있으면 **삭제하고** 새로 동의를 받는다.
##### 3-3-4. 램 검사 로직
> 아래 기준은 모두 JSON의 서버 램 필드(`serverMinRam`, `serverMaxRam`)를 사용한다.
```
유저 시스템 램 >= serverMaxRam → serverMaxRam으로 설정
유저 시스템 램 >= serverMinRam → serverMinRam으로 설정 (경고 메시지 표시)
유저 시스템 램 < serverMinRam → "플레이 불가" 메시지 출력 후 설치 중단
``` ```
- 서버 실행 시 `-Xmx``serverMaxRam`, `-Xms``serverMinRam` 값을 JVM 인자로 사용한다. 리소스팩 설치기는 `yt-dlp` 가 필요합니다. 자동 다운로드되지만, 막혀 있는 환경이라면 [`docs/yt-dlp-setup.md`](docs/yt-dlp-setup.md) 참고.
#### 3-4. 서버 설정
- **로컬 웹서버**를 띄워 브라우저에서 서버 설정 파일을 GUI로 편집할 수 있게 한다.
- 메모장으로 수정해야 했던 파일들을 설명과 함께 편리하게 수정 가능:
- `bukkit.yml`
- `server.properties`
- 기타 설정 파일
- 수정 후 "적용" 버튼으로 실제 파일에 반영한다.
#### 3-5. 서버 포트포워딩 설정
1. **이미 포트포워딩 되어 있는 경우** (UPnP 포함): 외부 접속 주소를 화면에 표시하고 다음 단계로 진행.
2. **포트포워딩 안 된 경우 → UPnP 시도**:
- UPnP로 포트 개방 가능 여부 확인
- 가능하면 자동으로 개방 후 외부 접속 테스트
- 접속 확인되면 다음 단계로 진행 가능
3. **UPnP 불가 시**: "직접 포트포워딩을 해주세요." 메시지를 안내와 함께 표시.
--- ---
### 4단계: 유저 클라이언트 설정 ## 자주 쓰는 문서
- 음악퀴즈 JSON 파일에 명시된 클라이언트 설정을 기반으로 설치한다.
#### 4-1. 모드 플랫폼 설치 | 문서 | 내용 |
- JSON 파일의 `platform.type`에 명시된 플랫폼 이름을 화면에 표시하고, **설치** / **건너뛰기** 버튼으로 사용자가 직접 선택한다. | --- | --- |
- `vanilla`가 아닌 경우: `platform.downloadUrl`을 기반으로 다운로드 후 설치한다. | [`docs/installer.md`](docs/installer.md) | 음악퀴즈 간편설치기 사용자 흐름(단계별 화면·동작). |
- **건너뛰기** 선택 시: 플랫폼 설치 없이 바닐라로 진행한다. | [`docs/admin-site.md`](docs/admin-site.md) | 관리 사이트 운영자 가이드(음악퀴즈 추가·편집, 음악 목록, 데이터팩 출력). |
| [`docs/resourcepack-installer.md`](docs/resourcepack-installer.md) | 리소스팩 간편설치기 동작 명세(yt-dlp 흐름, 이미지 정규화 규칙). |
#### 4-2. 설치설정 설정 | [`docs/painting-variant.md`](docs/painting-variant.md) | 1.21+ painting variant 슬롯/리소스팩 텍스처 규격. |
- `%appdata%\.minecraft\launcher_profiles.json`에 프로필을 추가한다. | [`docs/yt-dlp-setup.md`](docs/yt-dlp-setup.md) | yt-dlp 자동/수동 설치, 트러블슈팅. |
- 이미 동일한 이름의 프로필이 있으면 새로 만들지 않고 기존 프로필을 수정한다.
- **`gameDir``%appdata%\.mc_custom`으로 설정한다.**
- 이 설정으로 인해 모드, 리소스팩, 세이브 등 모든 파일이 `.mc_custom` 기준으로 읽히고 저장된다.
- 램 설정(`javaArgs`)을 JSON의 서버 램 필드 기준으로 적용한다.
- `-Xmx``serverMaxRam`, `-Xms``serverMinRam` 값을 사용한다.
- 플랫폼(Forge 등) 설치 버전을 `lastVersionId`로 설정한다.
```json
// launcher_profiles.json 프로필 예시
{
"음악퀴즈": {
"name": "음악퀴즈",
"type": "custom",
"gameDir": "%appdata%\\.mc_custom",
"lastVersionId": "1.20.1-forge-47.2.0",
"javaArgs": "-Xmx4G -Xms2G"
}
}
```
#### 4-3. 모드 및 리소스팩 설치
- JSON 파일에 명시된 모드와 리소스팩을 다운로드하여 `.mc_custom` 하위 폴더에 저장한다.
- 다운로드 진행 상황을 실시간 로그 뷰어로 표시한다. (다른 프로그램 설치 화면과 동일한 형태)
| 파일 종류 | 저장 경로 |
|-----------|-----------|
| 모드 (`.jar`) | `%appdata%\.mc_custom\mods\` |
| 리소스팩 (`.zip`) | `%appdata%\.mc_custom\resourcepacks\` |
- 이미 동일한 파일명이 존재하면 덮어쓴다.
- 다운로드 완료 후 마인크래프트 실행 시 자동으로 적용된다. (별도 설정 불필요)
--- ---
### 5단계: 완료 페이지 ## 데이터 포맷 (요약)
- 멀티로 진행해서 서버도 설치했다면: 자세한 필드 설명은 `docs/admin-site.md` 의 "음악퀴즈 JSON" 절을 참고하세요.
- **서버 폴더 열기** 버튼
- **바탕화면에 서버 실행 바로가기 만들기** 토글 (기본값: ON)
- **서버 바로 실행** 토글 (기본값: ON)
- 마인크래프트 런처 실행 토글 (기본값: ON)
--- ### `manifest.json` — 사이트 루트, 음악퀴즈 목록
## 파트 2. 음악퀴즈 관리 웹사이트
> **기술 스택: Node.js + TypeScript + Express + EJS**
### 라우팅 구조
| 경로 | 설명 |
|------|------|
| `/` | 메인 페이지 (음악퀴즈 목록) |
| `/manifest.json` | manifest.json 파일 직접 접근 |
| `/file/` | 음악퀴즈 파일 제공 경로 |
| `/op` | 관리자 로그인 페이지 |
| `/op/dashboard` | 관리자 대시보드 |
| `/op/dashboard/:packName` | 음악퀴즈 JSON 편집 페이지 |
---
### 메인 페이지 (`/`)
- `manifest.json`에 등록된 음악퀴즈를 **가로 한 줄 목록(카드) 형식**으로 표시한다.
---
### 관리자 인증 (`/op`)
- `/op` 하위 모든 경로는 로그인 없이 접근 불가 (미들웨어로 처리).
- 로그인 화면: 아이디 + 비밀번호 입력.
- 계정 정보는 **`account.json`** 파일에 저장.
- `account.json`**서버 내부에서만 접근 가능**, 외부 HTTP 요청으로 절대 노출되지 않아야 함.
- 로그인 성공 시 `/op/dashboard`로 리다이렉트.
#### account.json 예시 구조
```json
[
{
"id": "admin",
"password": "admin"
}
]
```
---
### 관리자 대시보드 (`/op/dashboard`)
#### 공통 레이아웃 (메뉴바)
- **왼쪽**: 로고 + "관리자 페이지" 텍스트 → 클릭 시 `/op/dashboard`로 이동
- **오른쪽**: 로그인한 아이디 표시 → 클릭 시 드롭다운 메뉴 표시
- 드롭다운 항목: **로그아웃** 버튼
#### 음악퀴즈 목록
- `/manifest` 폴더 안의 JSON 파일들을 가져와 **가로 한 줄 카드 형식**으로 표시.
- 카드 클릭 → `/op/dashboard/:packName` 으로 이동하여 JSON 편집 시작.
#### 음악퀴즈 추가 버튼
- `/manifest/` 폴더 안에 새 JSON 파일 생성.
- 기본 이름: `new.json`
- 이미 존재하면: `new2.json`, `new3.json` ... 순으로 증가.
- 생성과 동시에 `manifest.json`에도 자동 등록.
#### 음악퀴즈 삭제 버튼
- 버튼 클릭 시:
- 목록 카드에 **체크박스** 표시
- 버튼 아래 **취소** / **확인** 버튼 표시
- 확인 클릭 시 체크된 JSON 파일 삭제 + `manifest.json`에서도 자동 제거.
---
### 음악퀴즈 편집 페이지 (`/op/dashboard/:packName`)
- 해당 JSON 파일의 내용을 GUI 폼으로 편집한다.
- **JSON 파일 이름 변경** 기능 제공.
- 이름 변경 후 적용 클릭 시 현재 브라우저 URL의 `:packName` 부분도 자동 변경 (리다이렉트).
---
## 파트 3. 음악퀴즈 JSON 구조 및 편집 항목
> `/manifest/*.json` 파일의 구조. 관리자 편집 페이지에서 아래 항목들을 GUI로 수정 가능.
### JSON 필드 정의
| 필드명 | 타입 | 설명 |
|--------|------|------|
| `name` | `string` | 음악퀴즈 이름 |
| `mcVersion` | `string` | 마인크래프트 버전 (스냅샷 제외한 정식 릴리즈만) |
| `platform` | `object` | 모드 플랫폼 정보 (아래 참고) |
| `platform.type` | `string` | 플랫폼 종류 (`vanilla` / `forge` / `fabric` / `neoforge` 등) |
| `platform.downloadUrl` | `string` | 플랫폼 설치파일 다운로드 URL (바닐라면 생략) |
| `mods` | `array` | 설치할 모드 목록 (아래 참고) |
| `mods[].name` | `string` | 모드 이름 |
| `mods[].downloadUrl` | `string` | 모드 다운로드 URL |
| `resourcepacks` | `array` | 설치할 리소스팩 목록 |
| `resourcepacks[].name` | `string` | 리소스팩 이름 |
| `resourcepacks[].downloadUrl` | `string` | 리소스팩 다운로드 URL |
| `serverMinRam` | `number` | 서버 최소 램 (MB 단위) |
| `serverMaxRam` | `number` | 서버 최대 램 (MB 단위) |
| `clientMinRam` | `number` | 유저(클라이언트) 최소 램 (MB 단위) |
| `clientRecommendedRam` | `number` | 유저(클라이언트) 권장 램 (MB 단위) |
| `packPath` | `string` | 서버 파일 경로 (`/file/` 이후의 경로, 멀티 전용) |
### JSON 예시
```json
{
"name": "음악퀴즈 v1",
"mcVersion": "1.20.1",
"platform": {
"type": "forge",
"downloadUrl": "https://example.com/forge-installer.jar"
},
"mods": [
{
"name": "ExampleMod",
"downloadUrl": "https://example.com/examplemod.jar"
}
],
"resourcepacks": [
{
"name": "ExampleResourcePack",
"downloadUrl": "https://example.com/resourcepack.zip"
}
],
"serverMinRam": 2048,
"serverMaxRam": 8192,
"clientMinRam": 4096,
"clientRecommendedRam": 8192,
"packPath": "music-quiz/files"
}
```
### 편집 UI 항목별 비고
| 항목 | UI 형태 | 비고 |
|------|---------|------|
| `mcVersion` | 드롭다운 | Mojang API에서 정식 릴리즈만 가져와 표시, 스냅샷 제외 |
| `platform.type` | 드롭다운 | `vanilla` 선택 시 `downloadUrl` 입력란 숨김 |
| `mods` | 동적 목록 | 항목 추가 / 삭제 가능 |
| `resourcepacks` | 동적 목록 | 항목 추가 / 삭제 가능 |
| 램 관련 필드 | 숫자 입력 | MB 단위, `clientMinRam``clientRecommendedRam` 유효성 검사 |
| `packPath` | 텍스트 입력 | `/file/` 이후 경로만 입력 |
---
## 파트 4. manifest.json 구조
> 사이트 루트의 `manifest.json`. 설치기와 메인 페이지가 이 파일을 읽는다.
```json ```json
{ {
"packs": [ "packs": [
{ { "name": "음악퀴즈 v1", "file": "mq-v1" }
"name": "음악퀴즈 이름",
"file": "new"
}
] ]
} }
``` ```
- `file`: `/manifest/` 폴더 안의 JSON 파일 이름 (확장자 제외). - `file` `/manifest/<file>.json` 의 파일명(확장자 제외).
- 음악퀴즈 추가/삭제 자동으로 이 파일도 업데이트된다. - 관리 사이트에서 음악퀴즈 추가/삭제하면 자동으로 갱신됩니다.
### `/manifest/<key>.json` — 음악퀴즈 정의
```json
{
"name": "음악퀴즈 v1",
"mcVersion": "1.21.4",
"platform": {
"type": "fabric",
"loaderVersion": "0.16.10"
},
"modsFolder": "mq-v1",
"resourcepackPath": "mq-v1.zip",
"mapPath": "mq-v1-map.zip",
"serverPath": "mq-v1-server.zip",
"serverMinRam": 4096,
"serverMaxRam": 8192,
"clientMinRam": 4096,
"clientRecommendedRam": 8192
}
```
- `platform.type` = `vanilla` / `forge` / `fabric` / `neoforge`.
- `fabric``loaderVersion` 만 지정하면 설치기가 최신 fabric-installer 로 자동 CLI 설치합니다.
- 나머지(forge/neoforge) 는 `platform.downloadUrl` 에 설치 jar URL.
- `modsFolder``/file/mods/<폴더>/` 의 모든 `.jar` 를 자동으로 받습니다.
- `serverPath` / `mapPath` / `resourcepackPath``/file/servers/`, `/file/maps/`, `/file/resourcepacks/` 아래 zip 파일명.
### `/file/list/<key>.json` — 음악·사진 목록 (리소스팩 설치기용)
```json
{
"musicPlaylistUrl": "https://www.youtube.com/playlist?list=...",
"imagePlaylistUrl": "https://www.youtube.com/playlist?list=...",
"music": [
{ "url": "https://www.youtube.com/watch?v=...", "title": "...", "artist": "...", "durationSec": 213 }
],
"images": [
{ "url": "https://www.youtube.com/watch?v=..." },
{ "url": "https://example.com/cover.png" }
]
}
```
--- ---
## 디렉리 구조 (웹사이트) ## 디렉리 구조 (전체)
``` ```
project-root/ minecraft_launcher/
├── manifest.json # 음악퀴즈 목록 (외부 접근 가능) ├─ src/
├── account.json # 관리자 계정 정보 (외부 접근 절대 불가) │ ├─ server/ Express + EJS 관리 사이트
├── /manifest/ # 음악퀴즈 JSON 파일 저장 폴더 │ ├─ installer/ 음악퀴즈 간편설치기 (Electron 메인 + preload)
├── new.json ├─ installer-rp/ 리소스팩 간편설치기 (Electron 메인 + 음악/이미지 파이프라인)
└── music-quiz.json └─ shared/ 공용 타입, 매니페스트 스토어, mojang/upnp 유틸
├── /file/ # 서버 파일 제공 경로 (멀티 전용) ├─ installer/ 음악퀴즈 설치기 렌더러(HTML/CSS/JS)
├── /src/ # TypeScript 소스 ├─ installer-rp/ 리소스팩 설치기 렌더러(HTML/CSS/JS)
│ ├── app.ts ├─ views/ 관리 사이트 EJS 템플릿
│ ├── routes/ ├─ public/ 관리 사이트 정적 파일(styles.css 등)
├── index.ts # 메인 페이지 ├─ manifest/ 음악퀴즈 JSON 정의 (운영자가 편집)
│ │ └── op.ts # 관리자 라우트 ├─ file/
└── middleware/ ├─ servers/ 서버 zip
└── auth.ts # /op 인증 미들웨어 ├─ maps/ 맵 zip
└── /views/ # EJS 템플릿 │ ├─ mods/<폴더>/ 모드 jar 묶음 (index.json 자동 생성)
├── index.ejs ├─ resourcepacks/ 리소스팩 zip
├── op/ ├─ platforms/ Forge / NeoForge 설치 jar
│ ├── login.ejs │ └─ list/<key>.json 음악·사진 목록
├── dashboard.ejs ├─ docs/ 사용·운영 문서
└── editor.ejs ├─ manifest.json 사이트 루트 매니페스트 (자동 관리)
└── partials/ ├─ account.json 관리자 계정 (절대 외부 노출 금지)
└── navbar.ejs ├─ package.json
└─ tsconfig.{,server,installer,installer-rp}.json
``` ```
---
## 빌드 산출물 / 배포
| 산출물 | 빌드 명령 | 비고 |
| --- | --- | --- |
| 관리 사이트 (Node 실행) | `npm start` | systemd 등으로 띄우기. 외부 도메인이 manifest 의 base URL 이 됩니다. |
| 음악퀴즈 간편설치기 `.exe` | `npm run dist:win` | `electron-builder.yml` 설정 사용. |
| 리소스팩 간편설치기 `.exe` | `tsconfig.installer-rp.json` 빌드 후 `electron-builder` 수동 패키징 | |
---
## 라이선스
내부 프로젝트. 외부 공개 시 별도 명시.

View File

@@ -1,144 +0,0 @@
# 추가사항: 음악퀴즈 리소스팩 간편설치기(exe) & 웹사이트
기본 골격은 현행 음악퀴즈 간편설치기와 동일하므로 그쪽 코드 재활용.
## 작동 방식
`manifest.json` → 음악퀴즈 선택 → `file/list/<음악퀴즈>.json` 에서 음악·사진 목록을 가져온 뒤,
yt-dlp로 음악 다운로드, 별도 경로로 사진 다운로드 → 리소스팩으로 패키징.
---
## 1. 리소스팩 간편설치기 (exe)
번호가 붙은 단계는 "이전 / 다음"으로 이동 가능하게 한다.
### 1) manifest 선택
- `도메인/manifest.json` 에서 음악퀴즈 선택.
- `file/list/<음악퀴즈>.json` 에서 다음을 로드:
- 음악 목록 (유튜브 영상 주소)
- 사진 목록 (유튜브 주소 또는 일반 이미지 주소)
### 2) 리소스팩 설치
- "다음"을 누르면 자동 시작.
- 설치 로그 표시 (곡 제목이 아니라 `n번 노래 다운로드 중…` 형식).
- 취소 버튼 / 창 닫기 / 강제 종료 시: 진행 중 다운로드를 안전하게 중단하고 임시 파일을 정리한 뒤 정상 종료.
#### 2-1. yt-dlp 준비
- 설치 경로: `%appdata%/.mc_custom/`
- 이미 있으면 업데이트 확인 후 사용.
- 시스템 PATH 에 이미 yt-dlp 가 등록돼 있으면 그것을 사용하고 업데이트 확인.
#### 2-2. 음악 다운로드 (1번부터 순차)
- 임시 경로: `%appdata%/.mc_custom/.temp/`
- 각 곡을 `ogg` 로 변환 (Minecraft 사운드 호환 포맷).
- 중단되거나 전부 끝나면 `.temp` 내용 삭제.
#### 2-3. 사진 다운로드 (1번부터 순차) → painting variant 텍스처
이미지는 두 형태로 들어올 수 있다.
- **유튜브 주소**: yt-dlp 가 알려준 영상 ID 로
`https://i.ytimg.com/vi/<id>/maxresdefault.jpg` 를 1차 시도, 실패하면 `hqdefault.jpg` 로 폴백.
- **일반 이미지 주소**: HTTP GET 으로 그대로 다운로드.
다운로드 후 painting variant 슬롯 규격에 맞춰 정규화한다 (자세한 슬롯 구조는
`docs/painting-variant.md` 참고).
- **슬롯 규격(고정, 데이터팩 측)**: `4 × 4` 블록 정사각, `cover_01 … cover_N`.
- **최종 PNG 규격(리소스팩 측)**: 정사각 1:1, 최대 `1024 × 1024` px.
- `4 × 4` 블록 × 블록당 `256` px (×16 배율) → 1024×1024 가 픽셀 그리드와 정확히 일치.
- **정규화 알고리즘**:
1. 가운데 정사각 크롭: `s = min(원본 가로, 원본 세로)``s × s`.
2. `s > 1024` 이면 `1024 × 1024` 로 축소 (Lanczos 권장).
3. `s ≤ 1024` 이면 그대로 `s × s` 유지 (업스케일 없음).
- 파일명: `cover_<NN>.png` (`NN` 은 2자리 0패딩).
- 저장 경로: `resourcepack/assets/musicquiz/textures/painting/`.
- 패키지 완성된 리소스팩을 `%appdata%/.minecraft/resourcepacks/` 에 zip 으로 배치.
### 3) 설치 완료
- "확인" 누르면 프로그램 종료.
---
## 2. 웹사이트 추가사항
### /op/dashboard 상단
- `[음악목록 수정]` 버튼 → `/op/list`
- `[데이터팩 수정]` 버튼 → `/op/datapack`
### /op/list
- 상단 왼쪽에 `[돌아가기]` 버튼 → `/op/dashboard`.
- `manifest.json` 에 등록된 음악퀴즈를 카드 한 줄(가로) 목록으로 표시. 표시 내용은 `/op/dashboard` 와 동일.
- 카드 클릭 → `/op/list/:음악퀴즈`.
### /op/list/:음악퀴즈
상단에 **음악목록 / 사진목록** 두 탭. 기본 탭은 "음악목록".
#### 음악목록 탭
- 상단 버튼: `[목록 저장]`, `[목록 초기화]` (초기화 시 빈 목록).
- 그 아래: 플레이리스트 주소 입력칸 + `[플레이리스트 불러오기]`.
- 클릭 시 "기존 순서가 전부 사라집니다" 경고 팝업(확인/취소). 확인 시 플레이리스트의 영상들을 아래 목록에 채움.
- 목록 디자인: 유튜브 플레이리스트 스타일 (좌측 번호 배지 + 썸네일 + 제목·가수·길이).
- 항목 드래그로 순서(번호) 변경.
- 우클릭 메뉴: 수정, 삭제.
- "수정" 선택 시 팝업으로 새 유튜브 주소 입력.
#### 사진목록 탭
- 상단 버튼: `[목록 저장]`, `[목록 초기화]`.
- 그 아래: 플레이리스트 주소 입력칸 + `[플레이리스트 불러오기]` (확인 팝업 동일).
- 불러오기 확정 시 플레이리스트 영상들의 **썸네일만** 추출해 목록에 채움.
- 목록 디자인: 이미지가 크게 보이도록 **반응형 그리드(카드)** 형식.
- 카드 좌상단에 번호 배지, 본문은 정사각형 또는 4:3 썸네일.
- 카드 드래그로 위치 변경.
- 우클릭 메뉴: 수정, 삭제.
- "수정" 팝업 상단에 `[유튜브 주소] / [이미지 주소]` 토글 버튼, 선택한 종류의 입력칸 하나만 표시.
### 저장 포맷: `/file/list/<음악퀴즈>.json`
```json
{
"musicPlaylistUrl": "https://www.youtube.com/playlist?list=...",
"imagePlaylistUrl": "https://www.youtube.com/playlist?list=...",
"music": [
{
"url": "https://www.youtube.com/watch?v=xxxxxxxxxxx",
"title": "곡 제목",
"artist": "가수",
"durationSec": 213
}
],
"images": [
{ "url": "https://www.youtube.com/watch?v=xxxxxxxxxxx" },
{ "url": "https://example.com/cover.png" }
]
}
```
### /op/datapack
- 상단 왼쪽에 `[돌아가기]``/op/dashboard`.
- 상단에 `[음악퀴즈 선택]` 버튼. 클릭 시 팝업에 `manifest.json` 의 음악퀴즈를 카드 목록으로 표시, 하나 선택.
- 선택 후: "총 N개의 음악을 찾았습니다." 텍스트 표시.
- 아래 `[데이터팩 출력]` 버튼.
- 클릭 시 아래 코드 영역에 mcfunction 출력 + 복사 버튼 제공.
- **출력 포맷(임시, 실제 포맷은 추후 확정 예정)**:
```mcfunction
# === musicquiz: <pack name> ===
# 총 N곡
say [musicquiz] 데이터팩 초기화
# 곡별 placeholder. 실제 포맷 확정되면 교체 예정.
# 1. <title> - <artist> (<duration>s)
# 2. ...
```

119
docs/admin-site.md Normal file
View File

@@ -0,0 +1,119 @@
# 관리 사이트 운영 가이드
음악퀴즈 정의(`/manifest/*.json`), 음악·사진 목록(`/file/list/*.json`), 데이터팩 출력을 한 곳에서 관리하는 Express + EJS 사이트입니다.
## 실행
```bash
npm install
npm start # 기본 포트 3000. 환경변수로 PORT 조정 가능.
```
배포 시에는 시스템 서비스(systemd 등) 로 등록해 두면 됩니다.
## 도메인 / 경로 구성
| 경로 | 내용 |
| --- | --- |
| `/` | 음악퀴즈 카드 한 줄 목록(공개). 카드 클릭 시 해당 음악퀴즈 페이지. |
| `/manifest.json` | 사이트 루트 매니페스트(공개). 설치기가 첫 화면에서 로드. |
| `/file/...` | 정적 파일 호스팅. `servers/`, `maps/`, `mods/<폴더>/`, `resourcepacks/`, `platforms/`, `list/`. |
| `/op` | 관리자 로그인. 미들웨어로 `/op/*` 전체를 보호. |
| `/op/dashboard` | 음악퀴즈 카드 목록 + 추가/삭제 + 편집 진입. |
| `/op/dashboard/:packKey` | 음악퀴즈 정의(JSON) 편집기. |
| `/op/list` | 카드 목록(음악·사진 편집 진입). |
| `/op/list/:packKey` | 음악퀴즈의 음악·사진 목록 편집(드래그/우클릭). |
| `/op/datapack` | 데이터팩 mcfunction 출력. |
| `/op/datapack/:packKey/generate` | 텍스트 한 덩어리로 mcfunction 반환. |
## 계정
`account.json` 에 정의합니다(루트 디렉터리). **외부 HTTP 로 절대 노출되지 않도록 라우팅에서 제외돼 있습니다.**
```json
[
{ "id": "admin", "password": "비번" }
]
```
> 운영 환경에서는 평문 비밀번호 대신 해시를 쓰도록 추후 보강할 여지가 있습니다.
## 대시보드 (`/op/dashboard`)
- 상단 메뉴: 좌측 로고 = 대시보드로 이동, 우측 아이디 드롭다운에 **로그아웃**.
- 상단 버튼: `[음악목록 수정]``/op/list`, `[데이터팩 수정]``/op/datapack`.
- 카드 목록: `manifest.json` 의 음악퀴즈를 가로 한 줄 카드로 표시.
- **음악퀴즈 추가** — `/manifest/new.json` 생성. 이미 있으면 `new2.json`, `new3.json` … 으로 증가. 동시에 `manifest.json` 갱신.
- **음악퀴즈 삭제** — 카드에 체크박스가 뜨고, 확인 시 선택한 JSON 과 매니페스트 항목을 삭제.
## 음악퀴즈 정의 편집 (`/op/dashboard/:packKey`)
폼 필드:
| 필드 | 설명 |
| --- | --- |
| 음악퀴즈 이름 | 사용자에게 보이는 표시명. |
| JSON 파일 이름 | URL 키. 영문/숫자/`_`/`-` 만 허용. 변경 시 파일명 + 매니페스트가 동시에 갱신되며 URL 도 새 키로 리다이렉트. |
| 마인크래프트 버전 | Mojang 공식 API 에서 정식 릴리즈만 드롭다운으로 표시. |
| 모드 플랫폼 | `vanilla` / `forge` / `fabric` / `neoforge`. |
| 플랫폼 설치파일 URL | `forge` / `neoforge` 용. 도메인 없이 입력하면 `/file/platforms/<파일명>` 으로 해석. |
| Fabric Loader 버전 | `fabric` 선택 시 자동 표시. Fabric Meta API 에서 선택한 mcVersion 의 로더 목록을 가져옴. 설치기는 최신 fabric-installer 로 CLI 자동 설치. |
| 서버 최소/최대 램 (MB) | 설치기 RAM 검사 및 `run.bat``-Xms/-Xmx`. |
| 클라이언트 최소/권장 램 (MB) | 사용자 PC 요구치 + 마인크래프트 런처 JVM 인수에 사용. |
| 맵 파일 (.zip) | `/file/maps/` 아래 zip 파일명. `.mc_custom/saves/` 로 압축 해제. |
| 서버 파일 (.zip) | `/file/servers/` 아래 zip. 멀티 전용. |
| 모드 폴더 이름 | `/file/mods/<폴더>/` 아래 모든 `.jar` 자동 다운로드. 빈 값이면 받지 않음. |
| 리소스팩 (.zip) | `/file/resourcepacks/` 아래 zip 파일명. |
저장 시 `clientMinRam ≤ clientRecommendedRam` 검증이 들어갑니다.
## 음악·사진 목록 편집 (`/op/list/:packKey`)
상단 탭: **음악목록** / **사진목록**.
### 음악목록 탭
- `[목록 저장]`, `[목록 초기화]`.
- 플레이리스트 주소 + `[플레이리스트 불러오기]`. 확인 팝업에서 동의해야 기존 순서를 덮어씀.
- 항목 표시: 좌측 번호 배지, 썸네일, 제목 / 가수 / 길이.
- 드래그로 순서 변경. 우클릭: **수정** / **삭제**.
- **수정** → 새 유튜브 주소를 입력하면 yt-dlp 가 메타데이터를 가져와 자동 채움.
### 사진목록 탭
- `[목록 저장]`, `[목록 초기화]`, 플레이리스트 불러오기는 동일.
- 카드 그리드(반응형) 로 표시. 우클릭에서 수정/삭제. 수정 팝업은 [유튜브 주소] / [이미지 주소] 토글.
저장 포맷은 `/file/list/<packKey>.json` (README 데이터 포맷 참고).
> yt-dlp 메타데이터/플레이리스트 조회를 위해 서버에 yt-dlp 가 설치되어 있어야 합니다. ([`yt-dlp-setup.md`](yt-dlp-setup.md))
## 데이터팩 (`/op/datapack`)
- `[음악퀴즈 선택]` → 팝업에서 음악퀴즈 선택.
- "총 N개의 음악을 찾았습니다." 와 `[데이터팩 출력]` 버튼.
- 출력 클릭 시 코드 영역에 mcfunction(임시 포맷) + 복사 버튼. 실제 포맷은 추후 확정.
```
# === musicquiz: <pack name> ===
# 총 N곡 / 사진 M장
say [musicquiz] 데이터팩 초기화
# 곡별 placeholder. 실제 포맷 확정되면 교체 예정.
# 1. <title> - <artist> (<duration>s)
```
## 파일 업로드 / 정리 운영
| 카테고리 | 위치 | 비고 |
| --- | --- | --- |
| 서버 zip | `file/servers/<이름>.zip` | EULA / `run.bat` 포함 권장. 설치기가 `run.bat` 에 UPnP/JVM 후처리 자동 주입. |
| 맵 zip | `file/maps/<이름>.zip` | 압축 해제 시 `.mc_custom/saves/` 아래에 풀림. |
| 모드 jar | `file/mods/<폴더>/...jar` | 해당 폴더의 `index.json` 이 자동 생성됩니다. 신규 jar 를 넣은 뒤에는 사이트가 인덱스를 다시 만들도록 한 번 호출. |
| 리소스팩 zip | `file/resourcepacks/<이름>.zip` | — |
| 플랫폼 설치 jar | `file/platforms/<이름>.jar` | forge/neoforge 용. fabric 은 사이트에 둘 필요 없음(자동 다운로드). |
## 보안 주의
- `account.json` 은 라우팅에서 차단되어 있으나, 디스크 권한도 운영자만 접근 가능하게 두는 것이 안전합니다.
- 관리자 비밀번호는 충분히 강하게 설정.
- 모든 `/op/*` 라우트는 세션 기반 인증 미들웨어를 거칩니다. 세션 만료 시 자동으로 로그인 페이지로 리다이렉트.

105
docs/installer.md Normal file
View File

@@ -0,0 +1,105 @@
# 음악퀴즈 간편설치기 사용 가이드
`%APPDATA%\.mc_custom\` 을 게임 디렉터리로 쓰는 별도 인스턴스를 자동으로 구축합니다. 평소 쓰던 `.minecraft` 는 그대로 유지되며, 음악퀴즈 모드/리소스팩/맵/서버만 분리된 폴더에 자동 설치됩니다.
설치기는 단계별 화면으로 진행됩니다. 단계 사이에는 "이전 / 다음" 버튼이 있고, 일부 단계는 페이지 진입 시 자동으로 작업을 시작합니다.
---
## 1단계 — 음악퀴즈 선택
`manifest.json` 을 읽어 등록된 음악퀴즈를 카드 한 줄로 표시합니다. 하나를 선택하면 다음 단계로 진행됩니다.
> manifest URL 은 기본값이 박혀 있지만, 다른 호스트의 음악퀴즈를 받고 싶다면 첫 화면 상단의 URL 입력란에 붙여 넣고 새로고침 버튼을 누르면 됩니다.
## 2단계 — 싱글 / 멀티 선택
| 선택 | 흐름 |
| --- | --- |
| **싱글** | 3단계(서버 설치)를 건너뛰고 곧장 4단계로 진행. |
| **멀티** | 3단계의 5개 소항목을 거친 뒤 4단계로 진행. |
## 3단계 — 서버 관련 설정 (멀티 전용)
### 3-1. 서버 설치 경로
- 폴더 선택 또는 직접 입력.
- **경로에 한글이 있으면 안 됩니다.** (마인크래프트 서버가 비정상 동작)
### 3-2. JDK 확인
- 환경변수(`JAVA_HOME`, `JDK_HOME`) → 자동 설치 위치(`%APPDATA%\jdk\temurin-21`) → `C:\Program Files\Java` 순으로 자동 탐색.
- **자동 설치** 버튼을 누르면 Adoptium Temurin 21 LTS Windows x64 zip 을 받아 `%APPDATA%\jdk\temurin-21\` 에 풀어 사용합니다.
- 설치 중 같은 버튼이 "설치 취소" 로 바뀌고, 누르면 다운로드를 즉시 중단하고 부분 파일을 정리합니다.
### 3-3. 서버 다운로드 및 설치
페이지 진입 즉시 음악퀴즈의 `serverPath` 에 지정된 서버 zip 을 다운로드해 압축을 풉니다. 압축 해제 직후 `run.bat` 에 다음 처리를 자동 주입합니다:
- **UPnP 자동 등록/해제**: 서버 시작 직전 `server-port` 를 읽어 PowerShell COM (`HNetCfg.NATUPnP.1`) 으로 TCP 매핑 추가, 종료 후 해제.
- 설치 끝나면 EULA 동의 화면이 표시되고, 동의해야 다음으로 넘어갑니다. 이어서 시스템 RAM 검사가 자동 실행됩니다.
램 검사 규칙:
```
시스템 RAM ≥ serverMaxRam → 서버 RAM = serverMaxRam
시스템 RAM ≥ serverMinRam → 서버 RAM = serverMinRam (경고 표시)
시스템 RAM < serverMinRam → "플레이 불가" 후 설치 중단
```
### 3-4. 서버 설정 (편집기)
내장 로컬 웹서버를 띄워 브라우저에서 `server.properties`, `bukkit.yml`, `paper-global.yml` 등 주요 설정 파일을 GUI 로 편집합니다. 저장 누르면 실제 파일에 반영됩니다.
### 3-5. 서버 포트포워딩 점검
페이지 진입 시 자동으로 검사합니다. 흐름:
1. 이전 실행에서 만든 UPnP 매핑이 남아 있으면 먼저 제거.
2. 외부 포트체크 서비스(`ifconfig.co`) 로 1차 점검. 임시 TCP 리스너를 띄워 외부에서 닿는지 확인.
3. 이미 사용자가 라우터 규칙으로 포워딩 해 두었으면 → "포워딩 됨" 으로 통과.
4. 아니면 UPnP 로 자동 개방 시도 후 재점검. 성공 시 테스트용 매핑은 즉시 제거(실제 개방은 `run.bat` 이 서버 기동 때마다 처리).
5. UPnP 도 실패하면 안내 메시지 (사용자가 라우터에서 수동 포워딩) 표시.
> 동일 페이지에 **재점검** 버튼이 있어, 라우터 설정을 바꾼 뒤 다시 누르면 1차부터 다시 검사합니다.
## 4단계 — 유저 클라이언트 설정 (자동 진행)
페이지 진입 즉시 시작합니다.
1. `.mc_custom` 폴더 생성 + `mods/`, `resourcepacks/` 생성.
2. `.minecraft` 최상위 설정 파일(`options.txt`, `optionsof.txt`, `servers.dat`, `usercache.json`, …) 을 `.mc_custom` 으로 복사. 이미 같은 이름이 있으면 보존.
3. 플랫폼 설치:
- `vanilla` → 건너뜀.
- `fabric` → Adoptium 자동 설치 → 최신 fabric-installer.jar 다운로드 → `java -jar fabric-installer.jar client -mcversion X -loader Y -dir .mc_custom -noprofile` 자동 실행.
- `forge` / `neoforge``platform.downloadUrl` 의 설치 jar 다운로드(사용자가 직접 실행하거나 마인크래프트 런처가 인식).
4. `modsFolder` 의 모든 `.jar``resourcepackPath` zip, `mapPath` zip 을 자동 다운로드.
5. `.minecraft\{assets,libraries,versions}``.mc_custom\{assets,libraries,versions}` 로 junction 링크. (없으면 "Unable to prepare assets for download" 오류로 마인크래프트가 실패하기 때문)
6. `.minecraft\launcher_profiles.json` 에 해당 음악퀴즈 이름의 프로필을 추가/갱신:
- `gameDir` = `%APPDATA%\.mc_custom`
- `lastVersionId` = `vanilla``mcVersion`, `fabric``fabric-loader-<loaderVer>-<mcVer>` (forge/neoforge 는 `versions/` 폴더에서 휴리스틱 매칭)
- `javaArgs` = `-Xmx<serverMaxRam>M` + Aikar 권장 G1 GC 플래그 6종 (`-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M`). 기존에 사용자가 지정한 키는 덮어쓰지 않음.
## 5단계 — 완료
- 멀티 설치까지 거친 경우:
- **서버 폴더 열기**
- **바탕화면에 서버 실행 바로가기 만들기**
- **서버 바로 실행**
- **마인크래프트 런처 실행** — 실행 우선순위:
1. Win32 설치판 (`Program Files\Minecraft Launcher\MinecraftLauncher.exe` 등)
2. App Execution Alias (`%LOCALAPPDATA%\Microsoft\WindowsApps\Minecraft.exe` 등, `cmd /c start` 경유)
3. `explorer.exe shell:AppsFolder\Microsoft.4297127D64EC6_8wekyb3d8bbwe!Minecraft` (MS Store MSIX)
4. 마지막 수단: `minecraft://` URL 스킴
런처가 떴다면 음악퀴즈 이름의 프로필을 선택해 플레이하면 됩니다.
---
## 로그
설치기 화면 하단에 실시간 로그가 표시됩니다. 모든 다운로드/링크/JVM 인수 갱신/UPnP 시도 내역이 기록됩니다. 문제가 생기면 이 로그 내용을 캡처해 함께 전달해 주세요.
## 음악퀴즈 제거
`%APPDATA%\.mc_custom\` 폴더를 통째로 삭제하면 인스턴스가 사라집니다. `.minecraft\launcher_profiles.json` 에 남은 프로필은 마인크래프트 런처에서 직접 지우거나, 다른 음악퀴즈를 재설치하면 같은 이름이 갱신됩니다.

View File

@@ -0,0 +1,69 @@
# 리소스팩 간편설치기
음악퀴즈에 등록된 음악·사진 목록을 yt-dlp 로 받아 마인크래프트 1.21+ painting variant 텍스처 리소스팩으로 패키징하는 별도 설치기입니다.
기본 골격은 음악퀴즈 간편설치기(`src/installer/`)와 동일하므로 UI/스토어 코드를 공유합니다. 진입점은 `src/installer-rp/main.ts` + `installer-rp/` 렌더러.
## 실행
```bash
npm run installer:rp # 개발 실행
```
윈도우 `.exe` 패키징은 `tsconfig.installer-rp.json` 으로 컴파일한 뒤 `electron-builder` 로 수동 패키징합니다.
## 단계
번호가 붙은 단계는 "이전 / 다음" 으로 이동 가능합니다.
### 1) 음악퀴즈 선택
- 도메인의 `manifest.json` 에서 음악퀴즈 카드 목록을 표시.
- 선택한 음악퀴즈의 `file/list/<key>.json` 에서 다음을 로드:
- 음악 목록(유튜브 영상 주소).
- 사진 목록(유튜브 주소 또는 일반 이미지 주소).
### 2) 리소스팩 설치 (자동 시작)
- "다음" 누르면 즉시 시작.
- 설치 로그를 실시간으로 표시(곡 제목 노출 없이 `n번 노래 다운로드 중…` 형식).
- 취소 버튼 / 창 닫기 / 강제 종료 시: 진행 중 다운로드를 안전하게 중단하고 임시 파일을 정리한 뒤 정상 종료.
#### 2-1. yt-dlp 준비
- 자동 설치 위치: `%APPDATA%/.mc_custom/` (Linux `~/.config/.mc_custom/`, macOS `~/Library/Application Support/.mc_custom/`).
- 이미 있으면 업데이트 확인 후 그대로 사용.
- 시스템 `PATH``yt-dlp` 가 있으면 그것을 우선 사용 후 업데이트 확인.
- 자동 설치가 실패하는 환경은 [`yt-dlp-setup.md`](yt-dlp-setup.md) 참고.
#### 2-2. 음악 다운로드 (순차)
- 임시 경로: `%APPDATA%/.mc_custom/.temp/`.
- 각 곡을 `ogg` 로 변환(Minecraft 사운드 호환 포맷). ffmpeg 필요.
- 중단되거나 모두 끝나면 `.temp` 내용 삭제.
#### 2-3. 사진 다운로드 → painting variant 텍스처
이미지는 두 형태로 들어옵니다.
- **유튜브 주소** — yt-dlp 가 알려준 영상 ID 로 `https://i.ytimg.com/vi/<id>/maxresdefault.jpg` 1차 시도, 실패하면 `hqdefault.jpg` 폴백.
- **일반 이미지 주소** — HTTP GET 으로 그대로.
정규화 규칙:
- **슬롯 규격(고정, 데이터팩 측)**: `4 × 4` 블록 정사각, `cover_01 … cover_N`.
- **최종 PNG 규격(리소스팩 측)**: 정사각 1:1, 최대 `1024 × 1024` px.
- `4 × 4` 블록 × 블록당 `256` px (×16 배율) → 1024×1024 가 픽셀 그리드와 정확히 일치.
- **알고리즘**:
1. 가운데 정사각 크롭: `s = min(원본 가로, 원본 세로)``s × s`.
2. `s > 1024` 이면 `1024 × 1024` 로 축소 (Lanczos 권장).
3. `s ≤ 1024` 이면 그대로 `s × s` 유지(업스케일 없음).
- 파일명: `cover_<NN>.png` (`NN` 은 2자리 0패딩).
- 저장 경로: `resourcepack/assets/musicquiz/textures/painting/`.
- 패키지 완성된 리소스팩을 `%APPDATA%/.minecraft/resourcepacks/` 에 zip 으로 배치.
painting variant 의 슬롯·태그 구조와 게임 내 호출 예시는 [`painting-variant.md`](painting-variant.md) 참고.
### 3) 설치 완료
- "확인" 누르면 프로그램 종료.