Download Stems button now zips all stem files into a single .zip
- New /admin/request/<id>/download-stems endpoint - Parses stems_url (label: url; label: url format), fetches each stem file, bundles them into a zip served as an attachment download - If MusicGPT returns a single .zip bundle URL, redirects directly - Template button now points to the zip endpoint instead of raw CDN URLs
This commit is contained in:
parent
c676acd32f
commit
f2c64848dd
2 changed files with 73 additions and 1 deletions
72
app.py
72
app.py
|
|
@ -34,6 +34,9 @@ import hmac
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
import io
|
||||||
|
import zipfile
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Flask and related imports
|
# Flask and related imports
|
||||||
|
|
@ -790,6 +793,75 @@ def audio(token, version):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/admin/request/<int:rid>/download-stems')
|
||||||
|
def admin_download_stems(rid):
|
||||||
|
"""Download all stems files for a request as a single .zip archive."""
|
||||||
|
redir = require_admin()
|
||||||
|
if redir:
|
||||||
|
return redir
|
||||||
|
req = get_request_by_id(rid)
|
||||||
|
if not req:
|
||||||
|
abort(404)
|
||||||
|
req = dict(req)
|
||||||
|
|
||||||
|
stems_url = req.get('stems_url') or ''
|
||||||
|
if not stems_url:
|
||||||
|
flash('No stems available to download.', 'error')
|
||||||
|
return redirect(url_for('admin_request', rid=rid))
|
||||||
|
|
||||||
|
# Parse the stems_url field. It can be:
|
||||||
|
# 1. A single bundle/zip URL (use directly)
|
||||||
|
# 2. "label1: url1; label2: url2" (multiple individual stem files)
|
||||||
|
# 3. Just a bare URL
|
||||||
|
urls = []
|
||||||
|
if ';' in stems_url or ': ' in stems_url:
|
||||||
|
# Multiple stems: "instrumental: https://...; vocals: https://..."
|
||||||
|
parts = stems_url.split(';')
|
||||||
|
for part in parts:
|
||||||
|
part = part.strip()
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
# Split on first ": " to separate label from URL
|
||||||
|
if ': ' in part:
|
||||||
|
label, url = part.split(': ', 1)
|
||||||
|
urls.append((label.strip(), url.strip()))
|
||||||
|
else:
|
||||||
|
urls.append(('stem', part))
|
||||||
|
else:
|
||||||
|
urls.append(('stems', stems_url.strip()))
|
||||||
|
|
||||||
|
if not urls:
|
||||||
|
flash('No stems files found.', 'error')
|
||||||
|
return redirect(url_for('admin_request', rid=rid))
|
||||||
|
|
||||||
|
# If there's only one URL and it's already a .zip, redirect directly.
|
||||||
|
if len(urls) == 1 and urls[0][1].endswith('.zip'):
|
||||||
|
return redirect(urls[0][1])
|
||||||
|
|
||||||
|
# Download each stem file and bundle them into a zip in memory.
|
||||||
|
import requests as req_lib
|
||||||
|
buf = io.BytesIO()
|
||||||
|
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
for label, url in urls:
|
||||||
|
try:
|
||||||
|
r = req_lib.get(url, timeout=60)
|
||||||
|
r.raise_for_status()
|
||||||
|
# Derive a filename from the URL or label.
|
||||||
|
filename = url.split('/')[-1] if '/' in url else f"{label}.mp3"
|
||||||
|
# If the URL has no extension, use the label.
|
||||||
|
if '.' not in filename:
|
||||||
|
filename = f"{label}.mp3"
|
||||||
|
zf.writestr(filename, r.content)
|
||||||
|
except Exception:
|
||||||
|
# Skip files that fail to download, but include the rest.
|
||||||
|
pass
|
||||||
|
buf.seek(0)
|
||||||
|
|
||||||
|
title = req.get('suno_title') or req.get('name') or f'request_{rid}'
|
||||||
|
download_name = f"stems_{title}.zip"
|
||||||
|
return send_file(buf, mimetype='application/zip', as_attachment=True, download_name=download_name)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Admin routes
|
# Admin routes
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -618,7 +618,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if req.stems_url %}
|
{% if req.stems_url %}
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<a href="{{ req.stems_url }}" target="_blank" rel="noopener noreferrer" class="button-link" style="display:inline-flex;align-items:center;gap:.4rem;padding:.5rem 1rem;background:#4b5563;border-radius:.375rem;color:#fff;text-decoration:none;font-size:.875rem">⬇ Download Stems</a>
|
<a href="{{ url_for('admin_download_stems', rid=req.id) }}" class="button-link" style="display:inline-flex;align-items:center;gap:.4rem;padding:.5rem 1rem;background:#4b5563;border-radius:.375rem;color:#fff;text-decoration:none;font-size:.875rem">⬇ Download Stems (zip)</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue