#!/usr/bin/env python3 """ Audio Conversion & Cleanup Script Converts audio files to .ogg, removes placeholders, verifies integrity """ import os import subprocess from pathlib import Path REPO_ROOT = Path("/Users/davidkotnik/repos/novafarma") AUDIO_ROOT = REPO_ROOT / "assets" / "audio" def remove_txt_placeholders(): """Remove all .txt placeholder files""" print("\nšŸ—‘ļø Removing .txt placeholders...") txt_files = list(AUDIO_ROOT.rglob("*.txt")) for txt_file in txt_files: txt_file.unlink() print(f" āœ… Removed: {txt_file.name}") print(f"\nāœ… Removed {len(txt_files)} placeholder files") def convert_to_ogg(): """Convert all MP3/WAV to OGG using ffmpeg""" print("\nšŸ”„ Converting audio files to .ogg...") # Find all MP3 and WAV files audio_files = [] audio_files.extend(AUDIO_ROOT.rglob("*.mp3")) audio_files.extend(AUDIO_ROOT.rglob("*.wav")) converted = 0 for audio_file in audio_files: ogg_file = audio_file.with_suffix(".ogg") # Skip if .ogg already exists if ogg_file.exists(): print(f" ā­ļø Skip: {ogg_file.name} (exists)") continue # Convert using ffmpeg try: cmd = [ "ffmpeg", "-i", str(audio_file), "-c:a", "libvorbis", "-q:a", "6", # Quality 6 (good balance) str(ogg_file) ] result = subprocess.run( cmd, capture_output=True, text=True ) if result.returncode == 0: print(f" āœ… Converted: {audio_file.name} → {ogg_file.name}") converted += 1 else: print(f" āŒ Failed: {audio_file.name}") print(f" Error: {result.stderr[:100]}") except FileNotFoundError: print("\nāŒ ERROR: ffmpeg not found!") print("Install with: brew install ffmpeg") return False print(f"\nāœ… Converted {converted} files to .ogg") return True def verify_audio(): """Verify all audio files exist and are valid""" print("\nšŸ” Verifying audio files...") # Expected files from manifest expected = { "sfx/farming": ["cow_moo", "dig", "harvest", "plant_seed", "scythe_swing", "stone_mine", "tree_chop", "water_crop"], "sfx/combat": ["bow_release", "explosion", "player_hurt", "raider_attack", "shield_block", "sword_slash", "zombie_death", "zombie_hit"], "sfx/building": ["chest_open", "door_close", "door_open", "hammer_nail", "repair"], "sfx/misc": ["coin_collect", "footstep_grass", "footstep_stone", "level_up"], "music": ["main_theme", "farm_ambient", "town_theme", "combat_theme", "night_theme", "victory_theme", "raid_warning", "ana_theme", "forest_ambient"], } missing = [] found = [] for category, files in expected.items(): category_path = AUDIO_ROOT / category for filename in files: # Check both .ogg and original formats ogg_file = category_path / f"{filename}.ogg" mp3_file = category_path / f"{filename}.mp3" wav_file = category_path / f"{filename}.wav" if ogg_file.exists(): size = ogg_file.stat().st_size if size > 1000: # At least 1KB found.append(str(ogg_file.relative_to(AUDIO_ROOT))) else: missing.append(f"{category}/{filename}.ogg (too small: {size}B)") elif mp3_file.exists() or wav_file.exists(): found.append(f"{category}/{filename} (needs conversion)") else: missing.append(f"{category}/{filename}.ogg") print(f"\nāœ… Found: {len(found)} files") print(f"āŒ Missing: {len(missing)} files") if missing: print("\nāŒ MISSING FILES:") for file in missing[:10]: # Show first 10 print(f" - {file}") if len(missing) > 10: print(f" ... and {len(missing) - 10} more") return len(missing) == 0 def generate_manifest(): """Generate audio file manifest for Phaser preload""" print("\nšŸ“ Generating audio manifest...") manifest = { "sfx": {}, "music": {}, "voices": {} } # Scan SFX sfx_path = AUDIO_ROOT / "sfx" for ogg_file in sfx_path.rglob("*.ogg"): category = ogg_file.parent.name filename = ogg_file.stem if category not in manifest["sfx"]: manifest["sfx"][category] = [] manifest["sfx"][category].append({ "key": f"{category}_{filename}", "path": str(ogg_file.relative_to(AUDIO_ROOT.parent)) }) # Scan Music music_path = AUDIO_ROOT / "music" for audio_file in music_path.glob("*"): if audio_file.suffix in [".ogg", ".mp3"]: manifest["music"][audio_file.stem] = { "key": audio_file.stem, "path": str(audio_file.relative_to(AUDIO_ROOT.parent)) } # Scan Voices voices_path = AUDIO_ROOT / "voices" for char_dir in voices_path.iterdir(): if char_dir.is_dir(): manifest["voices"][char_dir.name] = [] for mp3_file in char_dir.glob("*.mp3"): manifest["voices"][char_dir.name].append({ "key": f"{char_dir.name}_{mp3_file.stem}", "path": str(mp3_file.relative_to(AUDIO_ROOT.parent)) }) # Write manifest manifest_file = REPO_ROOT / "src" / "data" / "audioManifest.json" import json manifest_file.parent.mkdir(parents=True, exist_ok=True) with open(manifest_file, 'w') as f: json.dump(manifest, f, indent=2) print(f"āœ… Manifest written to: {manifest_file}") print(f" - SFX categories: {len(manifest['sfx'])}") print(f" - Music tracks: {len(manifest['music'])}") print(f" - Voice characters: {len(manifest['voices'])}") def main(): """Run all audio processing tasks""" print("="*60) print("šŸŽµ AUDIO CONVERSION & CLEANUP") print("="*60) # Step 1: Remove placeholders remove_txt_placeholders() # Step 2: Convert to OGG if not convert_to_ogg(): return # Step 3: Verify verify_audio() # Step 4: Generate manifest generate_manifest() print("\n" + "="*60) print("āœ… AUDIO PROCESSING COMPLETE!") print("="*60) if __name__ == "__main__": main()