Add rename functionality for recordings

- Added /rename/<filename> API endpoint
  - Accepts new_name in JSON body
  - Automatically appends .wav extension if missing
  - Uses secure_filename() for safety
  - Checks for existing files to prevent overwrites
  - Backs up renamed file to USB if backup enabled

- Updated web interface (template v1.6.0)
  - Added "Rename" button to each recording
  - Added renameRecording() JavaScript function
  - Uses prompt dialog for name input
  - Pre-fills current name (without .wav extension)
  - Reloads page after successful rename

This allows users to assign guest names to recordings
for easier organization (e.g., "John_Smith.wav").

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-27 13:14:58 +07:00
parent 73ca1e6935
commit 7cce795a43

View File

@@ -81,7 +81,7 @@ BACKUP_ON_WRITE = BACKUP_CONFIG.get('backup_on_write', True)
WEB_PORT = SYS_CONFIG['web']['port']
# Template version - increment this when HTML template changes
TEMPLATE_VERSION = "1.5.0" # Updated: Redesigned sound selection with dropdowns
TEMPLATE_VERSION = "1.6.0" # Updated: Added rename functionality for recordings
# Flask app
app = Flask(__name__)
@@ -945,6 +945,40 @@ def delete_recording(filename):
return jsonify({"success": True})
return jsonify({"error": "File not found"}), 404
@app.route('/rename/<filename>', methods=['POST'])
def rename_recording(filename):
"""Rename a recording"""
data = request.get_json()
new_name = data.get('new_name', '').strip()
if not new_name:
return jsonify({"error": "New name is required"}), 400
# Ensure .wav extension
if not new_name.endswith('.wav'):
new_name += '.wav'
# Sanitize filenames
old_filepath = os.path.join(OUTPUT_DIR, secure_filename(filename))
new_filepath = os.path.join(OUTPUT_DIR, secure_filename(new_name))
if not os.path.exists(old_filepath):
return jsonify({"error": "File not found"}), 404
if os.path.exists(new_filepath):
return jsonify({"error": "A file with that name already exists"}), 409
try:
os.rename(old_filepath, new_filepath)
# Also backup renamed file if backup is enabled
if SYS_CONFIG['backup']['enabled'] and SYS_CONFIG['backup']['backup_on_write']:
backup_file_to_usb(new_filepath, 'recordings')
return jsonify({"success": True, "new_name": secure_filename(new_name)})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/restore_default_sound', methods=['POST'])
def restore_default_sound():
"""Restore default dial tone"""
@@ -1752,10 +1786,10 @@ def main():
{% for recording in recordings %}
<div class="recording-item" id="recording-{{ loop.index }}">
<div class="recording-info">
<h3>{{ recording.filename }}</h3>
<h3 id="filename-{{ loop.index }}">{{ recording.filename }}</h3>
<div class="recording-meta">
📅 {{ recording.date }} |
⏱️ {{ "%.1f"|format(recording.duration) }}s |
📅 {{ recording.date }} |
⏱️ {{ "%.1f"|format(recording.duration) }}s |
💾 {{ "%.2f"|format(recording.size_mb) }} MB
</div>
</div>
@@ -1766,6 +1800,9 @@ def main():
<button class="btn btn-primary" onclick="downloadRecording('{{ recording.filename }}')">
⬇️ Download
</button>
<button class="btn btn-secondary" onclick="renameRecording('{{ recording.filename }}', {{ loop.index }})">
✏️ Rename
</button>
<button class="btn btn-danger" onclick="deleteRecording('{{ recording.filename }}', {{ loop.index }})">
🗑️ Delete
</button>
@@ -1985,6 +2022,30 @@ def main():
}
}
function renameRecording(filename, index) {
// Remove .wav extension for cleaner input
const nameWithoutExt = filename.replace('.wav', '');
const newName = prompt('Enter new name for recording:', nameWithoutExt);
if (newName && newName.trim() !== '' && newName !== nameWithoutExt) {
fetch('/rename/' + filename, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ new_name: newName.trim() })
})
.then(response => response.json())
.then(data => {
if (data.success) {
showAlert('Recording renamed! ✓', 'success');
setTimeout(() => location.reload(), 1500);
} else {
showAlert('Error: ' + data.error, 'error');
}
})
.catch(error => showAlert('Error: ' + error, 'error'));
}
}
function playAudio(type, filename) {
const modal = document.getElementById('audio-modal');
const player = document.getElementById('audio-player');