op: 데이터팩 출력을 실제 music_quiz zip 으로 교체

가이드 (mc_datapack/launcher_datapack_연동_가이드.txt) 에 따라:
- file/datapacks/music_quiz_template/ 에 mc_datapack 의 music_quiz/ 정적
  파일을 미리 동봉 (data/mq/function/init/songs.mcfunction 제외).
- src/server/datapack.ts: list.music → SNBT (`{title, author, alias}`)
  songs.mcfunction 빌더와 archiver 기반 zip 스트리머 추가.
- /op/datapack/:packName/generate 가 텍스트 placeholder 대신
  music_quiz_<key>.zip 을 Content-Disposition attachment 로 내려준다.
- datapack.ejs 의 코드블록·복사 UI 제거, 곡 수는 서버 렌더 시점에 표시.
- 더 이상 쓰이지 않는 locales 의 datapackOutput.* 키 제거, datapack
  버튼 라벨/상태 문구를 zip 다운로드용으로 정리.
This commit is contained in:
2026-05-13 16:34:34 +09:00
parent 2344c4b8d2
commit af884706d4
66 changed files with 871 additions and 72 deletions

View File

@@ -17,6 +17,7 @@ import { fetchPlaylistEntries, fetchVideoMeta, YtDlpUnavailableError } from '../
import { requireAuth } from '../middleware/auth.js'
import type { PackDefinition, PackList } from '../../shared/types.js'
import { t } from '../i18n.js'
import { streamMusicQuizZip } from '../datapack.js'
export const opRouter = Router()
@@ -223,17 +224,19 @@ opRouter.post('/op/list/:packName/playlist', requireAuth, async (req, res) => {
opRouter.get('/op/datapack', requireAuth, async (req, res, next) => {
try {
const keys = await listPackKeys()
const items = await Promise.all(keys.map(async (key) => ({
key,
definition: await loadPackDefinition(key)
})))
const items = await Promise.all(keys.map(async (key) => {
const definition = await loadPackDefinition(key)
const list = await loadPackList(key)
return { key, definition, musicCount: list.music.length }
}))
res.render('op/datapack', { userId: req.session.userId, items })
} catch (error) {
next(error)
}
})
// 데이터팩 출력: 임시 포맷의 mcfunction 텍스트를 반환.
// 데이터팩 출력: mc_datapack 의 music_quiz/ 템플릿을 zip 으로 묶고,
// data/mq/function/init/songs.mcfunction 만 list.music 으로 새로 만들어 덮어쓴다.
opRouter.get('/op/datapack/:packName/generate', requireAuth, async (req, res, next) => {
try {
const packKey = sanitizePackKey(pickFirstValue(req.params.packName))
@@ -243,25 +246,7 @@ opRouter.get('/op/datapack/:packName/generate', requireAuth, async (req, res, ne
return
}
const list = await loadPackList(packKey)
const lines: string[] = []
lines.push(t('datapackOutput.header', { name: definition.name }))
lines.push(t('datapackOutput.summary', {
musicCount: list.music.length,
imageCount: list.images.length
}))
lines.push(t('datapackOutput.initLine'))
lines.push(t('datapackOutput.placeholder'))
list.music.forEach((entry, index) => {
const title = entry.title || t('datapackOutput.titleFallback')
const artist = entry.artist || t('datapackOutput.artistFallback')
lines.push(t('datapackOutput.trackLine', {
index: index + 1,
title,
artist,
duration: entry.durationSec
}))
})
res.type('text/plain; charset=utf-8').send(lines.join('\n') + '\n')
streamMusicQuizZip(res, packKey, list)
} catch (error) {
next(error)
}