[+] Step-by-step buttons
This commit is contained in:
@@ -140,7 +140,7 @@ export const getSongUrl = async (id: number | string) => {
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
// API for Song Preparation
|
||||
|
||||
export interface ProgressItem { task: string, progress: number }
|
||||
export interface ProgressItem { id: string, task: string, progress: number }
|
||||
export interface SongProcessState { items: ProgressItem[], status: 'running' | 'done' | 'error' }
|
||||
|
||||
const songProcessingStatus = new Map<number, SongProcessState>()
|
||||
@@ -152,20 +152,20 @@ export const prepareSong = async (songId: number) => {
|
||||
const state: SongProcessState = { items: [], status: 'running' }
|
||||
songProcessingStatus.set(songId, state)
|
||||
|
||||
const addTask = (task: string) => ({ task, progress: 0 }).also(it => state.items.push(it))
|
||||
const addTask = (id: string, task: string) => ({ id, task, progress: 0 }).also(it => state.items.push(it))
|
||||
try {
|
||||
// 1. Get Lyrics
|
||||
const taskLyrics = addTask('从网易云获取歌词')
|
||||
const taskLyrics = addTask('lyrics', '从网易云获取歌词')
|
||||
const raw = await getLyricsRaw(songId)
|
||||
taskLyrics.progress = 1
|
||||
|
||||
if (raw.lang !== 'jpn') {
|
||||
addTask('错误: 不是日语歌曲').progress = -1
|
||||
addTask('error', '错误: 不是日语歌曲').progress = -1
|
||||
return state.status = 'error'
|
||||
}
|
||||
|
||||
// 2. AI Process
|
||||
const taskAI = addTask('AI 标注歌词读音')
|
||||
const taskAI = addTask('ai', 'AI 标注歌词读音')
|
||||
|
||||
// Check cache
|
||||
if (await checkLyricsProcessed(songId)) taskAI.progress = 1
|
||||
@@ -176,12 +176,12 @@ export const prepareSong = async (songId: number) => {
|
||||
}
|
||||
|
||||
// 3. Audio
|
||||
const taskAudio = addTask('从网易云获取音乐')
|
||||
const taskAudio = addTask('music', '从网易云获取音乐')
|
||||
await getSongUrl(songId)
|
||||
taskAudio.progress = 1
|
||||
|
||||
// 4. Source Separation
|
||||
const taskSeparation = addTask('AI 人声分离')
|
||||
const taskSeparation = addTask('separation', 'AI 人声分离')
|
||||
const inputPath = path.join(CACHE_DIR, `${songId}/exhigh.mp3`)
|
||||
const outputDir = path.join(CACHE_DIR, `${songId}`)
|
||||
|
||||
@@ -189,14 +189,14 @@ export const prepareSong = async (songId: number) => {
|
||||
await separateSong(inputPath, outputDir)
|
||||
taskSeparation.progress = 1
|
||||
} catch (e: any) {
|
||||
addTask(`错误: ${e.message}`).progress = -1
|
||||
addTask('error', `错误: ${e.message}`).progress = -1
|
||||
// Don't fail the whole process, just this step
|
||||
}
|
||||
|
||||
state.status = 'done'
|
||||
|
||||
} catch (e) {
|
||||
addTask(`错误: ${eToString(e)}`).progress = -1
|
||||
addTask('error', `错误: ${eToString(e)}`).progress = -1
|
||||
state.status = 'error'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,17 @@
|
||||
const t = getI18n().song.mode
|
||||
|
||||
let { data } = $props()
|
||||
let taskStatus = $state({
|
||||
lyrics: false,
|
||||
ai: false,
|
||||
music: false,
|
||||
separation: false
|
||||
})
|
||||
|
||||
let modes = $derived([
|
||||
{ icon: "i-material-symbols:keyboard-rounded", label: t.typing, url: `/song/${data.song.id}/play` },
|
||||
{ icon: "i-material-symbols:music-note-rounded", label: t.music, url: `/song/${data.song.id}/play?music=true` },
|
||||
{ icon: "i-material-symbols:mic-rounded", label: t.karaoke, url: `/song/${data.song.id}/karaoke` },
|
||||
{ icon: "i-material-symbols:keyboard-rounded", label: t.typing, url: `/song/${data.song.id}/play`, disabled: !taskStatus.lyrics || !taskStatus.ai },
|
||||
{ icon: "i-material-symbols:music-note-rounded", label: t.music, url: `/song/${data.song.id}/play?music=true`, disabled: !taskStatus.music },
|
||||
{ icon: "i-material-symbols:mic-rounded", label: t.karaoke, url: `/song/${data.song.id}/karaoke`, disabled: !taskStatus.separation },
|
||||
])
|
||||
|
||||
let loadStatus = $state<"idle" | "loading" | "done">("idle")
|
||||
@@ -33,6 +40,15 @@
|
||||
const state = res.status
|
||||
|
||||
if (state && state.items) {
|
||||
// Update task status
|
||||
for (const item of state.items) {
|
||||
if (item.progress !== 1) continue
|
||||
if (item.id === 'lyrics') taskStatus.lyrics = true
|
||||
if (item.id === 'ai') taskStatus.ai = true
|
||||
if (item.id === 'music') taskStatus.music = true
|
||||
if (item.id === 'separation') taskStatus.separation = true
|
||||
}
|
||||
|
||||
progressItems = state.items.map((item: any) => ({
|
||||
title: item.task + (item.progress > 0 && item.progress < 1 ? ` (${Math.round(item.progress * 100)}%)` : ''),
|
||||
icon: item.progress === 1 ? 'i-material-symbols:check text-green-500' :
|
||||
@@ -59,10 +75,8 @@
|
||||
|
||||
<ProgressList percentage={progressPercentage} items={progressItems} />
|
||||
|
||||
{#if loadStatus === "done"}
|
||||
<div class="hbox gap-4 p-16px flex-wrap">
|
||||
{#each modes as mode}
|
||||
<Button big icon={mode.icon} onclick={() => goto(mode.url)} class="!w-auto !min-w-[calc(50%-8px)] grow">{mode.label}</Button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="hbox gap-4 p-16px flex-wrap">
|
||||
{#each modes as mode}
|
||||
<Button big icon={mode.icon} onclick={() => goto(mode.url)} disabled={mode.disabled} class="!w-auto !min-w-[calc(50%-8px)] grow disabled:opacity-50 disabled:cursor-not-allowed">{mode.label}</Button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user