Files
2026-04-14 17:14:32 +02:00

221 lines
7.4 KiB
Python

"""
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"]),
}