import os import shutil from pathlib import Path from mutagen.id3 import ID3 from datetime import datetime from collections import defaultdict CD_SIZE = 695 * 1024 * 1024 # 695 MB GROUP_SIZE = 5 # Store the current date run_date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") # Ask for source path source_input = input("Enter the full path to your source MP3 folder: ").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.") exit(1) # Build destination path 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) 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 as e: print(f"Skipping {file_path.name} (no BPM): {e}") return None # Collect all tracks, grouping unknown BPM separately all_tracks = [] for file in source_media_path.glob("*.mp3"): bpm = get_bpm(file) size = file.stat().st_size if bpm is None: 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}) if not all_tracks: print("No MP3 files found.") exit(0) # Calculate total size and number of CDs needed total_size = sum(t["size"] for t in all_tracks) 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") # Group tracks by BPM range bpm_groups = defaultdict(list) for track in all_tracks: bpm_groups[track["bpm_range"]].append(track) # Sort each bpm group for bpm_range in bpm_groups: bpm_groups[bpm_range].sort(key=lambda x: x["file"].name) # Split each BPM group evenly into num_cds parts 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)] bpm_chunks = {} for bpm_range, tracks in bpm_groups.items(): bpm_chunks[bpm_range] = split_evenly(tracks, num_cds) # Prepare CDs cd_contents = [[] for _ in range(num_cds)] # Fill CDs with balanced BPM chunks for bpm_range in bpm_chunks: for i, chunk in enumerate(bpm_chunks[bpm_range]): cd_contents[i].extend(chunk) # Write CDs with BPM subfolders 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!")