/ scripts / music-library-tree.py
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()