| import { useEffect, useMemo, useState } from "react"; |
|
|
| const DEFAULT_PROMPT = |
| "Analyze this full song and provide concise, timestamped sections describing vocals, instrumentation, production effects, mix changes, energy flow, and genre cues. End with a short overall summary."; |
|
|
| export default function App() { |
| const [mode, setMode] = useState("path"); |
| const [audioPath, setAudioPath] = useState("E:\\Coding\\hf-music-gen\\train-dataset\\Andrew Spacey - Wonder (Prod Beat It AT).mp3"); |
| const [audioFile, setAudioFile] = useState(null); |
| const [backend, setBackend] = useState("hf_endpoint"); |
| const [endpointUrl, setEndpointUrl] = useState(""); |
| const [hfToken, setHfToken] = useState(""); |
| const [modelId, setModelId] = useState("nvidia/audio-flamingo-3-hf"); |
| const [openAiApiKey, setOpenAiApiKey] = useState(""); |
| const [openAiModel, setOpenAiModel] = useState("gpt-5-mini"); |
| const [prompt, setPrompt] = useState(DEFAULT_PROMPT); |
| const [userContext, setUserContext] = useState(""); |
| const [artistName, setArtistName] = useState(""); |
| const [trackName, setTrackName] = useState(""); |
| const [enableWebSearch, setEnableWebSearch] = useState(false); |
| const [loading, setLoading] = useState(false); |
| const [error, setError] = useState(""); |
| const [result, setResult] = useState(null); |
|
|
| useEffect(() => { |
| let mounted = true; |
| fetch("/api/config") |
| .then((r) => r.json()) |
| .then((data) => { |
| if (!mounted) return; |
| const d = data?.defaults || {}; |
| if (d.backend) setBackend(d.backend); |
| if (d.endpoint_url) setEndpointUrl(d.endpoint_url); |
| if (d.model_id) setModelId(d.model_id); |
| if (d.openai_model) setOpenAiModel(d.openai_model); |
| if (d.af3_prompt) setPrompt(d.af3_prompt); |
| }) |
| .catch(() => {}); |
| return () => { |
| mounted = false; |
| }; |
| }, []); |
|
|
| const requestPreview = useMemo(() => { |
| return { |
| backend, |
| endpoint_url: endpointUrl || "(env default)", |
| model_id: modelId, |
| openai_model: openAiModel, |
| enable_web_search: enableWebSearch, |
| artist_name: artistName || "(none)", |
| track_name: trackName || "(none)", |
| }; |
| }, [backend, endpointUrl, modelId, openAiModel, enableWebSearch, artistName, trackName]); |
|
|
| async function runPipeline() { |
| setLoading(true); |
| setError(""); |
| setResult(null); |
| try { |
| let response; |
| if (mode === "path") { |
| response = await fetch("/api/pipeline/run-path", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ |
| audio_path: audioPath, |
| backend, |
| endpoint_url: endpointUrl, |
| hf_token: hfToken, |
| model_id: modelId, |
| af3_prompt: prompt, |
| openai_api_key: openAiApiKey, |
| openai_model: openAiModel, |
| user_context: userContext, |
| artist_name: artistName, |
| track_name: trackName, |
| enable_web_search: enableWebSearch, |
| }), |
| }); |
| } else { |
| if (!audioFile) { |
| throw new Error("Select an audio file first."); |
| } |
| const form = new FormData(); |
| form.append("audio_file", audioFile); |
| form.append("backend", backend); |
| form.append("endpoint_url", endpointUrl); |
| form.append("hf_token", hfToken); |
| form.append("model_id", modelId); |
| form.append("af3_prompt", prompt); |
| form.append("openai_api_key", openAiApiKey); |
| form.append("openai_model", openAiModel); |
| form.append("user_context", userContext); |
| form.append("artist_name", artistName); |
| form.append("track_name", trackName); |
| form.append("enable_web_search", String(enableWebSearch)); |
| response = await fetch("/api/pipeline/run-upload", { |
| method: "POST", |
| body: form, |
| }); |
| } |
|
|
| const data = await response.json(); |
| if (!response.ok) { |
| const detail = typeof data?.detail === "string" ? data.detail : JSON.stringify(data); |
| throw new Error(detail); |
| } |
| setResult(data); |
| } catch (err) { |
| setError(err.message || String(err)); |
| } finally { |
| setLoading(false); |
| } |
| } |
|
|
| return ( |
| <div className="page"> |
| <div className="hero"> |
| <h1>AF3 + ChatGPT Pipeline</h1> |
| <p>Run Audio Flamingo 3 analysis, then clean/structure for Ace Step 1.5 LoRA metadata.</p> |
| </div> |
| |
| <div className="grid"> |
| <section className="card"> |
| <h2>Inputs</h2> |
| <div className="row"> |
| <label>Mode</label> |
| <select value={mode} onChange={(e) => setMode(e.target.value)}> |
| <option value="path">Local Path</option> |
| <option value="upload">Upload</option> |
| </select> |
| </div> |
| |
| {mode === "path" ? ( |
| <div className="row"> |
| <label>Audio Path</label> |
| <input value={audioPath} onChange={(e) => setAudioPath(e.target.value)} /> |
| </div> |
| ) : ( |
| <div className="row"> |
| <label>Audio File</label> |
| <input type="file" accept="audio/*" onChange={(e) => setAudioFile(e.target.files?.[0] || null)} /> |
| </div> |
| )} |
| |
| <div className="row"> |
| <label>AF3 Backend</label> |
| <select value={backend} onChange={(e) => setBackend(e.target.value)}> |
| <option value="hf_endpoint">HF Endpoint</option> |
| <option value="local">Local Model</option> |
| </select> |
| </div> |
| <div className="row"> |
| <label>AF3 Endpoint URL</label> |
| <input value={endpointUrl} onChange={(e) => setEndpointUrl(e.target.value)} placeholder="https://..." /> |
| </div> |
| <div className="row"> |
| <label>HF Token (optional)</label> |
| <input type="password" value={hfToken} onChange={(e) => setHfToken(e.target.value)} /> |
| </div> |
| <div className="row"> |
| <label>AF3 Model ID</label> |
| <input value={modelId} onChange={(e) => setModelId(e.target.value)} /> |
| </div> |
| <div className="row"> |
| <label>OpenAI API Key (optional)</label> |
| <input type="password" value={openAiApiKey} onChange={(e) => setOpenAiApiKey(e.target.value)} /> |
| </div> |
| <div className="row"> |
| <label>OpenAI Model</label> |
| <input value={openAiModel} onChange={(e) => setOpenAiModel(e.target.value)} /> |
| </div> |
| <div className="row"> |
| <label>Artist (optional)</label> |
| <input value={artistName} onChange={(e) => setArtistName(e.target.value)} /> |
| </div> |
| <div className="row"> |
| <label>Track (optional)</label> |
| <input value={trackName} onChange={(e) => setTrackName(e.target.value)} /> |
| </div> |
| <div className="row"> |
| <label>Prompt</label> |
| <textarea rows={5} value={prompt} onChange={(e) => setPrompt(e.target.value)} /> |
| </div> |
| <div className="row"> |
| <label>User Context</label> |
| <textarea rows={4} value={userContext} onChange={(e) => setUserContext(e.target.value)} /> |
| </div> |
| <div className="row inline"> |
| <input |
| id="websearch" |
| type="checkbox" |
| checked={enableWebSearch} |
| onChange={(e) => setEnableWebSearch(e.target.checked)} |
| /> |
| <label htmlFor="websearch">Enable ChatGPT web search (optional)</label> |
| </div> |
| |
| <button className="run" disabled={loading} onClick={runPipeline}> |
| {loading ? "Running..." : "Run Pipeline"} |
| </button> |
| </section> |
| |
| <section className="card"> |
| <h2>Request Summary</h2> |
| <pre>{JSON.stringify(requestPreview, null, 2)}</pre> |
| {error ? <p className="error">{error}</p> : null} |
| {result ? ( |
| <> |
| <h3>Saved Sidecar</h3> |
| <p className="mono">{result.saved_to}</p> |
| <h3>AF3 Analysis</h3> |
| <pre>{result.af3_analysis}</pre> |
| <h3>Final LoRA JSON</h3> |
| <pre>{JSON.stringify(result.sidecar, null, 2)}</pre> |
| </> |
| ) : null} |
| </section> |
| </div> |
| </div> |
| ); |
| } |
|
|