From 9575464be20bb483426c97cff358e237bb954c2d Mon Sep 17 00:00:00 2001 From: "Troll (Hermes Agent)" Date: Wed, 12 Aug 2026 03:37:43 +0000 Subject: [PATCH] =?UTF-8?q?v0.8.8:=20Fix=20stems=20extraction=20=E2=80=94?= =?UTF-8?q?=20upload=20MP3=20file=20instead=20of=20audio=5Furl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MusicGPT Extraction API's audio_url field only accepts YouTube URLs, not direct MP3 links. Sending our /api/audio-source/ endpoint URL resulted in 'Error fetching audio length from YouTube' because MusicGPT tried to parse it as a YouTube video. Switch to the audio_file upload option: open the local MP3 and upload it directly as multipart/form-data to the Extraction API. Bumped timeout from 30s to 60s for file upload. --- README.md | 4 ++-- VERSION | 2 +- app.py | 6 +++--- helpers.py | 13 +++++++++---- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1c524e7..faaab11 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Theme Song Booth (MusicGPT Edition) -![Version](https://img.shields.io/badge/version-v0.8.7-blue) +![Version](https://img.shields.io/badge/version-v0.8.8-blue) A Flask web application for running a convention booth where visitors request a custom AI-generated theme song. Operators manage the queue from an admin dashboard, generate prompts via Hermes, queue generations through the MusicGPT API, and deliver final songs by email. @@ -169,7 +169,7 @@ A global toggle in `/admin/settings` controls whether the generated album cover 2. Select the source MP3 using the **radio buttons** — Version A or Version B. Only versions with files ready are selectable; unavailable versions are greyed out. 3. Click **Generate Stems**. -The app constructs a public URL for the selected MP3 via the `/api/audio-source//.mp3` endpoint and sends it to the MusicGPT Extraction API. The Extraction API downloads the audio, separates vocals and instrumental tracks, and posts the result to the webhook. +The app uploads the selected MP3 file directly to the MusicGPT Extraction API (the API's `audio_url` field only accepts YouTube URLs, so we use the `audio_file` upload option). The Extraction API separates vocals and instrumental tracks and posts the result to the webhook. ### Stems webhook and Gokapi upload diff --git a/VERSION b/VERSION index 35864a9..5c5cbb3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.7 \ No newline at end of file +0.8.8 \ No newline at end of file diff --git a/app.py b/app.py index 60210d8..0b1674f 100644 --- a/app.py +++ b/app.py @@ -1267,10 +1267,10 @@ def admin_request(rid): if not song_path or not Path(song_path).exists(): flash(f'Version {selected.upper()} MP3 is not available. Generate or upload it first.', 'error') return redirect(url_for('admin_request', rid=rid)) - # Build a public URL the MusicGPT Extraction API can fetch. - audio_url = f"{current_app.config['PUBLIC_BASE_URL'].rstrip('/')}/api/audio-source/{req['player_token']}/{selected}.mp3" + # Upload the MP3 file directly to the Extraction API. + # The API's audio_url field only supports YouTube URLs, not direct MP3 links. stems = request.form.getlist('stems') or ['vocals', 'instrumental'] - task_id, conv_id, estimate, error = musicgpt_queue_stems(rid, audio_url, stems=stems) + task_id, conv_id, estimate, error = musicgpt_queue_stems(rid, song_path, stems=stems) if error: update_request(rid, stems_status='ERROR', stems_error=error) flash(f'Failed to queue stems extraction: {error}', 'error') diff --git a/helpers.py b/helpers.py index a787aac..37b4359 100644 --- a/helpers.py +++ b/helpers.py @@ -619,21 +619,26 @@ def musicgpt_poll_status(task_id): return {"error": str(e)} -def musicgpt_queue_stems(rid, audio_url, stems=None): +def musicgpt_queue_stems(rid, audio_file_path, stems=None): """ - Queue a stem extraction job for a generated audio URL. + Queue a stem extraction job by uploading the MP3 file directly. + + The MusicGPT Extraction API's audio_url field only accepts YouTube URLs, + not direct MP3 URLs. We use the audio_file upload option instead. + Returns (task_id, conversion_id, credit_estimate, error_message). """ url = f"{MUSICGPT_API_BASE}/v2/Extraction" if stems is None: stems = ["vocals", "instrumental"] payload = { - "audio_url": audio_url, "stems": json.dumps(stems), "webhook_url": build_musicgpt_webhook_url(), } try: - resp = requests.post(url, data=payload, headers={"Authorization": get_musicgpt_api_key()}, timeout=30) + with open(audio_file_path, "rb") as f: + files = {"audio_file": f} + resp = requests.post(url, data=payload, files=files, headers={"Authorization": get_musicgpt_api_key()}, timeout=60) try: data = resp.json() except Exception: