Roncero Blanco, Edgar f7ee691b4d Better split algorithm
2025-05-26 20:17:05 +02:00

155 lines
5.0 KiB
Python

import os
import shutil
from pathlib import Path
from mutagen.id3 import ID3, TBPM, ID3NoHeaderError
from datetime import datetime
import configparser
import sys
import librosa
import warnings
GROUP_SIZE = 5
def load_config():
config_path = Path("config.ini")
default_config_path = Path("def_config.ini")
if not config_path.exists():
if default_config_path.exists():
shutil.copy(default_config_path, config_path)
print("config.ini created from def_config.ini with default settings.")
else:
print("Error: def_config.ini not found! Please create it and rerun.")
input("Press Enter to exit...")
sys.exit(1)
default_config = configparser.ConfigParser()
default_config.read(default_config_path)
user_config = configparser.ConfigParser()
user_config.read(config_path)
merged_config = configparser.ConfigParser()
merged_config.read_dict(default_config)
merged_config.read_dict(user_config)
return merged_config
def analyze_bpm_librosa(file_path):
try:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
y, sr = librosa.load(file_path, mono=True)
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
return int(round(float(tempo)))
except Exception as e:
print(f"Error analyzing BPM for {file_path.name}: {e}")
return None
def write_bpm_tag(file_path, bpm):
try:
try:
tags = ID3(file_path)
except ID3NoHeaderError:
tags = ID3()
tags.delall("TBPM")
tags.add(TBPM(encoding=3, text=str(bpm)))
tags.save(file_path)
print(f"Written BPM={bpm} to {file_path.name}")
except Exception as e:
print(f"Failed to write BPM tag for {file_path.name}: {e}")
def get_bpm(file_path):
try:
tags = ID3(file_path)
bpm_tag = tags.get("TBPM")
if bpm_tag:
return int(float(bpm_tag.text[0]))
except Exception:
pass
return None
def main():
config = load_config()
SAFETY_MARGIN = 1024 * 1024 * 2 # 2 MB buffer
CD_SIZE = config.getint("Settings", "SplitFolderMB") * 1024 * 1024 - SAFETY_MARGIN
bWriteNonPresentBPM = config.getboolean("Settings", "bWriteNonPresentBPM")
bCheckAllTracksBPM = config.getboolean("Settings", "bCheckAllTracksBPM")
run_date = datetime.now().strftime("%Y-%m-%d_%H%M%S")
source_input = input("Drag and drop your music folder here, then press Enter: ").strip().strip('"')
source_media_path = Path(source_input)
if not source_media_path.exists() or not source_media_path.is_dir():
print(f"Error: {source_media_path} is not a valid directory.")
input("Press Enter to exit...")
sys.exit(1)
all_tracks = []
for file in source_media_path.rglob("*.mp3"):
bpm = get_bpm(file)
if bpm is None and bWriteNonPresentBPM:
bpm = analyze_bpm_librosa(file)
if bpm is not None:
write_bpm_tag(file, bpm)
elif bpm is not None and bCheckAllTracksBPM:
new_bpm = analyze_bpm_librosa(file)
if new_bpm is not None and new_bpm != bpm:
bpm = new_bpm
write_bpm_tag(file, bpm)
size = file.stat().st_size
bpm_range = f"{(bpm // GROUP_SIZE) * GROUP_SIZE}-to-{((bpm // GROUP_SIZE) * GROUP_SIZE) + GROUP_SIZE - 1}" if bpm else "[Unknown BPM]"
all_tracks.append({"file": file, "size": size, "bpm_range": bpm_range})
if not all_tracks:
print("No MP3 files found.")
input("Press Enter to exit...")
sys.exit(0)
parent = source_media_path.parent
folder_name = source_media_path.name
dest_media_path = parent / f"[CDs-{run_date}]{folder_name}"
dest_media_path.mkdir(parents=True, exist_ok=True)
all_tracks.sort(key=lambda x: -x["size"]) # Sort by size descending for better packing
cd_contents = []
current_cd = []
current_size = 0
for track in all_tracks:
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"]
if current_cd:
cd_contents.append(current_cd)
print(f"Total size: {sum(t['size'] for t in all_tracks) / 1024**2:.2f} MB, splitting into {len(cd_contents)} CDs")
for i, tracks in enumerate(cd_contents, start=1):
cd_folder = dest_media_path / f"CD-{i:02}"
cd_folder.mkdir(parents=True, exist_ok=True)
size_accum = 0
for track in tracks:
bpm_subfolder = cd_folder / track["bpm_range"]
bpm_subfolder.mkdir(parents=True, exist_ok=True)
shutil.copy(track["file"], bpm_subfolder / track["file"].name)
size_accum += track["size"]
print(f"[WRITE] CD-{i:02}: {len(tracks)} tracks, approx {size_accum / 1024**2:.2f} MB")
print("\n✓ Done!")
input("Press Enter to exit...")
if __name__ == "__main__":
main()