""" sharing.py — Forenklet deling af playlister. Kun ejeren kan redigere. Delte brugere får read-only via sync. """ from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks from sqlalchemy.orm import Session from pydantic import BaseModel, EmailStr from app.core.database import get_db from app.core.security import get_current_user from app.models import User, Project, PlaylistShare router = APIRouter(prefix="/sharing", tags=["sharing"]) class ShareRequest(BaseModel): email: EmailStr # ── Del med bruger ──────────────────────────────────────────────────────────── @router.post("/playlists/{project_id}/share", status_code=201) async def share_playlist( project_id: str, data: ShareRequest, background_tasks: BackgroundTasks, db: Session = Depends(get_db), me: User = Depends(get_current_user), ): """Del en playliste med en bruger — de får listen ved næste sync.""" project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first() if not project: raise HTTPException(404, "Playliste ikke fundet eller du er ikke ejer") target = db.query(User).filter_by(email=data.email).first() existing = db.query(PlaylistShare).filter_by( project_id=project_id, invited_email=data.email ).first() if existing: return {"detail": "Allerede delt med denne bruger"} share = PlaylistShare( project_id=project_id, shared_with_id=target.id if target else None, invited_email=data.email, permission="view", ) db.add(share) db.commit() # Send invitation-mail try: from app.core.mail import send_share_invitation from app.core.config import settings background_tasks.add_task( send_share_invitation, email=data.email, owner_name=me.username, playlist_name=project.name, permission="view", accept_url=f"{settings.BASE_URL}/sharing/playlists/{project_id}", ) except Exception: pass return {"detail": f"Delt med {data.email}"} @router.delete("/playlists/{project_id}/share/{share_id}", status_code=204) def remove_share( project_id: str, share_id: str, db: Session = Depends(get_db), me: User = Depends(get_current_user), ): project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first() if not project: raise HTTPException(404, "Playliste ikke fundet") share = db.query(PlaylistShare).filter_by(id=share_id, project_id=project_id).first() if not share: raise HTTPException(404, "Deling ikke fundet") db.delete(share) db.commit() @router.get("/playlists/{project_id}/shares") def list_shares( project_id: str, db: Session = Depends(get_db), me: User = Depends(get_current_user), ): project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first() if not project: raise HTTPException(404, "Playliste ikke fundet") shares = db.query(PlaylistShare).filter_by(project_id=project_id).all() return [{"id": s.id, "email": s.invited_email} for s in shares] # ── Visibility ──────────────────────────────────────────────────────────────── @router.patch("/playlists/{project_id}/visibility") def set_visibility( project_id: str, visibility: str, db: Session = Depends(get_db), me: User = Depends(get_current_user), ): if visibility not in ("private", "shared", "public"): raise HTTPException(400, "Brug private, shared eller public") project = db.query(Project).filter_by(id=project_id, owner_id=me.id).first() if not project: raise HTTPException(404, "Playliste ikke fundet") project.visibility = visibility db.commit() return {"detail": f"Synlighed: {visibility}"} # ── Hent playliste-indhold ──────────────────────────────────────────────────── @router.get("/public") def list_public_playlists(db: Session = Depends(get_db)): """Hent alle public playlister — ingen login krævet.""" projects = db.query(Project).filter_by(visibility="public").all() result = [] for p in projects: owner = db.query(User).filter_by(id=p.owner_id).first() result.append({ "id": p.id, "name": p.name, "owner": owner.username if owner else "?", "song_count": len(p.project_songs), }) return result @router.post("/playlists/{project_id}/copy", status_code=201) def copy_playlist( project_id: str, db: Session = Depends(get_db), me: User = Depends(get_current_user), ): """Kopiér en public playliste til brugerens egen konto.""" p = db.query(Project).filter_by(id=project_id).first() if not p: raise HTTPException(404, "Playliste ikke fundet") if p.visibility != "public": raise HTTPException(403, "Kun public playlister kan kopieres") if p.owner_id == me.id: raise HTTPException(400, "Du ejer allerede denne playliste") from app.models import Song owner = db.query(User).filter_by(id=p.owner_id).first() new_name = f"{p.name} (kopi fra {owner.username if owner else '?'})" new_p = Project( owner_id=me.id, name=new_name, description=p.description or "", visibility="private", ) db.add(new_p) db.flush() for ps in p.project_songs: from app.models import ProjectSong db.add(ProjectSong( project_id=new_p.id, song_id=ps.song_id, position=ps.position, status="pending", is_workshop=ps.is_workshop, dance_override=ps.dance_override or "", )) db.commit() return {"detail": "Kopieret", "id": new_p.id} @router.get("/playlists/{project_id}") def get_shared_playlist( project_id: str, db: Session = Depends(get_db), me: User = Depends(get_current_user), ): p = db.query(Project).filter_by(id=project_id).first() if not p: raise HTTPException(404, "Playliste ikke fundet") if p.owner_id != me.id: if p.visibility != "public": share = db.query(PlaylistShare).filter( PlaylistShare.project_id == project_id, (PlaylistShare.shared_with_id == me.id) | (PlaylistShare.invited_email == me.email) ).first() if not share: raise HTTPException(403, "Ingen adgang") from app.models import Song songs = [] for ps in p.project_songs: song = db.query(Song).filter_by(id=ps.song_id).first() if not song: continue songs.append({ "title": song.title, "artist": song.artist, "position": ps.position, "status": ps.status, "is_workshop": ps.is_workshop, "dance_override": ps.dance_override or "", }) return { "id": p.id, "name": p.name, "description": p.description or "", "visibility": p.visibility, "songs": sorted(songs, key=lambda x: x["position"]), }