Add delete-songs controls to admin request page
Operators can now select Version A and/or Version B in the Upload Songs section and click 'Delete Selected Songs'. The handler removes the chosen MP3 files, clears their DB paths, resets customer_approved to 'none', and sets the request status back to 'prompt_ready' so new songs can be uploaded. Bumps version to 0.5.1.
This commit is contained in:
parent
d7e54809c9
commit
7eb72e511d
4 changed files with 68 additions and 18 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
# Theme Song Booth
|
# Theme Song Booth
|
||||||
|
|
||||||
**Version:** `v0.5.0`
|
**Version:** `v0.5.1`
|
||||||
|
|
||||||
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 Suno prompts, upload MP3 previews, collect payment, and deliver final songs by email.
|
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 Suno prompts, upload MP3 previews, collect payment, and deliver final songs by email.
|
||||||
|
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.5.0
|
0.5.1
|
||||||
|
|
|
||||||
36
app.py
36
app.py
|
|
@ -1126,6 +1126,42 @@ def admin_request(rid):
|
||||||
)
|
)
|
||||||
flash('Prompt saved.', 'success')
|
flash('Prompt saved.', 'success')
|
||||||
|
|
||||||
|
elif action == 'delete_songs':
|
||||||
|
# Remove selected uploaded MP3s and reset status so new songs can be uploaded.
|
||||||
|
to_delete = request.form.getlist('delete_song')
|
||||||
|
if not to_delete:
|
||||||
|
flash('Select at least one song to delete.', 'error')
|
||||||
|
return redirect(url_for('admin_request', rid=rid))
|
||||||
|
|
||||||
|
update_fields = {}
|
||||||
|
for version in to_delete:
|
||||||
|
if version == 'a':
|
||||||
|
path = req.get('song_a_path')
|
||||||
|
if path and Path(path).exists():
|
||||||
|
try:
|
||||||
|
Path(path).unlink()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
update_fields['song_a_path'] = None
|
||||||
|
elif version == 'b':
|
||||||
|
path = req.get('song_b_path')
|
||||||
|
if path and Path(path).exists():
|
||||||
|
try:
|
||||||
|
Path(path).unlink()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
update_fields['song_b_path'] = None
|
||||||
|
|
||||||
|
if update_fields:
|
||||||
|
# Wipe any prior customer approval because the old files are gone.
|
||||||
|
update_fields['customer_approved'] = 'none'
|
||||||
|
# Reset to prompt_ready since songs need to be uploaded again.
|
||||||
|
update_fields['status'] = 'prompt_ready'
|
||||||
|
update_request(rid, **update_fields)
|
||||||
|
flash('Selected songs deleted. Request reset to Prompt Ready.', 'success')
|
||||||
|
else:
|
||||||
|
flash('No uploaded songs selected for deletion.', 'error')
|
||||||
|
|
||||||
elif action == 'upload_songs':
|
elif action == 'upload_songs':
|
||||||
# Save uploaded MP3 files for Version A and/or Version B.
|
# Save uploaded MP3 files for Version A and/or Version B.
|
||||||
# Use the saved Suno title as the MP3 title tag if available.
|
# Use the saved Suno title as the MP3 title tag if available.
|
||||||
|
|
|
||||||
|
|
@ -325,22 +325,36 @@
|
||||||
<!-- Upload Songs -->
|
<!-- Upload Songs -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>2. Upload Songs</h2>
|
<h2>2. Upload Songs</h2>
|
||||||
<p>
|
<form method="POST">
|
||||||
Version A:
|
<input type="hidden" name="action" value="delete_songs">
|
||||||
{% if req.song_a_path and file_exists(req.song_a_path) %}
|
<p>
|
||||||
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_a_path) }}
|
<input type="checkbox" id="delete_a" name="delete_song" value="a"
|
||||||
{% else %}
|
{% if not (req.song_a_path and file_exists(req.song_a_path)) %}disabled{% endif %}>
|
||||||
<span class="status-badge missing">❌ Not uploaded</span>
|
<label for="delete_a">Version A:
|
||||||
{% endif %}
|
{% if req.song_a_path and file_exists(req.song_a_path) %}
|
||||||
</p>
|
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_a_path) }}
|
||||||
<p>
|
{% else %}
|
||||||
Version B:
|
<span class="status-badge missing">❌ Not uploaded</span>
|
||||||
{% if req.song_b_path and file_exists(req.song_b_path) %}
|
{% endif %}
|
||||||
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_b_path) }}
|
</label>
|
||||||
{% else %}
|
</p>
|
||||||
<span class="status-badge missing">❌ Not uploaded</span>
|
<p>
|
||||||
{% endif %}
|
<input type="checkbox" id="delete_b" name="delete_song" value="b"
|
||||||
</p>
|
{% if not (req.song_b_path and file_exists(req.song_b_path)) %}disabled{% endif %}>
|
||||||
|
<label for="delete_b">Version B:
|
||||||
|
{% if req.song_b_path and file_exists(req.song_b_path) %}
|
||||||
|
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_b_path) }}
|
||||||
|
{% else %}
|
||||||
|
<span class="status-badge missing">❌ Not uploaded</span>
|
||||||
|
{% endif %}
|
||||||
|
</label>
|
||||||
|
</p>
|
||||||
|
<div class="actions">
|
||||||
|
<button type="submit" class="danger" onclick="return confirm('Delete selected song files? This will reset the request to Prompt Ready.')">Delete Selected Songs</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr style="border-color:#374151;margin:1rem 0">
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<input type="hidden" name="action" value="upload_songs">
|
<input type="hidden" name="action" value="upload_songs">
|
||||||
|
|
|
||||||
Reference in a new issue