neue Sortierung nach mp3-tags

This commit is contained in:
2026-09-29 12:17:10 +02:00
parent 21a5fe9937
commit 68019352e2
+61 -8
View File
@@ -2,6 +2,7 @@ import os
import signal import signal
import subprocess import subprocess
import threading import threading
import json
import time import time
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
@@ -137,6 +138,51 @@ def format_duration(seconds):
return f"{minutes}:{seconds:02d}" 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): def get_duration(filename, directory=RECORDINGS_DIR):
path = directory / filename path = directory / filename
@@ -741,9 +787,10 @@ a.button:hover {
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Start</th> <th>Zeitpunkt</th>
<th>Datei</th> <th>Redner</th>
<th>Größe</th> <th>Beschreibung</th>
<th>Veranstaltung</th>
<th>Dauer</th> <th>Dauer</th>
<th>Wiedergabe</th> <th>Wiedergabe</th>
<th></th> <th></th>
@@ -757,9 +804,11 @@ a.button:hover {
<td>{{ item.datetime }}</td> <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> <td>{{ item.duration }}</td>
@@ -855,17 +904,21 @@ def render_index():
for path in FINISHED_DIR.glob("*.mp3"): for path in FINISHED_DIR.glob("*.mp3"):
try: try:
stat = path.stat() stat = path.stat()
tags = read_metadata(path)
files.append({ files.append({
"filename": path.name, "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), "size": format_size(stat.st_size),
"duration": format_duration(get_duration(path.name, FINISHED_DIR)), "duration": format_duration(get_duration(path.name, FINISHED_DIR)),
"mtime": stat.st_mtime, "sort_date": metadata_sort_key(path),
}) })
except FileNotFoundError: except FileNotFoundError:
continue continue
files.sort(key=lambda item: item["mtime"], reverse=True) files.sort(key=lambda item: item["sort_date"], reverse=True)
with state_lock: with state_lock:
process = ffmpeg_process process = ffmpeg_process