from abc import ABC, abstractmethod from typing import Optional, Tuple import httpx import re from bs4 import BeautifulSoup class BaseDownloader(ABC): """Base class for all host downloaders""" def __init__(self): self.client = httpx.AsyncClient(timeout=10.0, follow_redirects=True) @abstractmethod async def get_download_link(self, url: str) -> Tuple[str, str]: """ Extract direct download link and filename from host URL Returns: (download_url, filename) """ pass @abstractmethod def can_handle(self, url: str) -> bool: """Check if this downloader can handle the given URL""" pass async def close(self): await self.client.aclose() async def _fetch_page(self, url: str) -> str: response = await self.client.get(url) response.raise_for_status() return response.text def _extract_filename_from_headers(self, headers: dict) -> Optional[str]: content_disposition = headers.get("content-disposition", "") if "filename=" in content_disposition: filename = content_disposition.split("filename=")[-1].strip('"') return filename return None async def search_anime(self, query: str, lang: str = "vostfr") -> list[dict]: """ Search for anime on this provider Returns list of anime with title, url, and optional cover image """ return [] async def get_episodes(self, anime_url: str, lang: str = "vostfr") -> list[dict]: """ Get list of episodes for an anime Returns list of episode numbers and their URLs """ return []