diff --git a/apps/api/app/api/routes/templates_editor/clips.py b/apps/api/app/api/routes/templates_editor/clips.py index b678b9d15..5ee2946fb 100755 --- a/apps/api/app/api/routes/templates_editor/clips.py +++ b/apps/api/app/api/routes/templates_editor/clips.py @@ -690,16 +690,16 @@ def _update_mediakit_recommendations_async( # pragma: no cover 此函数在后台异步执行,不影响接口响应时间。 失败时静默处理,不影响已创建的片段。 """ - from app.config import settings - from packages.adapters.sqlalchemy_impl.asset_repository import SQLAlchemyAssetRepository - from packages.adapters.sqlalchemy_impl.session import build_session_factory + from packages.adapters.sqlalchemy_impl.session import SessionLocal db = None try: - # 创建独立的数据库 session(不复用请求 session) - _, session_factory = build_session_factory(settings.DATABASE_URL) - db = session_factory() + # 复用应用全局 Session(避免每次创建新连接池导致资源泄漏) + if SessionLocal is None: + logger.warning("后台任务: SessionLocal 未初始化,跳过 MediaKit 更新") + return + db = SessionLocal() # 初始化服务 asset_repo = SQLAlchemyAssetRepository(db) @@ -711,14 +711,32 @@ def _update_mediakit_recommendations_async( # pragma: no cover logger.info("后台任务: MediaKit 无推荐结果,跳过更新") return - # 查询该 plan 的所有片段 - clips = plan_svc.list_clips(plan_id, skip=0, limit=500) + # 查询该 plan 的所有片段(分批获取,避免硬编码 limit 截断) + batch_size = 500 + all_clips = [] + offset = 0 + while True: + batch = plan_svc.list_clips(plan_id, skip=offset, limit=batch_size) + if not batch: + break + all_clips.extend(batch) + if len(batch) < batch_size: + break + offset += batch_size + clips = all_clips + if not clips: logger.info("后台任务: plan_id=%s 无片段,跳过更新", plan_id) return - # 按 asset_id 分组,跟踪已使用时间段 + # 按 asset_id 分组,初始化已使用时间段(包含所有片段的当前随机时间) used_segments: dict[str, list[tuple[float, float]]] = {} + for clip in clips: + aid = getattr(clip, "asset_id", "") or "" + if aid and clip.start_time is not None: + used_segments.setdefault(aid, []).append( + (clip.start_time, clip.start_time + clip.duration) + ) # 遍历片段,按 asset_id 匹配推荐时间 for clip in clips: