import os import logging import json from typing import Dict, List, Optional logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message:s") class PlaylistManager: """Manages playlists for the SpotSpot application.""" def __init__(self, config): """Initialize the PlaylistManager with configuration. Args: config: Configuration object containing app settings """ self.config = config self.playlists_dir = os.path.expanduser(config.playlists_dir) self._ensure_playlists_dir() def _ensure_playlists_dir(self): """Ensure the playlists directory exists.""" os.makedirs(self.playlists_dir, exist_ok=True) def get_playlist_path(self, playlist_name: str) -> str: """Get the filesystem path for a playlist. Args: playlist_name: Name of the playlist Returns: str: Full path to the playlist file """ safe_name = "".join(c if c.isalnum() else "_" for c in playlist_name) return os.path.join(self.playlists_dir, f"{safe_name}.json") def create_playlist(self, name: str, description: str = "") -> bool: """Create a new playlist. Args: name: Name of the playlist description: Optional description Returns: bool: True if created successfully, False if already exists """ playlist_path = self.get_playlist_path(name) if os.path.exists(playlist_path): return False playlist = { 'name': name, 'description': description, 'tracks': [], 'created_at': str(datetime.utcnow()), 'updated_at': str(datetime.utcnow()) } with open(playlist_path, 'w') as f: json.dump(playlist, f, indent=2) return True def add_to_playlist(self, playlist_name: str, track_info: Dict) -> bool: """Add a track to a playlist. Args: playlist_name: Name of the playlist track_info: Dictionary containing track information Returns: bool: True if added successfully, False otherwise """ try: playlist_path = self.get_playlist_path(playlist_name) if not os.path.exists(playlist_path): return False with open(playlist_path, 'r+') as f: playlist = json.load(f) # Check if track already exists if any(t.get('url') == track_info.get('url') for t in playlist['tracks']): return False playlist['tracks'].append(track_info) playlist['updated_at'] = str(datetime.utcnow()) # Write back to file f.seek(0) json.dump(playlist, f, indent=2) f.truncate() return True except Exception as e: logging.error(f"Error adding to playlist {playlist_name}: {str(e)}") return False def get_playlist(self, name: str) -> Optional[Dict]: """Get a playlist by name. Args: name: Name of the playlist Returns: Optional[Dict]: Playlist data or None if not found """ playlist_path = self.get_playlist_path(name) if not os.path.exists(playlist_path): return None try: with open(playlist_path, 'r') as f: return json.load(f) except Exception as e: logging.error(f"Error reading playlist {name}: {str(e)}") return None def list_playlists(self) -> List[Dict]: """List all available playlists. Returns: List[Dict]: List of playlist metadata """ playlists = [] for filename in os.listdir(self.playlists_dir): if filename.endswith('.json'): try: with open(os.path.join(self.playlists_dir, filename), 'r') as f: data = json.load(f) playlists.append({ 'name': data.get('name', filename), 'track_count': len(data.get('tracks', [])), 'created_at': data.get('created_at'), 'updated_at': data.get('updated_at') }) except Exception as e: logging.error(f"Error reading playlist {filename}: {str(e)}") return playlists