diff --git a/app/api/v1/public_api/video.py b/app/api/v1/public_api/video.py index 0a3976a..f79d617 100644 --- a/app/api/v1/public_api/video.py +++ b/app/api/v1/public_api/video.py @@ -5,8 +5,9 @@ import uuid from typing import Optional, List, Dict, Any import orjson +import httpx from fastapi import APIRouter, Depends, HTTPException, Query, Request -from fastapi.responses import StreamingResponse +from fastapi.responses import StreamingResponse, Response from pydantic import BaseModel, Field from app.core.auth import verify_public_key @@ -21,6 +22,8 @@ router = APIRouter() VIDEO_SESSION_TTL = 600 _VIDEO_SESSIONS: dict[str, dict] = {} _VIDEO_SESSIONS_LOCK = asyncio.Lock() +_VENDOR_CACHE: dict[str, bytes] = {} +_VENDOR_LOCK = asyncio.Lock() _VIDEO_RATIO_MAP = { "1280x720": "16:9", @@ -35,6 +38,28 @@ _VIDEO_RATIO_MAP = { "1:1": "1:1", } +_FFMPEG_VENDOR_SOURCES = { + "ffmpeg-core.js": [ + "https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.js", + "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.js", + ], + "ffmpeg-core.wasm": [ + "https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.wasm", + "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.wasm", + ], + # 某些版本没有 worker 文件,允许返回 404 由前端自动降级。 + "ffmpeg-core.worker.js": [ + "https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.worker.js", + "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.worker.js", + ], +} + +_VENDOR_CONTENT_TYPE = { + "ffmpeg-core.js": "application/javascript; charset=utf-8", + "ffmpeg-core.worker.js": "application/javascript; charset=utf-8", + "ffmpeg-core.wasm": "application/wasm", +} + def _extract_parent_post_id_from_url(url: str) -> str: text = str(url or "").strip() @@ -390,6 +415,51 @@ async def public_video_stop(data: VideoStopRequest): return {"status": "success", "removed": removed} +@router.get("/video/vendor/{filename}") +async def public_video_vendor(filename: str): + filename = str(filename or "").strip() + if filename not in _FFMPEG_VENDOR_SOURCES: + raise HTTPException(status_code=404, detail="vendor asset not found") + + async with _VENDOR_LOCK: + cached = _VENDOR_CACHE.get(filename) + if cached: + return Response( + content=cached, + media_type=_VENDOR_CONTENT_TYPE.get(filename, "application/octet-stream"), + headers={"Cache-Control": "public, max-age=86400"}, + ) + + timeout = httpx.Timeout(connect=8.0, read=60.0, write=30.0, pool=8.0) + last_error = None + async with httpx.AsyncClient(timeout=timeout) as client: + for url in _FFMPEG_VENDOR_SOURCES[filename]: + try: + resp = await client.get(url, follow_redirects=True) + if resp.status_code == 200 and resp.content: + content = bytes(resp.content) + async with _VENDOR_LOCK: + _VENDOR_CACHE[filename] = content + logger.info( + f"Video vendor proxy loaded: {filename} <- {url} ({len(content)} bytes)" + ) + return Response( + content=content, + media_type=_VENDOR_CONTENT_TYPE.get( + filename, "application/octet-stream" + ), + headers={"Cache-Control": "public, max-age=86400"}, + ) + last_error = f"status={resp.status_code}" + except Exception as e: + last_error = str(e) + + raise HTTPException( + status_code=502, + detail=f"vendor fetch failed: {filename}, error={last_error or 'unknown'}", + ) + + @router.get("/video/cache/list", dependencies=[Depends(verify_public_key)]) async def public_video_cache_list(page: int = 1, page_size: int = 100): page = max(1, int(page or 1)) diff --git a/app/static/public/js/video.js b/app/static/public/js/video.js index ae317fa..376348f 100644 --- a/app/static/public/js/video.js +++ b/app/static/public/js/video.js @@ -637,13 +637,13 @@ try { const candidates = { coreURL: [ - '/static/vendor/ffmpeg/ffmpeg-core.js', + '/v1/public/video/vendor/ffmpeg-core.js', ], wasmURL: [ - '/static/vendor/ffmpeg/ffmpeg-core.wasm', + '/v1/public/video/vendor/ffmpeg-core.wasm', ], workerURL: [ - '/static/vendor/ffmpeg/ffmpeg-core.worker.js', + '/v1/public/video/vendor/ffmpeg-core.worker.js', ], }; // 统一转为同源 blob URL,避免跨域 Worker 限制,同时实现多源超时切换。 diff --git a/app/static/public/pages/video.html b/app/static/public/pages/video.html index 5dab45f..b41f6d6 100644 --- a/app/static/public/pages/video.html +++ b/app/static/public/pages/video.html @@ -274,7 +274,7 @@ - +