Skip to content

feat: milthm chart support#50

Merged
Naptie merged 9 commits intocnfrom
main
Nov 27, 2025
Merged

feat: milthm chart support#50
Naptie merged 9 commits intocnfrom
main

Conversation

@Naptie
Copy link
Member

@Naptie Naptie commented Nov 27, 2025

No description provided.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 27, 2025

Deploying phizone-ui with  Cloudflare Pages  Cloudflare Pages

Latest commit: 06da116
Status: ✅  Deploy successful!
Preview URL: https://0d1449ca.phizone-ui.pages.dev

View logs

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for Milthm chart format to the PhiZone submission system. The implementation allows users to upload Milthm-formatted charts (using Rain Editor) in ZIP files, which are then parsed and processed alongside existing PE/RPE chart formats. The PR also updates Vite from 6.3.5 to 6.4.1 and adds JSZip as a dependency for ZIP file parsing.

Key Changes:

  • Implemented tryParseMilthmZip function to parse Milthm chart ZIP files with fallback logic for file discovery
  • Added new chart format enum value and level types (DZ, SK, CB, CL) to support Milthm difficulty levels
  • Updated translation files across all locales to reflect support for Rain Editor/Milthm charts
  • Modified submission workflow to handle Milthm charts differently (skip preview step, direct upload)

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/routes/(app)/studio/new-submission/+page.svelte Added Milthm ZIP parsing logic, state management, and modified submission flow to handle Milthm charts
src/routes/(app)/studio/new-submission/+page.server.ts Refactored schema creation to support locale-specific validation messages
src/routes/(app)/studio/chart-submissions/new/+page.svelte Added .zip to accepted file types and fixed query enablement with browser check
src/lib/components/SongSubmissionForm.svelte Added Milthm JSON parsing support for BPM extraction alongside existing RPE format
src/lib/utils.ts Added color mappings for new Milthm level types (4-8)
src/lib/types.ts Added MilthmMeta, MilthmBpm, and MilthmJson interfaces
src/lib/api/chart.ts Added Milthm to ChartFormat enum and new level types to ChartLevel enum
src/lib/constants.ts Extended LEVEL_TYPES array with Milthm difficulty levels
src/lib/translations/*.json Updated translation strings to include Rain Editor/Milthm support
package.json Added jszip dependency and upgraded Vite to 6.4.1
.github/.vscode/settings.json Added VSCode workspace settings (misplaced in .github directory)
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

"chart_submissions": "谱面稿件",
"choose_chart": "选择谱面",
"choose_chart_description": "请在此处选择包含 RPE 或 PE 谱面的压缩包(.pez 或 .zip),并且须至少包含谱面、曲目、曲绘文件。",
"choose_chart_description": "PE / RPE / Rain Editor で作成された譜面のみ対応しています。50MB以下。",
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The translation key milthm_preview_not_supported is missing for the ja-JP locale. This key is used at line 943 in the new-submission page but is only defined in en-US and zh-CN translation files. Please add: "milthm_preview_not_supported": "Milthmの譜面プレビューはまだサポートされていません" after line 32 in the session section.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines +257 to +262
const imageFileObj = new File(
[imageBlob],
imageFile.name,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
{ type: 'image/' + imageFile.name.split('.').pop() },
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MIME type construction is incorrect. Image extensions should use proper MIME types: jpg/jpeg → 'image/jpeg', png → 'image/png'. Using 'image/jpg' is technically incorrect, though browsers may handle it. Consider using a proper MIME type mapping.

Suggested change
const imageFileObj = new File(
[imageBlob],
imageFile.name,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
{ type: 'image/' + imageFile.name.split('.').pop() },
// Map image file extensions to correct MIME types
const imageMimeTypes: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
bmp: 'image/bmp',
webp: 'image/webp',
svg: 'image/svg+xml',
};
const imageExt = imageFile.name.split('.').pop()?.toLowerCase() || '';
const imageMimeType = imageMimeTypes[imageExt] || 'application/octet-stream';
const imageFileObj = new File(
[imageBlob],
imageFile.name,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
{ type: imageMimeType },

Copilot uses AI. Check for mistakes.
Comment on lines +449 to +453
if (!songResp.ok) {
alert($t(`error.${(await songResp.json()).code}`));
return;
}
processMatchResults((await songResp.json()).data);
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The response is parsed twice: once to get the error code (line 450) and once to get the data (line 453). This is inefficient and could fail if the response body is already consumed. Store the parsed response in a variable and reuse it.

Suggested change
if (!songResp.ok) {
alert($t(`error.${(await songResp.json()).code}`));
return;
}
processMatchResults((await songResp.json()).data);
const songRespJson = await songResp.json();
if (!songResp.ok) {
alert($t(`error.${songRespJson.code}`));
return;
}
processMatchResults(songRespJson.data);

Copilot uses AI. Check for mistakes.
musicFile.name,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
{ type: 'audio/' + musicFile.name.split('.').pop() },
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MIME type construction is incorrect. File extensions like 'wav' or 'ogg' should not be directly concatenated to 'audio/'. For example, OGG files should use 'audio/ogg' or 'audio/opus', not just 'audio/ogg'. Consider using a proper MIME type mapping or library. Valid MIME types: mp3 → 'audio/mpeg', wav → 'audio/wav', ogg → 'audio/ogg'.

Suggested change
{ type: 'audio/' + musicFile.name.split('.').pop() },
{ type: ((): string => {
const audioMimeTypes: Record<string, string> = {
mp3: 'audio/mpeg',
wav: 'audio/wav',
ogg: 'audio/ogg',
opus: 'audio/opus',
flac: 'audio/flac',
aac: 'audio/aac',
m4a: 'audio/mp4',
};
const ext = musicFile.name.split('.').pop()?.toLowerCase() ?? '';
return audioMimeTypes[ext] || 'audio/*';
})() },

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +136
let lastBpm = 0;
let lastBeat = 0;
let lastTimeSec = 0;
bpmList.forEach((bpm, i) => {
bpm.startBeat = toBeats(bpm.startTime);
bpm.startTimeSec =
i === 0 ? lastTimeSec : lastTimeSec + ((bpm.startBeat - lastBeat) / lastBpm) * 60;
lastBpm = bpm.bpm;
lastBeat = bpm.startBeat;
lastTimeSec = bpm.startTimeSec;
});

const bpmArr = bpmList.map((bpm) => bpm.bpm);
$form.MinBpm = Math.min(...bpmArr);
$form.MaxBpm = Math.max(...bpmArr);
if ($form.MinBpm === $form.MaxBpm) {
$form.Bpm = $form.MinBpm;
}
} else if ('lines' in chartJson && 'meta' in chartJson) {
const { bpms, meta } = chartJson as MilthmJson;
$form.Offset = Math.round(meta.offset * 100);

bpmList = bpms.map((b) => ({
bpm: b.bpm,
startTime: b.time,
startBeat: 0,
startTimeSec: 0,
}));

let lastBpm = 0;
let lastBeat = 0;
let lastTimeSec = 0;
bpmList.forEach((bpm, i) => {
bpm.startBeat = toBeats(bpm.startTime);
bpm.startTimeSec =
i === 0 ? lastTimeSec : lastTimeSec + ((bpm.startBeat - lastBeat) / lastBpm) * 60;
lastBpm = bpm.bpm;
lastBeat = bpm.startBeat;
lastTimeSec = bpm.startTimeSec;
});

const bpmArr = bpmList.map((bpm) => bpm.bpm);
$form.MinBpm = Math.min(...bpmArr);
$form.MaxBpm = Math.max(...bpmArr);
if ($form.MinBpm === $form.MaxBpm) {
$form.Bpm = $form.MinBpm;
}
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] There's significant code duplication between the RPE (lines 90-107) and Milthm (lines 119-136) chart processing branches. The BPM calculation logic is identical. Consider extracting this into a shared function to improve maintainability and reduce duplication.

Copilot uses AI. Check for mistakes.
"chart_submissions": "譜面稿件",
"choose_chart": "选择谱面",
"choose_chart_description": "请在此处选择包含 RPE 或 PE 谱面的压缩包(.pez 或 .zip),并且须至少包含谱面、曲目、曲绘文件。",
"choose_chart_description": "僅支持 PE / RPE / Rain Editor 創作的譜面,不大於 50 MB",
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The translation key milthm_preview_not_supported is missing for the zh-TW locale. This key is used at line 943 in the new-submission page but is only defined in en-US and zh-CN translation files. Please add: "milthm_preview_not_supported": "暫不支援 Milthm 譜面預覽" after line 32 in the session section.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI commented Nov 27, 2025

@Naptie I've opened a new pull request, #51, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 6 commits November 27, 2025 15:50
…d zh-TW locales

Co-authored-by: Naptie <57532127+Naptie@users.noreply.github.com>
Co-authored-by: Naptie <57532127+Naptie@users.noreply.github.com>
Add missing milthm_preview_not_supported translation key for ja-JP and zh-TW locales
…ServerLoad

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@Naptie Naptie merged commit b88573d into cn Nov 27, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants