Add recording sorting with multiple options

Added comprehensive sorting functionality for recordings:

Backend changes:
- Updated get_recordings() to accept sort_by and sort_order parameters
- Sort options: 'date', 'name', 'duration', 'size'
- Sort order: 'asc' (ascending) or 'desc' (descending)
- Added timestamp field to recordings for accurate date sorting
- Default sort: by date, descending (newest first)

Frontend changes (template v1.8.0):
- Added sort controls above recordings list
- Two dropdowns: sort field and sort direction
- Visual styling with emojis for each option:
  📅 Date - Sort by recording date/time
  📝 Name - Sort alphabetically by filename
  ⏱️ Duration - Sort by recording length
  💾 Size - Sort by file size
  ⬇️ Descending / ⬆️ Ascending
- updateSort() JavaScript function reloads page with params
- Preserves selected sort options via query params

URL parameters:
- ?sort=date&order=desc (default)
- ?sort=name&order=asc (alphabetical A-Z)
- ?sort=duration&order=desc (longest first)
- ?sort=size&order=asc (smallest first)

This makes it easy to find specific recordings or organize
them by different criteria depending on what you need.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-27 16:20:13 +07:00
parent 13e1c6dd5c
commit 63ce5b1703

View File

@@ -151,7 +151,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.7.0" # Updated: Separate volume controls for greeting, button, and beep
TEMPLATE_VERSION = "1.8.0" # Updated: Added recording sorting by date, name, duration, size
# Flask app
app = Flask(__name__)
@@ -834,7 +834,11 @@ phone = RotaryPhone()
@app.route('/')
def index():
"""Main page"""
recordings = get_recordings()
# Get sort parameters from query string
sort_by = request.args.get('sort', 'date')
sort_order = request.args.get('order', 'desc')
recordings = get_recordings(sort_by=sort_by, sort_order=sort_order)
greetings = get_greetings()
status = phone.get_status()
active_greeting = phone.config.get("active_greeting", "dialtone.wav")
@@ -860,7 +864,9 @@ def index():
volume_greeting=volume_greeting,
volume_button=volume_button,
volume_beep=volume_beep,
greeting_delay=greeting_delay)
greeting_delay=greeting_delay,
sort_by=sort_by,
sort_order=sort_order)
@app.route('/api/status')
def api_status():
@@ -1221,15 +1227,20 @@ def get_greetings():
})
return greetings
def get_recordings():
"""Get list of all recordings with metadata"""
def get_recordings(sort_by='date', sort_order='desc'):
"""Get list of all recordings with metadata
Args:
sort_by: Sort field - 'date', 'name', 'duration', or 'size'
sort_order: Sort order - 'asc' or 'desc'
"""
recordings = []
if os.path.exists(OUTPUT_DIR):
for filename in sorted(os.listdir(OUTPUT_DIR), reverse=True):
for filename in os.listdir(OUTPUT_DIR):
if filename.endswith('.wav'):
filepath = os.path.join(OUTPUT_DIR, filename)
stat = os.stat(filepath)
# Get duration from WAV file
try:
wf = wave.open(filepath, 'rb')
@@ -1239,14 +1250,28 @@ def get_recordings():
wf.close()
except:
duration = 0
recordings.append({
"filename": filename,
"size": stat.st_size,
"size_mb": stat.st_size / (1024 * 1024),
"date": datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
"timestamp": stat.st_mtime, # For sorting
"duration": duration
})
# Sort recordings based on parameters
reverse = (sort_order == 'desc')
if sort_by == 'name':
recordings.sort(key=lambda x: x['filename'].lower(), reverse=reverse)
elif sort_by == 'duration':
recordings.sort(key=lambda x: x['duration'], reverse=reverse)
elif sort_by == 'size':
recordings.sort(key=lambda x: x['size'], reverse=reverse)
else: # default to date
recordings.sort(key=lambda x: x['timestamp'], reverse=reverse)
return recordings
def get_local_ip():
@@ -1995,6 +2020,23 @@ def main():
</div>
{% if recordings %}
<!-- Sort Controls -->
<div style="margin-bottom: 20px; padding: 15px; background: #f9fafb; border-radius: 8px; border: 1px solid #e5e7eb;">
<div style="display: flex; align-items: center; gap: 15px; flex-wrap: wrap;">
<span style="font-weight: 600; color: #374151;">🔄 Sort by:</span>
<select id="sort-by" onchange="updateSort()" style="padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; background: white; cursor: pointer; font-size: 0.95em;">
<option value="date" {% if sort_by == 'date' %}selected{% endif %}>📅 Date</option>
<option value="name" {% if sort_by == 'name' %}selected{% endif %}>📝 Name</option>
<option value="duration" {% if sort_by == 'duration' %}selected{% endif %}>⏱️ Duration</option>
<option value="size" {% if sort_by == 'size' %}selected{% endif %}>💾 Size</option>
</select>
<select id="sort-order" onchange="updateSort()" style="padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; background: white; cursor: pointer; font-size: 0.95em;">
<option value="desc" {% if sort_order == 'desc' %}selected{% endif %}>⬇️ Descending</option>
<option value="asc" {% if sort_order == 'asc' %}selected{% endif %}>⬆️ Ascending</option>
</select>
</div>
</div>
<div class="stats">
<div class="stat-box">
<div class="stat-value">{{ recordings|length }}</div>
@@ -2242,6 +2284,12 @@ def main():
window.location.href = '/download_all';
}
function updateSort() {
const sortBy = document.getElementById('sort-by').value;
const sortOrder = document.getElementById('sort-order').value;
window.location.href = `/?sort=${sortBy}&order=${sortOrder}`;
}
function deleteRecording(filename, index) {
if (confirm('Delete this recording?')) {
fetch('/delete/' + filename, { method: 'POST' })