Files
ohm_streaming/app/routers/router_recommendations.py
T
root 9f85908ff3
CI / Test (Python 3.11) (push) Has been cancelled
CI / Test (Python 3.12) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Type Check (push) Has been cancelled
CI / Summary (push) Has been cancelled
Phase 3: HTMX & Alpine.js integration, router refactoring, and UI modernization
- Modernized the frontend with HTMX for server-driven UI and Alpine.js for client state.
- Refactored anime, player, and recommendation logic into modular routers.
- Updated README.md to reflect the latest project state and technologies (v2.4).
- Added Plyr.io for an improved streaming experience.
- Improved project structure with componentized templates.
- Added Playwright and Vitest configuration for frontend testing.
2026-03-26 10:34:26 +00:00

155 lines
4.4 KiB
Python

"""
Recommendations and releases routes for Ohm Stream Downloader API.
"""
import hashlib
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Request, Query, HTTPException
from fastapi.templating import Jinja2Templates
from app.recommendation_engine import RecommendationEngine
router = APIRouter(prefix="/api", tags=["recommendations"])
templates = Jinja2Templates(directory="templates")
# Add custom filters to Jinja2
def hash_filter(s):
return hashlib.md5(s.encode()).hexdigest()[:10]
templates.env.filters["hash"] = hash_filter
@router.get("/recommendations")
async def get_recommendations(
request: Request,
limit: int = 15,
html: bool = Query(False),
):
"""Get personalized anime recommendations based on download history"""
engine = RecommendationEngine(download_dir="downloads")
try:
recommendations = await engine.get_personalized_recommendations(limit=limit)
if html or request.headers.get("HX-Request"):
return templates.TemplateResponse(
"components/recommendations_list.html",
{"request": request, "recommendations": recommendations}
)
return {"recommendations": recommendations, "count": len(recommendations)}
except Exception as e:
import logging
logging.getLogger(__name__).error(f"Recommendations error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
finally:
await engine.close()
@router.get("/releases/latest")
async def get_latest_releases(
request: Request,
limit: int = 20,
html: bool = Query(False),
):
"""Get latest anime releases"""
from app.recommendations import get_latest_releases_with_info
try:
releases = await get_latest_releases_with_info(limit=limit)
if html or request.headers.get("HX-Request"):
return templates.TemplateResponse(
"components/releases_list.html",
{"request": request, "releases": releases}
)
return {
"releases": releases,
"count": len(releases),
"updated": datetime.now().isoformat(),
}
except Exception as e:
import logging
logging.getLogger(__name__).error(f"Latest releases error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.get("/releases/seasonal")
async def get_seasonal_anime(
year: Optional[int] = None,
season: Optional[str] = None,
):
"""Get current/previously seasonal anime"""
from app.recommendations import AnimeReleasesFetcher
fetcher = AnimeReleasesFetcher()
try:
anime = await fetcher.get_seasonal_anime(year, season)
return {
"anime": anime,
"count": len(anime),
"year": year or datetime.now().year,
"season": season or "current",
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
await fetcher.close()
@router.get("/releases/scheduled")
async def get_scheduled_anime(day: Optional[str] = None):
"""Get anime scheduled for a specific day"""
from app.recommendations import AnimeReleasesFetcher
fetcher = AnimeReleasesFetcher()
try:
anime = await fetcher.get_scheduled_anime(day)
return {"anime": anime, "count": len(anime), "day": day or "today"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
await fetcher.close()
@router.get("/releases/top")
async def get_top_anime(
type: str = "tv",
limit: int = 15,
):
"""Get top rated anime"""
from app.recommendations import AnimeReleasesFetcher
fetcher = AnimeReleasesFetcher()
try:
anime = await fetcher.get_top_anime(type=type, limit=limit)
return {"anime": anime, "count": len(anime)}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
await fetcher.close()
@router.get("/stats/downloads")
async def get_download_statistics():
"""Get download statistics and preferences"""
engine = RecommendationEngine(download_dir="downloads")
try:
stats = await engine.get_download_stats()
return stats
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
await engine.close()