neue Sortierung nach mp3-tags
This commit is contained in:
+61
-8
@@ -2,6 +2,7 @@ import os
|
||||
import signal
|
||||
import subprocess
|
||||
import threading
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -137,6 +138,51 @@ def format_duration(seconds):
|
||||
return f"{minutes}:{seconds:02d}"
|
||||
|
||||
|
||||
def read_metadata(path):
|
||||
"""Read common ID3/FFmpeg metadata from an MP3."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["ffprobe", "-v", "error", "-show_entries",
|
||||
"format_tags=date,artist,title,album", "-of", "json", str(path)],
|
||||
capture_output=True, text=True, timeout=10,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return {}
|
||||
data = json.loads(result.stdout)
|
||||
tags = data.get("format", {}).get("tags", {}) or {}
|
||||
return {str(k).lower(): str(v) for k, v in tags.items() if v is not None}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def metadata_datetime(path):
|
||||
tags = read_metadata(path)
|
||||
value = tags.get("date", "")
|
||||
if value:
|
||||
try:
|
||||
dt = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
if dt.tzinfo is None:
|
||||
dt = dt.replace(tzinfo=TIMEZONE)
|
||||
return dt.astimezone(TIMEZONE).strftime("%d.%m.%Y %H:%M")
|
||||
except ValueError:
|
||||
pass
|
||||
return format_datetime(path.stat().st_mtime)
|
||||
|
||||
|
||||
def metadata_sort_key(path):
|
||||
tags = read_metadata(path)
|
||||
value = tags.get("date", "")
|
||||
if value:
|
||||
try:
|
||||
dt = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
if dt.tzinfo is None:
|
||||
dt = dt.replace(tzinfo=TIMEZONE)
|
||||
return dt.timestamp()
|
||||
except ValueError:
|
||||
pass
|
||||
return path.stat().st_mtime
|
||||
|
||||
|
||||
def get_duration(filename, directory=RECORDINGS_DIR):
|
||||
path = directory / filename
|
||||
|
||||
@@ -741,9 +787,10 @@ a.button:hover {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Start</th>
|
||||
<th>Datei</th>
|
||||
<th>Größe</th>
|
||||
<th>Zeitpunkt</th>
|
||||
<th>Redner</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Veranstaltung</th>
|
||||
<th>Dauer</th>
|
||||
<th>Wiedergabe</th>
|
||||
<th></th>
|
||||
@@ -757,9 +804,11 @@ a.button:hover {
|
||||
|
||||
<td>{{ item.datetime }}</td>
|
||||
|
||||
<td>{{ item.filename }}</td>
|
||||
<td>{{ item.artist or "–" }}</td>
|
||||
|
||||
<td>{{ item.size }}</td>
|
||||
<td>{{ item.title or "–" }}</td>
|
||||
|
||||
<td>{{ item.album or "–" }}</td>
|
||||
|
||||
<td>{{ item.duration }}</td>
|
||||
|
||||
@@ -855,17 +904,21 @@ def render_index():
|
||||
for path in FINISHED_DIR.glob("*.mp3"):
|
||||
try:
|
||||
stat = path.stat()
|
||||
tags = read_metadata(path)
|
||||
files.append({
|
||||
"filename": path.name,
|
||||
"datetime": format_datetime(stat.st_mtime),
|
||||
"datetime": metadata_datetime(path),
|
||||
"artist": tags.get("artist", ""),
|
||||
"title": tags.get("title", ""),
|
||||
"album": tags.get("album", ""),
|
||||
"size": format_size(stat.st_size),
|
||||
"duration": format_duration(get_duration(path.name, FINISHED_DIR)),
|
||||
"mtime": stat.st_mtime,
|
||||
"sort_date": metadata_sort_key(path),
|
||||
})
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
files.sort(key=lambda item: item["mtime"], reverse=True)
|
||||
files.sort(key=lambda item: item["sort_date"], reverse=True)
|
||||
|
||||
with state_lock:
|
||||
process = ffmpeg_process
|
||||
|
||||
Reference in New Issue
Block a user