File size: 3,262 Bytes
070c931
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
"""Custom video styles: writing guidance for the script, used on videos as custom:<id>.

Styles are made on our one blog2video account, so b2v_styles records which workspace made each; only that
workspace sees, uses, edits or deletes it. Built-in styles are read-only here, and "Your Style" (which learns from
every workspace's edits) is never offered. The account-wide style selection, pin and built-in overrides are never
touched (blog2video.DENIED).
"""

from typing import Literal

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field

from app.auth import Ctx, get_ctx
from app.models import B2VStyle
from app.routers.videos import b2v_ready, upstream
from app.services import b2v_access
from app.services import blog2video as b2v

router = APIRouter(prefix="/api/video-styles", tags=["videos"])


class DraftIn(BaseModel):
    prompt: str = Field(min_length=20, max_length=1000)


class StyleIn(BaseModel):
    name: str = Field(min_length=1, max_length=80)
    guidance: str = Field(min_length=1, max_length=2000)
    creation_method: Literal["manual", "ai"] = "manual"
    source_prompt: str | None = Field(None, max_length=1000)


class StylePatchIn(BaseModel):
    name: str = Field(min_length=1, max_length=80)
    guidance: str = Field(min_length=1, max_length=2000)
    version: int = Field(ge=0)


@router.get("")
def list_styles(ctx: Ctx = Depends(get_ctx), _: None = Depends(b2v_ready)):
    with upstream():
        return b2v_access.visible_styles(ctx)


@router.post("/ai-draft")
def ai_draft(body: DraftIn, ctx: Ctx = Depends(get_ctx), _: None = Depends(b2v_ready)):
    """A name and guidance written from a description; nothing is saved."""
    with upstream():
        return b2v.request("POST", "/api/video-styles/ai-draft", json=body.model_dump(), timeout=90)


def _out(s: dict) -> dict:
    return {"id": s.get("id"), "custom_id": s.get("custom_id"), "name": s.get("name"), "guidance": s.get("guidance"),
            "description": s.get("description"), "version": s.get("version"), "kind": "custom"}


@router.post("", status_code=201)
def create_style(body: StyleIn, ctx: Ctx = Depends(get_ctx), _: None = Depends(b2v_ready)):
    with upstream():
        made = b2v.request("POST", "/api/video-styles/custom", json=body.model_dump())
    ctx.db.add(B2VStyle(b2v_style_id=int(made["custom_id"]), workspace_id=ctx.workspace.id, name=body.name))
    ctx.db.commit()
    return _out(made)


@router.patch("/{style_id}")
def update_style(style_id: int, body: StylePatchIn, ctx: Ctx = Depends(get_ctx), _: None = Depends(b2v_ready)):
    row = b2v_access.owned_style(ctx, style_id)
    with upstream():
        made = b2v.request("PATCH", f"/api/video-styles/custom/{style_id}", json=body.model_dump())
    row.name = body.name
    ctx.db.commit()
    return _out(made)


@router.delete("/{style_id}")
def delete_style(style_id: int, ctx: Ctx = Depends(get_ctx), _: None = Depends(b2v_ready)):
    row = b2v_access.owned_style(ctx, style_id)
    try:
        with upstream():
            b2v.request("DELETE", f"/api/video-styles/custom/{style_id}")
    except HTTPException as e:
        if e.status_code != 404:
            raise
    ctx.db.delete(row)
    ctx.db.commit()
    return {"ok": True}