Files
stemify/server/prune.py
T
2026-10-29 10:28:35 -07:00

50 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""prune: list or remove stems whose song is no longer in the music library.
prune.py dry run: print orphans (song gone) and changed (song replaced)
prune.py --apply remove the orphans
prune.py --apply --changed also remove stems of replaced songs, so stemd redoes them
MUSIC_DIR and STEMS_DIR as for stemd. Refuses when the music folder holds no tracks.
Without --force it also refuses to remove more than 20% of the stems in one go.
"""
import argparse
import os
import sys
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(_ROOT, "desktop"))
sys.path.insert(0, _ROOT)
from stemify.library_core import find_stale, iter_tracks, remove_stems # noqa: E402
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--music", default=os.environ.get("MUSIC_DIR", "/music"))
ap.add_argument("--stems", default=os.environ.get("STEMS_DIR", "/stems"))
ap.add_argument("--apply", action="store_true")
ap.add_argument("--changed", action="store_true")
ap.add_argument("--force", action="store_true")
a = ap.parse_args()
total = sum(1 for _ in iter_tracks(a.music, a.stems))
if not total:
sys.exit(f"no tracks in {a.music}; is the share mounted?")
orphans, changed = find_stale(a.music, a.stems)
drop = orphans + (changed if a.changed else [])
for label, items in (("orphan", orphans), ("changed", changed)):
for p in items:
print(f"{label}\t{os.path.relpath(p, a.stems)}")
print(f"{len(orphans)} orphans, {len(changed)} changed, {total} tracks in the library")
if not a.apply or not drop:
return
if len(drop) > 0.2 * (total + len(orphans)) and not a.force:
sys.exit("that is over 20% of the stems; check the paths or pass --force")
remove_stems(a.stems, drop)
print(f"removed {len(drop)}")
if __name__ == "__main__":
main()