Prune stems whose source song was removed or replaced

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Sonnet 5.5 committed 2026-10-29 10:28:35 -07:00
1 parent 5b7fadb7f7
commit 0fc35b13e8
7 files changed
+133 -3

No files matched your search

+47
View File
@@ -12,6 +12,7 @@ import os
import shutil
import subprocess
import time
import unicodedata
import numpy as np
import soundfile as sf
@@ -105,3 +106,49 @@ def write_track(dest, stems, meta):
for name in [s["file"] for s in written] + [MARKER]:
os.replace(os.path.join(part, name), os.path.join(dest, name))
os.rmdir(part)
def _norm(path):
return unicodedata.normalize("NFC", os.path.normpath(path)).casefold()
def find_stale(src, dst):
"""Stems folders that no longer match the library.
Returns (orphans, changed): orphans have no source track any more (removed, renamed or
moved); changed still have one, but its size or mtime differs from the marker's, so the
stems are of an older version of the song.
"""
expected = {_norm(d): p for p, d in iter_tracks(src, dst)}
orphans, changed = [], []
for root, dirs, files in os.walk(dst):
dirs[:] = [d for d in dirs if not d.startswith(".")]
if MARKER not in files:
continue
dirs[:] = []
source = expected.get(_norm(root))
if source is None:
orphans.append(root)
continue
try:
with open(os.path.join(root, MARKER)) as f:
m = json.load(f)
if [m["size"], m["mtime_ns"]] != signature(source):
changed.append(root)
except (OSError, ValueError, KeyError):
pass # unreadable marker or source: leave it alone
return sorted(orphans), sorted(changed)
def remove_stems(dst, folders):
"""Delete stems folders, then any parent folders left empty (never dst itself)."""
dst = os.path.realpath(dst)
for folder in folders:
shutil.rmtree(folder)
parent = os.path.dirname(os.path.realpath(folder))
while parent != dst and parent.startswith(dst + os.sep):
try:
os.rmdir(parent)
except OSError:
break
parent = os.path.dirname(parent)