- Bandcamp: match artist+song, fall back to artist-only page - YouTube fallback: if not on Bandcamp, link to YouTube music video search - Fix My Playlist: AI analyzes playlist, finds outliers, suggests replacements - User chooses recommendation count (5, 10, 15, 20)
95 lines
3.2 KiB
Python
95 lines
3.2 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.database import get_db
|
|
from app.core.security import get_current_user
|
|
from app.models.user import User
|
|
from app.models.recommendation import Recommendation
|
|
from app.schemas.recommendation import RecommendationRequest, RecommendationResponse, RecommendationItem
|
|
from app.services.recommender import generate_recommendations
|
|
|
|
router = APIRouter(prefix="/recommendations", tags=["recommendations"])
|
|
|
|
|
|
@router.post("/generate", response_model=RecommendationResponse)
|
|
async def generate(
|
|
data: RecommendationRequest,
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
if not data.playlist_id and not data.query:
|
|
raise HTTPException(status_code=400, detail="Provide a playlist_id or query")
|
|
|
|
recs, remaining = await generate_recommendations(
|
|
db, user, playlist_id=data.playlist_id, query=data.query, bandcamp_mode=data.bandcamp_mode,
|
|
mode=data.mode, adventurousness=data.adventurousness, exclude=data.exclude, count=data.count,
|
|
)
|
|
|
|
if not recs and remaining == 0:
|
|
raise HTTPException(status_code=429, detail="Weekly recommendation limit reached. Upgrade to Premium for unlimited.")
|
|
|
|
return RecommendationResponse(
|
|
recommendations=[RecommendationItem.model_validate(r) for r in recs],
|
|
remaining_this_week=remaining,
|
|
)
|
|
|
|
|
|
@router.get("/history", response_model=list[RecommendationItem])
|
|
async def history(
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
result = await db.execute(
|
|
select(Recommendation)
|
|
.where(Recommendation.user_id == user.id)
|
|
.order_by(Recommendation.created_at.desc())
|
|
.limit(50)
|
|
)
|
|
return result.scalars().all()
|
|
|
|
|
|
@router.get("/saved", response_model=list[RecommendationItem])
|
|
async def saved(
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
result = await db.execute(
|
|
select(Recommendation)
|
|
.where(Recommendation.user_id == user.id, Recommendation.saved == True)
|
|
.order_by(Recommendation.created_at.desc())
|
|
)
|
|
return result.scalars().all()
|
|
|
|
|
|
@router.post("/{rec_id}/save")
|
|
async def save_recommendation(
|
|
rec_id: int,
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
result = await db.execute(
|
|
select(Recommendation).where(Recommendation.id == rec_id, Recommendation.user_id == user.id)
|
|
)
|
|
rec = result.scalar_one_or_none()
|
|
if not rec:
|
|
raise HTTPException(status_code=404, detail="Recommendation not found")
|
|
rec.saved = not rec.saved
|
|
return {"saved": rec.saved}
|
|
|
|
|
|
@router.post("/{rec_id}/dislike")
|
|
async def dislike_recommendation(
|
|
rec_id: int,
|
|
user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
result = await db.execute(
|
|
select(Recommendation).where(Recommendation.id == rec_id, Recommendation.user_id == user.id)
|
|
)
|
|
rec = result.scalar_one_or_none()
|
|
if not rec:
|
|
raise HTTPException(status_code=404, detail="Recommendation not found")
|
|
rec.disliked = not rec.disliked
|
|
return {"disliked": rec.disliked}
|