127 lines
3.7 KiB
Python
127 lines
3.7 KiB
Python
from uuid import UUID
|
|
|
|
from arq import create_pool
|
|
from arq.connections import RedisSettings
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from fastapi.responses import FileResponse
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.config import settings
|
|
from app.database import get_db
|
|
from app.models import Clip, RenderRequest
|
|
from app.schemas import RenderCreate, RenderResponse, BulkRenderCreate
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _redis_settings() -> RedisSettings:
|
|
from urllib.parse import urlparse
|
|
parsed = urlparse(settings.redis_url)
|
|
return RedisSettings(
|
|
host=parsed.hostname or "redis",
|
|
port=parsed.port or 6379,
|
|
database=int(parsed.path.lstrip("/") or "0"),
|
|
)
|
|
|
|
|
|
@router.post("/clips/{clip_id}/render", response_model=RenderResponse, status_code=201)
|
|
async def render_clip(
|
|
clip_id: UUID,
|
|
render_in: RenderCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
clip = await db.get(Clip, clip_id)
|
|
if not clip:
|
|
raise HTTPException(404, "Clip not found")
|
|
if not clip.raw_clip_path:
|
|
raise HTTPException(400, "Clip not yet extracted")
|
|
|
|
render = RenderRequest(
|
|
clip_id=clip_id,
|
|
aspect_ratio=render_in.aspect_ratio,
|
|
subtitle_style=render_in.subtitle_style,
|
|
status="pending",
|
|
)
|
|
db.add(render)
|
|
await db.commit()
|
|
await db.refresh(render)
|
|
|
|
pool = await create_pool(_redis_settings())
|
|
await pool.enqueue_job("render_clip", str(render.id))
|
|
await pool.close()
|
|
|
|
return render
|
|
|
|
|
|
@router.post("/jobs/{job_id}/render-all", response_model=list[RenderResponse], status_code=201)
|
|
async def render_all_clips(
|
|
job_id: UUID,
|
|
bulk_in: BulkRenderCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
renders = []
|
|
pool = await create_pool(_redis_settings())
|
|
|
|
for clip_id in bulk_in.clip_ids:
|
|
clip = await db.get(Clip, clip_id)
|
|
if not clip or not clip.raw_clip_path:
|
|
continue
|
|
|
|
render = RenderRequest(
|
|
clip_id=clip_id,
|
|
aspect_ratio=bulk_in.aspect_ratio,
|
|
subtitle_style=bulk_in.subtitle_style,
|
|
status="pending",
|
|
)
|
|
db.add(render)
|
|
await db.commit()
|
|
await db.refresh(render)
|
|
|
|
await pool.enqueue_job("render_clip", str(render.id))
|
|
renders.append(render)
|
|
|
|
await pool.close()
|
|
return renders
|
|
|
|
|
|
@router.get("/renders/{render_id}", response_model=RenderResponse)
|
|
async def get_render(render_id: UUID, db: AsyncSession = Depends(get_db)):
|
|
render = await db.get(RenderRequest, render_id)
|
|
if not render:
|
|
raise HTTPException(404, "Render not found")
|
|
return render
|
|
|
|
|
|
@router.get("/renders/{render_id}/download")
|
|
async def download_render(render_id: UUID, db: AsyncSession = Depends(get_db)):
|
|
render = await db.get(RenderRequest, render_id)
|
|
if not render:
|
|
raise HTTPException(404, "Render not found")
|
|
if render.status != "complete" or not render.output_path:
|
|
raise HTTPException(400, "Render not complete")
|
|
|
|
clip = await db.get(Clip, render.clip_id)
|
|
filename = f"{clip.title}_{render.aspect_ratio.replace(':', 'x')}.mp4" if clip else "clip.mp4"
|
|
|
|
return FileResponse(
|
|
render.output_path,
|
|
media_type="video/mp4",
|
|
filename=filename,
|
|
)
|
|
|
|
|
|
@router.get("/renders/{render_id}/preview")
|
|
async def preview_render(render_id: UUID, db: AsyncSession = Depends(get_db)):
|
|
render = await db.get(RenderRequest, render_id)
|
|
if not render:
|
|
raise HTTPException(404, "Render not found")
|
|
if render.status != "complete" or not render.output_path:
|
|
raise HTTPException(400, "Render not complete")
|
|
|
|
return FileResponse(
|
|
render.output_path,
|
|
media_type="video/mp4",
|
|
content_disposition_type="inline",
|
|
)
|