Add download all recordings as ZIP feature

- Add /download_all endpoint to create in-memory ZIP archive
- Include zipfile and io imports for ZIP creation
- Add "Download All as ZIP" button in web interface (only shows when recordings exist)
- ZIP filename includes timestamp: wedding_recordings_YYYYMMDD_HHMMSS.zip
- Only includes .wav files from recordings directory
- Update API documentation in README.md
- Document feature in CLAUDE.md

Allows users to quickly backup all guest recordings in a single download.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-27 12:24:17 +07:00
parent 72e39f9515
commit c68c8d2885
3 changed files with 63 additions and 4 deletions

View File

@@ -178,6 +178,14 @@ Edit the `RotaryPhone` class methods:
- Backup called after recording saves and greeting uploads
- Test with `/api/backup/test` endpoint
### Download All Recordings
The `/download_all` endpoint creates a ZIP archive of all recordings in memory:
- Uses `zipfile` and `io.BytesIO()` for in-memory ZIP creation
- Filename includes timestamp: `wedding_recordings_YYYYMMDD_HHMMSS.zip`
- Only includes `.wav` files from recordings directory
- Returns 404 if no recordings exist
- Web UI shows "Download All as ZIP" button when recordings are present
## Testing
### Audio Testing

View File

@@ -300,7 +300,8 @@ The web interface provides four main sections:
#### 4. Recordings
- **Play**: Listen to recordings directly in the browser
- **Download**: Save recordings to your computer
- **Download**: Save individual recordings to your computer
- **Download All**: Download all recordings as a single ZIP file with timestamp
- **Delete**: Remove unwanted recordings
- **Statistics**: View total recordings, storage used, and total duration
@@ -619,6 +620,7 @@ The system provides REST API endpoints:
- `POST /delete_greeting/<filename>` - Delete greeting
- `GET /play_audio/<type>/<filename>` - Stream audio file
- `GET /download/<filename>` - Download recording
- `GET /download_all` - Download all recordings as ZIP
- `POST /delete/<filename>` - Delete recording
- `POST /restore_default_sound` - Generate default dial tone
- `GET /api/backup/status` - Get USB backup drive status

View File

@@ -19,6 +19,8 @@ import json
import sys
import zlib
import shutil
import zipfile
import io
# Load configuration
def load_system_config():
@@ -798,6 +800,42 @@ def download_recording(filename):
return send_file(filepath, as_attachment=True)
return "File not found", 404
@app.route('/download_all')
def download_all_recordings():
"""Download all recordings as a ZIP file"""
if not os.path.exists(OUTPUT_DIR):
return "No recordings found", 404
# Get all recording files
recordings = [f for f in os.listdir(OUTPUT_DIR)
if f.endswith('.wav') and os.path.isfile(os.path.join(OUTPUT_DIR, f))]
if not recordings:
return "No recordings found", 404
# Create ZIP file in memory
memory_file = io.BytesIO()
with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
for filename in recordings:
filepath = os.path.join(OUTPUT_DIR, filename)
# Add file to ZIP with just the filename (no path)
zf.write(filepath, arcname=filename)
# Seek to beginning of file
memory_file.seek(0)
# Generate filename with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
zip_filename = f"wedding_recordings_{timestamp}.zip"
return send_file(
memory_file,
mimetype='application/zip',
as_attachment=True,
download_name=zip_filename
)
@app.route('/delete/<filename>', methods=['POST'])
def delete_recording(filename):
"""Delete a recording"""
@@ -1499,8 +1537,15 @@ def main():
<!-- Recordings Card -->
<div class="card">
<h2>🎙️ Recordings</h2>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h2 style="margin: 0;">🎙️ Recordings</h2>
{% if recordings %}
<button class="btn btn-primary" onclick="downloadAllRecordings()" style="font-size: 0.9em;">
📦 Download All as ZIP
</button>
{% endif %}
</div>
{% if recordings %}
<div class="stats">
<div class="stat-box">
@@ -1715,7 +1760,11 @@ def main():
function downloadRecording(filename) {
window.location.href = '/download/' + filename;
}
function downloadAllRecordings() {
window.location.href = '/download_all';
}
function deleteRecording(filename, index) {
if (confirm('Delete this recording?')) {
fetch('/delete/' + filename, { method: 'POST' })