import threading from pathlib import Path from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import FileResponse, StreamingResponse from sqlalchemy.orm import Session from api.schemas import CleanupRequest, VideoCreate, VideoResponse from database.database import SessionLocal, getDb from download.download_service import downloadVideo from model.video import Video from notify.notify_clients import notifyClients from stream.stream_service import streamVideoLive router = APIRouter(prefix="/videos", tags=["videos"]) @router.post("", status_code=204) async def create(videoData: VideoCreate, db: Session = Depends(getDb)): title = videoData.title youtuber = videoData.youtuber thumbnailUrl = videoData.thumbnailUrl youtubeUrl = videoData.youtubeUrl profileId = videoData.profileId Video.deleteIfExists(db, youtubeUrl, profileId) Video.create(db, title, youtuber, thumbnailUrl, youtubeUrl, profileId) await notifyClients(profileId) @router.get("", response_model=list[VideoResponse]) def getAll( profileId: Optional[int] = Query(None), db: Session = Depends(getDb), ): videos = Video.getAll(db, profileId=profileId) return [VideoResponse.fromModel(v) for v in videos] @router.get("/downloaded", response_model=list[VideoResponse]) def getDownloaded( profileId: Optional[int] = Query(None), db: Session = Depends(getDb), ): videos = Video.getDownloaded(db, profileId=profileId) return [VideoResponse.fromModel(v) for v in videos] @router.post("/cleanup") def cleanup(request: CleanupRequest, db: Session = Depends(getDb)): count = Video.deleteNotDownloaded(db, request.profileId, request.excludeIds or None) return {"deleted": count} @router.post("/{videoId}/download") def download(videoId: int, db: Session = Depends(getDb)): video = Video.getById(db, videoId) if not video: raise HTTPException(status_code=404, detail="Video nicht gefunden") if video.filePath: return {"status": "already_downloaded"} thread = threading.Thread(target=downloadVideo, args=(video.id, video.youtubeUrl)) thread.start() return {"status": "download_started"} @router.get("/{videoId}/stream") def stream(videoId: int, db: Session = Depends(getDb)): video = Video.getById(db, videoId) if not video: raise HTTPException(status_code=404, detail="Video nicht gefunden") if not video.filePath: def streamAndSave(): outputPath = f"/videos/{videoId}.mp4" yield from streamVideoLive(videoId, video.youtubeUrl) if Path(outputPath).exists(): sdb = SessionLocal() try: Video.updateFilePath(sdb, videoId, outputPath) finally: sdb.close() return StreamingResponse(streamAndSave(), media_type="video/mp4") path = Path(video.filePath) if not path.exists(): raise HTTPException(status_code=404, detail="Videodatei nicht gefunden") return FileResponse(path, media_type="video/mp4") @router.get("/{videoId}/file") def getFile(videoId: int, db: Session = Depends(getDb)): video = Video.getById(db, videoId) if not video: raise HTTPException(status_code=404, detail="Video nicht gefunden") if not video.filePath: raise HTTPException(status_code=404, detail="Video noch nicht heruntergeladen") path = Path(video.filePath) if not path.exists(): Video.updateFilePath(db, videoId, None) raise HTTPException(status_code=404, detail="Video noch nicht heruntergeladen") return FileResponse(path, media_type="video/mp4", filename=f"{video.title}.mp4") @router.delete("/{videoId}/file") def deleteFile(videoId: int, db: Session = Depends(getDb)): video = Video.getById(db, videoId) if not video: raise HTTPException(status_code=404, detail="Video nicht gefunden") if video.filePath: path = Path(video.filePath) if path.exists(): path.unlink() Video.updateFilePath(db, videoId, None) return {"status": "deleted"}