Better split algorithm

This commit is contained in:
Roncero Blanco, Edgar
2025-05-26 20:17:05 +02:00
parent a701e3f97a
commit f7ee691b4d

View File

@@ -3,10 +3,10 @@ import shutil
from pathlib import Path from pathlib import Path
from mutagen.id3 import ID3, TBPM, ID3NoHeaderError from mutagen.id3 import ID3, TBPM, ID3NoHeaderError
from datetime import datetime from datetime import datetime
from collections import defaultdict
import configparser import configparser
import sys import sys
import librosa import librosa
import warnings
GROUP_SIZE = 5 GROUP_SIZE = 5
@@ -20,6 +20,7 @@ def load_config():
print("config.ini created from def_config.ini with default settings.") print("config.ini created from def_config.ini with default settings.")
else: else:
print("Error: def_config.ini not found! Please create it and rerun.") print("Error: def_config.ini not found! Please create it and rerun.")
input("Press Enter to exit...")
sys.exit(1) sys.exit(1)
default_config = configparser.ConfigParser() default_config = configparser.ConfigParser()
@@ -36,7 +37,9 @@ def load_config():
def analyze_bpm_librosa(file_path): def analyze_bpm_librosa(file_path):
try: try:
y, sr = librosa.load(file_path, mono=True) with warnings.catch_warnings():
warnings.simplefilter("ignore")
y, sr = librosa.load(file_path, mono=True)
tempo, _ = librosa.beat.beat_track(y=y, sr=sr) tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
return int(round(float(tempo))) return int(round(float(tempo)))
except Exception as e: except Exception as e:
@@ -49,7 +52,6 @@ def write_bpm_tag(file_path, bpm):
tags = ID3(file_path) tags = ID3(file_path)
except ID3NoHeaderError: except ID3NoHeaderError:
tags = ID3() tags = ID3()
tags.delall("TBPM") tags.delall("TBPM")
tags.add(TBPM(encoding=3, text=str(bpm))) tags.add(TBPM(encoding=3, text=str(bpm)))
tags.save(file_path) tags.save(file_path)
@@ -67,14 +69,11 @@ def get_bpm(file_path):
pass pass
return None return None
def split_evenly(lst, n):
k, m = divmod(len(lst), n)
return [lst[i*k + min(i, m):(i+1)*k + min(i+1, m)] for i in range(n)]
def main(): def main():
config = load_config() config = load_config()
CD_SIZE = config.getint("Settings", "SplitFolderMB") * 1024 * 1024 SAFETY_MARGIN = 1024 * 1024 * 2 # 2 MB buffer
CD_SIZE = config.getint("Settings", "SplitFolderMB") * 1024 * 1024 - SAFETY_MARGIN
bWriteNonPresentBPM = config.getboolean("Settings", "bWriteNonPresentBPM") bWriteNonPresentBPM = config.getboolean("Settings", "bWriteNonPresentBPM")
bCheckAllTracksBPM = config.getboolean("Settings", "bCheckAllTracksBPM") bCheckAllTracksBPM = config.getboolean("Settings", "bCheckAllTracksBPM")
@@ -85,6 +84,7 @@ def main():
if not source_media_path.exists() or not source_media_path.is_dir(): if not source_media_path.exists() or not source_media_path.is_dir():
print(f"Error: {source_media_path} is not a valid directory.") print(f"Error: {source_media_path} is not a valid directory.")
input("Press Enter to exit...")
sys.exit(1) sys.exit(1)
all_tracks = [] all_tracks = []
@@ -103,14 +103,12 @@ def main():
write_bpm_tag(file, bpm) write_bpm_tag(file, bpm)
size = file.stat().st_size size = file.stat().st_size
if bpm is None: bpm_range = f"{(bpm // GROUP_SIZE) * GROUP_SIZE}-to-{((bpm // GROUP_SIZE) * GROUP_SIZE) + GROUP_SIZE - 1}" if bpm else "[Unknown BPM]"
bpm_range = "[Unknown BPM]"
else:
bpm_range = f"{(bpm // GROUP_SIZE) * GROUP_SIZE}-to-{((bpm // GROUP_SIZE) * GROUP_SIZE) + GROUP_SIZE - 1}"
all_tracks.append({"file": file, "size": size, "bpm_range": bpm_range}) all_tracks.append({"file": file, "size": size, "bpm_range": bpm_range})
if not all_tracks: if not all_tracks:
print("No MP3 files found.") print("No MP3 files found.")
input("Press Enter to exit...")
sys.exit(0) sys.exit(0)
parent = source_media_path.parent parent = source_media_path.parent
@@ -118,27 +116,24 @@ def main():
dest_media_path = parent / f"[CDs-{run_date}]{folder_name}" dest_media_path = parent / f"[CDs-{run_date}]{folder_name}"
dest_media_path.mkdir(parents=True, exist_ok=True) dest_media_path.mkdir(parents=True, exist_ok=True)
total_size = sum(t["size"] for t in all_tracks) all_tracks.sort(key=lambda x: -x["size"]) # Sort by size descending for better packing
num_cds = max(1, (total_size + CD_SIZE - 1) // CD_SIZE)
print(f"Total size: {total_size / 1024**2:.2f} MB, splitting into {num_cds} CDs") cd_contents = []
current_cd = []
current_size = 0
bpm_groups = defaultdict(list)
for track in all_tracks: for track in all_tracks:
bpm_groups[track["bpm_range"]].append(track) if current_size + track["size"] > CD_SIZE:
cd_contents.append(current_cd)
current_cd = []
current_size = 0
current_cd.append(track)
current_size += track["size"]
for bpm_range in bpm_groups: if current_cd:
bpm_groups[bpm_range].sort(key=lambda x: x["file"].name) cd_contents.append(current_cd)
bpm_chunks = {} print(f"Total size: {sum(t['size'] for t in all_tracks) / 1024**2:.2f} MB, splitting into {len(cd_contents)} CDs")
for bpm_range, tracks in bpm_groups.items():
bpm_chunks[bpm_range] = split_evenly(tracks, num_cds)
cd_contents = [[] for _ in range(num_cds)]
for bpm_range in bpm_chunks:
for i, chunk in enumerate(bpm_chunks[bpm_range]):
cd_contents[i].extend(chunk)
for i, tracks in enumerate(cd_contents, start=1): for i, tracks in enumerate(cd_contents, start=1):
cd_folder = dest_media_path / f"CD-{i:02}" cd_folder = dest_media_path / f"CD-{i:02}"
@@ -152,6 +147,7 @@ def main():
print(f"[WRITE] CD-{i:02}: {len(tracks)} tracks, approx {size_accum / 1024**2:.2f} MB") print(f"[WRITE] CD-{i:02}: {len(tracks)} tracks, approx {size_accum / 1024**2:.2f} MB")
print("\n✓ Done!") print("\n✓ Done!")
input("Press Enter to exit...")
if __name__ == "__main__": if __name__ == "__main__":
main() main()