Files
youtube-app/backend/api/video_controller.py
Marek Lenczewski ca988345e9 update
2026-04-07 17:55:30 +02:00

122 lines
4.1 KiB
Python

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"}