Regenerate M3U playlist after rename with correct order and durations

_update_m3u(): writes #EXTM3U + #EXTINF:seconds,Artist - Title + filename
per track, in disc/track order (same order as the renamed files).
Duration is read from mutagen; -1 if unavailable.

execute_album(): after renaming, finds existing *.m3u / *.m3u8 in the
album directory and overwrites it. Only triggered when files_renamed > 0
and a playlist file exists — never creates a new one from scratch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-04-29 03:13:39 +02:00
commit 888464b4d0

View file

@ -206,6 +206,33 @@ def write_tags(path: Path, proposal: TrackProposal, album_proposal: AlbumProposa
return False return False
def _update_m3u(m3u_path: Path, tracks: List[tuple]) -> bool:
"""
Schreibt M3U neu mit den umbenannten Dateien in Track-Reihenfolge.
tracks: [(TrackProposal, actual_path_after_rename), ...]
"""
try:
lines = ["#EXTM3U"]
for tp, track_path in tracks:
duration = -1
if HAS_MUTAGEN:
try:
audio = MutagenFile(str(track_path))
if audio and hasattr(audio, "info") and audio.info:
duration = int(audio.info.length)
except Exception:
pass
label = f"{tp.artist} - {tp.title}" if tp.artist else (tp.title or track_path.stem)
lines.append(f"#EXTINF:{duration},{label}")
lines.append(track_path.name)
m3u_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f" 📋 Playlist aktualisiert: {m3u_path.name}")
return True
except Exception as e:
print(f" ⚠️ M3U-Fehler {m3u_path.name}: {e}", file=sys.stderr)
return False
def execute_album( def execute_album(
proposal: AlbumProposal, proposal: AlbumProposal,
backup_dir: Optional[Path], backup_dir: Optional[Path],
@ -215,6 +242,7 @@ def execute_album(
report_data: List[Dict[str, Any]], report_data: List[Dict[str, Any]],
) -> Dict[str, int]: ) -> Dict[str, int]:
stats = {"tags_written": 0, "covers_embedded": 0, "files_renamed": 0, "errors": 0} stats = {"tags_written": 0, "covers_embedded": 0, "files_renamed": 0, "errors": 0}
final_tracks: List[tuple] = [] # (TrackProposal, final_path) für M3U
for tp in proposal.tracks: for tp in proposal.tracks:
old_title = tp.path.stem old_title = tp.path.stem
@ -263,6 +291,9 @@ def execute_album(
print(f" ⚠️ Umbenennungsfehler {tp.path.name}: {e}", file=sys.stderr) print(f" ⚠️ Umbenennungsfehler {tp.path.name}: {e}", file=sys.stderr)
stats["errors"] += 1 stats["errors"] += 1
if not dry_run:
final_tracks.append((tp, new_path))
report_data.append({ report_data.append({
"status": "dry-run" if dry_run else "ok", "status": "dry-run" if dry_run else "ok",
"album_dir": str(proposal.album_dir.name), "album_dir": str(proposal.album_dir.name),
@ -284,6 +315,15 @@ def execute_album(
"sources": ", ".join(proposal.sources), "sources": ", ".join(proposal.sources),
}) })
# M3U-Playlist aktualisieren wenn Dateien umbenannt wurden
if do_rename and not dry_run and stats["files_renamed"] > 0 and final_tracks:
m3u_files = (
list(proposal.album_dir.glob("*.m3u")) +
list(proposal.album_dir.glob("*.m3u8"))
)
if m3u_files:
_update_m3u(m3u_files[0], final_tracks)
# Nach allen Umbenennungen: Verzeichnis Linux-kompatibel bereinigen # Nach allen Umbenennungen: Verzeichnis Linux-kompatibel bereinigen
if do_rename and not dry_run: if do_rename and not dry_run:
sanitize_dir_names(proposal.album_dir) sanitize_dir_names(proposal.album_dir)