music-library-tree.py
1 #!/usr/bin/env python3 2 """ 3 Generate a tree view of the music library with track durations 4 Usage: python3 music-library-tree.py [music-directory] [output-file] 5 6 Requires: mutagen (install with: pip3 install mutagen) 7 If mutagen not available, falls back to showing file info without duration 8 """ 9 10 import os 11 import sys 12 from pathlib import Path 13 from datetime import datetime 14 15 # Try to import mutagen for audio metadata 16 try: 17 from mutagen import File as MutagenFile 18 MUTAGEN_AVAILABLE = True 19 except ImportError: 20 MUTAGEN_AVAILABLE = False 21 print("Warning: mutagen not installed. Duration info will not be available.") 22 print("Install with: pip3 install mutagen") 23 24 AUDIO_EXTENSIONS = {'.mp3', '.flac', '.ogg', '.m4a', '.wav', '.aac', '.opus', '.wma'} 25 26 def get_duration(file_path): 27 """Get duration of audio file using mutagen""" 28 if not MUTAGEN_AVAILABLE: 29 return "--:--" 30 31 try: 32 audio = MutagenFile(str(file_path)) 33 if audio is not None and hasattr(audio.info, 'length'): 34 duration_sec = int(audio.info.length) 35 minutes = duration_sec // 60 36 seconds = duration_sec % 60 37 return f"{minutes:02d}:{seconds:02d}" 38 except Exception: 39 pass 40 return "--:--" 41 42 def format_size(size): 43 """Format file size in human-readable format""" 44 for unit in ['B', 'KB', 'MB', 'GB']: 45 if size < 1024.0: 46 return f"{size:.2f} {unit}" 47 size /= 1024.0 48 return f"{size:.2f} TB" 49 50 def build_tree(directory, output_file, prefix="", is_last=True): 51 """Recursively build tree structure""" 52 try: 53 entries = sorted(Path(directory).iterdir(), key=lambda x: (not x.is_dir(), x.name.lower())) 54 except PermissionError: 55 return 56 57 for i, entry in enumerate(entries): 58 is_last_entry = (i == len(entries) - 1) 59 connector = "└── " if is_last_entry else "├── " 60 61 if entry.is_dir(): 62 output_file.write(f"{prefix}{connector}📁 {entry.name}/\n") 63 extension = " " if is_last_entry else "│ " 64 build_tree(entry, output_file, prefix + extension, is_last_entry) 65 else: 66 ext = entry.suffix.lower() 67 if ext in AUDIO_EXTENSIONS: 68 duration = get_duration(entry) 69 size = entry.stat().st_size 70 size_fmt = format_size(size) 71 output_file.write(f"{prefix}{connector}🎵 {entry.name} [{duration}] ({size_fmt})\n") 72 else: 73 output_file.write(f"{prefix}{connector}📄 {entry.name}\n") 74 75 def main(): 76 music_dir = sys.argv[1] if len(sys.argv) > 1 else "/home/glenneth/Music" 77 output_path = sys.argv[2] if len(sys.argv) > 2 else "music-library-tree.txt" 78 79 music_path = Path(music_dir) 80 if not music_path.exists(): 81 print(f"Error: Music directory '{music_dir}' does not exist") 82 sys.exit(1) 83 84 print("Generating music library tree...") 85 86 # Count audio files 87 audio_files = [] 88 for ext in AUDIO_EXTENSIONS: 89 audio_files.extend(music_path.rglob(f"*{ext}")) 90 total_audio = len(audio_files) 91 92 # Generate tree 93 with open(output_path, 'w', encoding='utf-8') as f: 94 f.write("Music Library Tree\n") 95 f.write("==================\n") 96 f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") 97 f.write(f"Directory: {music_dir}\n") 98 f.write(f"Mutagen available: {'Yes' if MUTAGEN_AVAILABLE else 'No (install with: pip3 install mutagen)'}\n") 99 f.write(f"\nTotal audio files: {total_audio}\n\n") 100 f.write(f"📁 {music_path.name}/\n") 101 build_tree(music_path, f, "", True) 102 103 print(f"\nTree generated successfully!") 104 print(f"Output saved to: {output_path}") 105 print(f"Total audio files: {total_audio}") 106 107 if __name__ == "__main__": 108 main()