from abc import ABC, abstractmethod from typing import List, Optional, Dict, Any from pydantic import BaseModel class TrackMetadata(BaseModel): service: str service_id: str title: str artist: str album: Optional[str] = None track_number: Optional[int] = None year: Optional[int] = None genre: Optional[str] = None duration_ms: Optional[int] = None is_explicit: bool = False cover_url: Optional[str] = None source_url: Optional[str] = None class BaseProvider(ABC): @abstractmethod def can_handle(self, url: str) -> bool: """Check if provider can handle the given URL.""" pass @abstractmethod async def resolve(self, url: str) -> List[TrackMetadata]: """Resolve URL into a list of TrackMetadata items.""" pass @abstractmethod async def download_track(self, track: TrackMetadata, dest_path: str) -> bool: """Download track to dest_path.""" pass