""" Favorites management system for Ohm Stream Downloader Stores user's favorite anime with metadata using SQLModel """ import logging from typing import List, Dict, Optional from datetime import datetime from sqlmodel import Session, select from app.database import engine from app.models.favorites import FavoriteTable logger = logging.getLogger(__name__) class FavoritesManager: """Manages user's favorite anime list using SQL database""" def __init__(self, storage_path: str = None): # Database connection is managed via engine and sessions pass async def add_favorite( self, anime_id: str, title: str, url: str, provider: str, metadata: Optional[Dict] = None, poster_url: Optional[str] = None ) -> Dict: """Add an anime to favorites""" with Session(engine) as session: statement = select(FavoriteTable).where(FavoriteTable.anime_id == anime_id) existing = session.exec(statement).first() if existing: # Update existing favorite existing.updated_at = datetime.now() if metadata: existing.anime_metadata = metadata if poster_url: existing.poster_url = poster_url session.add(existing) session.commit() session.refresh(existing) return self._to_dict(existing) else: # Add new favorite fav = FavoriteTable( anime_id=anime_id, title=title, url=url, provider=provider, anime_metadata=metadata or {}, poster_url=poster_url ) session.add(fav) session.commit() session.refresh(fav) return self._to_dict(fav) async def remove_favorite(self, anime_id: str) -> bool: """Remove an anime from favorites""" with Session(engine) as session: statement = select(FavoriteTable).where(FavoriteTable.anime_id == anime_id) existing = session.exec(statement).first() if existing: session.delete(existing) session.commit() return True return False async def get_favorite(self, anime_id: str) -> Optional[Dict]: """Get a specific favorite by ID""" with Session(engine) as session: statement = select(FavoriteTable).where(FavoriteTable.anime_id == anime_id) existing = session.exec(statement).first() if existing: return self._to_dict(existing) return None async def list_favorites( self, sort_by: str = "created_at", order: str = "desc", filter_provider: Optional[str] = None, filter_genre: Optional[str] = None ) -> List[Dict]: """List all favorites with optional sorting and filtering""" with Session(engine) as session: statement = select(FavoriteTable) if filter_provider: statement = statement.where(FavoriteTable.provider == filter_provider) # SQLite JSON filtering for genres is complex, handle it in Python results = session.exec(statement).all() favorites = [self._to_dict(fav) for fav in results] if filter_genre: favorites = [ f for f in favorites if filter_genre in f.get("metadata", {}).get("genres", []) ] # Sort favorites reverse = order == "desc" if sort_by == "title": favorites.sort(key=lambda x: x["title"].lower(), reverse=reverse) elif sort_by == "rating": favorites.sort( key=lambda x: float(x.get("metadata", {}).get("rating", "0").split("/")[0]), reverse=reverse ) elif sort_by == "year": favorites.sort( key=lambda x: x.get("metadata", {}).get("release_year", 0), reverse=reverse ) else: # created_at, updated_at favorites.sort(key=lambda x: x.get(sort_by, ""), reverse=reverse) return favorites async def is_favorite(self, anime_id: str) -> bool: """Check if an anime is in favorites""" with Session(engine) as session: statement = select(FavoriteTable).where(FavoriteTable.anime_id == anime_id) return session.exec(statement).first() is not None async def toggle_favorite( self, anime_id: str, title: str, url: str, provider: str, metadata: Optional[Dict] = None, poster_url: Optional[str] = None ) -> Dict: """Toggle an anime in favorites (add if not exists, remove if exists)""" is_fav = await self.is_favorite(anime_id) if is_fav: await self.remove_favorite(anime_id) return {"action": "removed", "anime_id": anime_id} else: fav = await self.add_favorite(anime_id, title, url, provider, metadata, poster_url) return {"action": "added", "anime_id": anime_id, "favorite": fav} async def get_stats(self) -> Dict: """Get statistics about favorites""" favorites = await self.list_favorites() total = len(favorites) # Count by provider by_provider = {} for fav in favorites: provider = fav["provider"] by_provider[provider] = by_provider.get(provider, 0) + 1 # Count by genre by_genre = {} for fav in favorites: for genre in fav.get("metadata", {}).get("genres", []): by_genre[genre] = by_genre.get(genre, 0) + 1 return { "total": total, "by_provider": by_provider, "by_genre": by_genre } def _to_dict(self, fav: FavoriteTable) -> Dict: """Convert a FavoriteTable instance to a dictionary for API compatibility""" return { "id": fav.anime_id, "title": fav.title, "url": fav.url, "provider": fav.provider, "metadata": fav.anime_metadata, "poster_url": fav.poster_url, "created_at": fav.created_at.isoformat() if fav.created_at else None, "updated_at": fav.updated_at.isoformat() if fav.updated_at else None } # Global favorites manager instance _favorites_manager: Optional[FavoritesManager] = None def get_favorites_manager() -> FavoritesManager: """Get the global favorites manager instance""" global _favorites_manager if _favorites_manager is None: _favorites_manager = FavoritesManager() return _favorites_manager