176 lines
7.2 KiB
Python
176 lines
7.2 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from fastapi import HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.enums.video_upscale import (
|
|
ALL_PROCESSOR_KEYS,
|
|
VIDEO_UPSCALE_CONFIG_DESCRIPTION,
|
|
VIDEO_UPSCALE_CONFIG_KEY,
|
|
VIDEO_UPSCALE_CONFIG_VERSION,
|
|
VIDEO_UPSCALE_RESOLUTION_RANK,
|
|
VIDEO_UPSCALE_RESOLUTIONS,
|
|
VideoUpscaleProcessorKey,
|
|
normalize_video_upscale_resolution,
|
|
)
|
|
from app.models.system_config import SystemConfig
|
|
from app.schemas.video_upscale import VideoUpscaleConfigData
|
|
from app.utils.id_gen import generate_id
|
|
|
|
|
|
def default_video_upscale_config() -> dict[str, Any]:
|
|
return {
|
|
"enabled": False,
|
|
"version": VIDEO_UPSCALE_CONFIG_VERSION,
|
|
"delete_source_after_success": True,
|
|
"rules": [],
|
|
}
|
|
|
|
|
|
def _dump(data: dict[str, Any]) -> str:
|
|
return json.dumps(data, ensure_ascii=False, separators=(",", ":"), default=str)
|
|
|
|
|
|
def _load(value: str | None) -> dict[str, Any]:
|
|
if not value:
|
|
return default_video_upscale_config()
|
|
try:
|
|
data = json.loads(value)
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=500, detail=f"视频超分配置 JSON 损坏: {exc}") from exc
|
|
if not isinstance(data, dict):
|
|
raise HTTPException(status_code=500, detail="视频超分配置必须是 JSON 对象")
|
|
return data
|
|
|
|
|
|
def _simplify_stored_config(data: dict[str, Any]) -> dict[str, Any]:
|
|
"""兼容开发阶段已保存的旧页面配置,忽略 processors、比例和手工像素等多余字段。"""
|
|
rules: list[dict[str, Any]] = []
|
|
for raw_rule in data.get("rules") or []:
|
|
if not isinstance(raw_rule, dict):
|
|
continue
|
|
rules.append(
|
|
{
|
|
"target_resolution": raw_rule.get("target_resolution"),
|
|
"provider_generation_resolution": raw_rule.get("provider_generation_resolution"),
|
|
"processor_key": raw_rule.get("processor_key"),
|
|
"enabled": bool(raw_rule.get("enabled", True)),
|
|
}
|
|
)
|
|
return {
|
|
"enabled": bool(data.get("enabled", False)),
|
|
"version": max(1, int(data.get("version") or VIDEO_UPSCALE_CONFIG_VERSION)),
|
|
"delete_source_after_success": bool(data.get("delete_source_after_success", True)),
|
|
"rules": rules,
|
|
}
|
|
|
|
|
|
async def _get_record(db: AsyncSession) -> SystemConfig | None:
|
|
result = await db.execute(select(SystemConfig).where(SystemConfig.key == VIDEO_UPSCALE_CONFIG_KEY).limit(1))
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
def validate_video_upscale_config(data: dict[str, Any] | VideoUpscaleConfigData) -> dict[str, Any]:
|
|
raw = data.model_dump() if isinstance(data, VideoUpscaleConfigData) else _simplify_stored_config(dict(data))
|
|
try:
|
|
model = VideoUpscaleConfigData.model_validate(raw)
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
normalized = model.model_dump()
|
|
seen: set[str] = set()
|
|
for rule in normalized.get("rules") or []:
|
|
target = normalize_video_upscale_resolution(rule.get("target_resolution"))
|
|
provider_resolution = normalize_video_upscale_resolution(rule.get("provider_generation_resolution"))
|
|
processor_key = str(rule.get("processor_key") or "").strip()
|
|
|
|
if target not in VIDEO_UPSCALE_RESOLUTIONS:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"不支持的客户目标分辨率: {target},仅支持 {'、'.join(VIDEO_UPSCALE_RESOLUTIONS)}",
|
|
)
|
|
if provider_resolution not in VIDEO_UPSCALE_RESOLUTIONS:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"不支持的实际生成分辨率: {provider_resolution},仅支持 {'、'.join(VIDEO_UPSCALE_RESOLUTIONS)}",
|
|
)
|
|
if processor_key not in ALL_PROCESSOR_KEYS:
|
|
raise HTTPException(status_code=400, detail=f"未注册的超分处理器: {processor_key}")
|
|
|
|
rule["target_resolution"] = target
|
|
rule["provider_generation_resolution"] = provider_resolution
|
|
rule["processor_key"] = processor_key
|
|
|
|
if VIDEO_UPSCALE_RESOLUTION_RANK[provider_resolution] > VIDEO_UPSCALE_RESOLUTION_RANK[target]:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"规则 {target} 的实际生成分辨率 {provider_resolution} 不能高于客户目标分辨率",
|
|
)
|
|
if processor_key == VideoUpscaleProcessorKey.VOLC_LARGE_MODEL_V1.value:
|
|
if target not in {"720p", "1080p", "2K"}:
|
|
raise HTTPException(status_code=400, detail="火山画质增强大模型目标分辨率仅支持 720p、1080p、2K")
|
|
if VIDEO_UPSCALE_RESOLUTION_RANK[provider_resolution] > VIDEO_UPSCALE_RESOLUTION_RANK["1080p"]:
|
|
raise HTTPException(status_code=400, detail="火山画质增强大模型输入视频最高支持 1080p")
|
|
if processor_key in {
|
|
VideoUpscaleProcessorKey.VOLC_STANDARD_V1.value,
|
|
VideoUpscaleProcessorKey.VOLC_PROFESSIONAL_V1.value,
|
|
} and VIDEO_UPSCALE_RESOLUTION_RANK[provider_resolution] > VIDEO_UPSCALE_RESOLUTION_RANK["2K"]:
|
|
raise HTTPException(status_code=400, detail="火山标准版/专业版输入视频最高支持 2K")
|
|
|
|
if rule.get("enabled"):
|
|
if target in seen:
|
|
raise HTTPException(status_code=400, detail=f"客户目标分辨率存在重复启用规则: {target}")
|
|
seen.add(target)
|
|
|
|
return normalized
|
|
|
|
|
|
async def get_video_upscale_config(db: AsyncSession) -> dict[str, Any]:
|
|
record = await _get_record(db)
|
|
data = default_video_upscale_config() if record is None else validate_video_upscale_config(_load(record.value))
|
|
return {
|
|
"id": record.id if record else None,
|
|
"key": VIDEO_UPSCALE_CONFIG_KEY,
|
|
"description": record.description if record else VIDEO_UPSCALE_CONFIG_DESCRIPTION,
|
|
"data": data,
|
|
"created_at": record.created_at if record else None,
|
|
"updated_at": record.updated_at if record else None,
|
|
}
|
|
|
|
|
|
async def get_runtime_video_upscale_config(db: AsyncSession) -> dict[str, Any]:
|
|
record = await _get_record(db)
|
|
if record is None:
|
|
return default_video_upscale_config()
|
|
return validate_video_upscale_config(_load(record.value))
|
|
|
|
|
|
async def save_video_upscale_config(db: AsyncSession, data: dict[str, Any] | VideoUpscaleConfigData) -> dict[str, Any]:
|
|
normalized = validate_video_upscale_config(data)
|
|
record = await _get_record(db)
|
|
old_version = 0
|
|
if record:
|
|
try:
|
|
old_version = int((_load(record.value).get("version") or 0))
|
|
except Exception:
|
|
old_version = 0
|
|
normalized["version"] = max(old_version + 1, VIDEO_UPSCALE_CONFIG_VERSION)
|
|
|
|
if record is None:
|
|
record = SystemConfig(
|
|
id=generate_id(),
|
|
key=VIDEO_UPSCALE_CONFIG_KEY,
|
|
value=_dump(normalized),
|
|
description=VIDEO_UPSCALE_CONFIG_DESCRIPTION,
|
|
)
|
|
db.add(record)
|
|
else:
|
|
record.value = _dump(normalized)
|
|
record.description = VIDEO_UPSCALE_CONFIG_DESCRIPTION
|
|
await db.flush()
|
|
return await get_video_upscale_config(db)
|