neue Sortierung nach mp3-tags
This commit is contained in:
+61
-8
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user