20260429_232813-fix: video frame display pipeline — default project seed, presigned URLs, Canvas/FrameTimeline real frames, upload-parse flow
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
"""FastAPI application entrypoint."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from config import settings
|
||||
from database import Base, engine
|
||||
from minio_client import ensure_bucket_exists
|
||||
from database import Base, engine, SessionLocal
|
||||
from minio_client import ensure_bucket_exists, upload_file
|
||||
from redis_client import ping as redis_ping
|
||||
|
||||
from routers import projects, templates, media, ai, export, auth
|
||||
@@ -19,6 +23,76 @@ logging.basicConfig(
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_VIDEO_PATH = "/home/wkmgc/Desktop/Seg_Server/Data_MyVideo_1.mp4"
|
||||
|
||||
|
||||
def _seed_default_project_sync() -> None:
|
||||
"""Synchronously seed the default video project on first startup."""
|
||||
import cv2
|
||||
from models import Project, Frame
|
||||
from services.frame_parser import parse_video, upload_frames_to_minio
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
existing = db.query(Project).filter(Project.name == "Data_MyVideo_1").first()
|
||||
if existing is not None:
|
||||
return
|
||||
|
||||
if not os.path.exists(DEFAULT_VIDEO_PATH):
|
||||
logger.warning("Default video not found at %s", DEFAULT_VIDEO_PATH)
|
||||
return
|
||||
|
||||
project = Project(
|
||||
name="Data_MyVideo_1",
|
||||
description="默认演示视频",
|
||||
status="pending",
|
||||
)
|
||||
db.add(project)
|
||||
db.commit()
|
||||
db.refresh(project)
|
||||
|
||||
with open(DEFAULT_VIDEO_PATH, "rb") as f:
|
||||
data = f.read()
|
||||
object_name = f"uploads/{project.id}/Data_MyVideo_1.mp4"
|
||||
upload_file(object_name, data, content_type="video/mp4", length=len(data))
|
||||
|
||||
project.video_path = object_name
|
||||
db.commit()
|
||||
|
||||
# Parse frames
|
||||
tmp_dir = tempfile.mkdtemp(prefix=f"seg_seed_{project.id}_")
|
||||
try:
|
||||
local_path = os.path.join(tmp_dir, "video.mp4")
|
||||
with open(local_path, "wb") as f:
|
||||
f.write(data)
|
||||
output_dir = os.path.join(tmp_dir, "frames")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
frame_files = parse_video(local_path, output_dir, fps=30, max_frames=100)
|
||||
|
||||
object_names = upload_frames_to_minio(frame_files, project.id)
|
||||
|
||||
for idx, obj_name in enumerate(object_names):
|
||||
img = cv2.imread(frame_files[idx])
|
||||
h, w = img.shape[:2] if img is not None else (None, None)
|
||||
frame = Frame(
|
||||
project_id=project.id,
|
||||
frame_index=idx,
|
||||
image_url=obj_name,
|
||||
width=w,
|
||||
height=h,
|
||||
)
|
||||
db.add(frame)
|
||||
|
||||
project.status = "ready"
|
||||
db.commit()
|
||||
logger.info("Seeded default project id=%s with %d frames", project.id, len(object_names))
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
except Exception as exc:
|
||||
logger.error("Failed to seed default project: %s", exc)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
@@ -45,6 +119,12 @@ async def lifespan(app: FastAPI):
|
||||
else:
|
||||
logger.warning("Redis connection failed.")
|
||||
|
||||
# Seed default project in background thread so it doesn't block startup
|
||||
try:
|
||||
asyncio.create_task(asyncio.to_thread(_seed_default_project_sync))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error("Failed to start default project seeding: %s", exc)
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown
|
||||
|
||||
Reference in New Issue
Block a user