import hashlib import os from pathlib import Path import sqlite3 import sys import tempfile root = Path.home() / 'Library/Application Support/KJamsClone/DJ' media = root / 'Media' def digest(path): h = hashlib.sha256() with path.open('rb') as stream: for chunk in iter(lambda: stream.read(1024 * 1024), b''): h.update(chunk) return h.hexdigest() with sqlite3.connect(f'file:{root / "Library.sqlite"}?mode=ro', uri=True) as db: originals = [Path(row[0]) for row in db.execute('select original_path from songs') if row[0]] # Include the failed, unregistered source only within the same explicitly imported folder. folders = {p.parent for p in originals} sources = set(originals) for folder in folders: sources.update(p for p in folder.iterdir() if p.is_file() and p.suffix.lower() == '.mp3') repairs = [] for source in sorted(sources): if not source.is_file() or source.stat().st_size == 0: continue expected = digest(source) target = media / (expected + source.suffix.lower()) if target.is_file() and target.stat().st_size == 0: repairs.append((source, target, expected)) print(f'Verified {len(repairs)} empty DJ copies with matching original audio.') if '--apply' in sys.argv: for source, target, expected in repairs: descriptor, temporary = tempfile.mkstemp(prefix='.kjmix-repair-', dir=media) try: with os.fdopen(descriptor, 'wb') as output, source.open('rb') as input_stream: for chunk in iter(lambda: input_stream.read(1024 * 1024), b''): output.write(chunk) output.flush() os.fsync(output.fileno()) assert digest(Path(temporary)) == expected assert target.stat().st_size == 0, 'Destination changed; refusing to replace it' os.chmod(temporary, 0o644) os.replace(temporary, target) print(f'Repaired verified audio: {target.name} ({target.stat().st_size} bytes)') finally: if os.path.exists(temporary): os.unlink(temporary)