diff --git a/apps/worker/video_processing/render_adapter.py b/apps/worker/video_processing/render_adapter.py index 7bf06ef4e..7c6955a30 100755 --- a/apps/worker/video_processing/render_adapter.py +++ b/apps/worker/video_processing/render_adapter.py @@ -367,11 +367,14 @@ class RenderAdapter: seen_asset_ids: set[str] = set() # 批量查询素材的 storage_key(OSS 存储路径) + # 兼容存量数据:storage_key 为空时 fallback 到 file_url clip_asset_ids = [c.asset_id for c in clips if c.asset_id] asset_storage_map: dict[str, str] = {} if clip_asset_ids: assets = self._db.query(AssetModel).filter(AssetModel.id.in_(clip_asset_ids)).all() - asset_storage_map = {a.id: a.storage_key for a in assets if a.storage_key} + asset_storage_map = { + a.id: (a.storage_key or a.file_url or "") for a in assets if a.storage_key or a.file_url + } for clip in clips: asset_id = clip.asset_id @@ -468,8 +471,9 @@ class RenderAdapter: from packages.adapters.sqlalchemy_impl.models import AssetModel model = self._db.query(AssetModel).filter(AssetModel.id == asset_id).first() - if model and model.storage_key: - storage_key = model.storage_key + if model and (model.storage_key or model.file_url): + # 兼容存量数据:storage_key 为空时 fallback 到 file_url + storage_key = model.storage_key or model.file_url logger.info("[plan_id=%s] [BGM] 从素材库下载: asset_id=%s", plan_id, asset_id) ok = download_asset(storage_key, bgm_file) if ok and bgm_file.exists() and bgm_file.stat().st_size > 0: diff --git a/apps/worker/worker_app/tasks/edit_plan_generation.py b/apps/worker/worker_app/tasks/edit_plan_generation.py index 9fce84cc6..5b06c702a 100755 --- a/apps/worker/worker_app/tasks/edit_plan_generation.py +++ b/apps/worker/worker_app/tasks/edit_plan_generation.py @@ -473,13 +473,16 @@ def render_edit_plan(self, plan_id: str) -> dict: failed_clip_ids: list[str] = [] # 预先批量查询所有素材的 storage_key + # 兼容存量数据:storage_key 为空时 fallback 到 file_url from packages.adapters.sqlalchemy_impl.models import AssetModel clip_asset_ids = [c.asset_id for c in clips if c.asset_id] asset_storage_map: dict[str, str] = {} if clip_asset_ids: assets = db.query(AssetModel).filter(AssetModel.id.in_(clip_asset_ids)).all() - asset_storage_map = {a.id: a.storage_key for a in assets if a.storage_key} + asset_storage_map = { + a.id: (a.storage_key or a.file_url or "") for a in assets if a.storage_key or a.file_url + } for clip in clips: if not clip.asset_id: diff --git a/apps/worker/worker_app/tasks/generation.py b/apps/worker/worker_app/tasks/generation.py index a9f988d86..1f89202ea 100755 --- a/apps/worker/worker_app/tasks/generation.py +++ b/apps/worker/worker_app/tasks/generation.py @@ -537,8 +537,9 @@ def _prepare_bgm_track( session = SessionLocal() try: model = session.query(AssetModel).filter(AssetModel.id == asset_id).first() - if model and model.storage_key: - storage_key = model.storage_key + if model and (model.storage_key or model.file_url): + # 兼容存量数据:storage_key 为空时 fallback 到 file_url + storage_key = model.storage_key or model.file_url logger.info("[task_id=%s] [BGM] 从素材库下载: asset_id=%s", task_id, asset_id) ok = download_asset(storage_key, bgm_file) if ok and bgm_file.exists() and bgm_file.stat().st_size > 0: diff --git a/tests/unit/test_render_adapter.py b/tests/unit/test_render_adapter.py index 13a15fe00..efb920d79 100755 --- a/tests/unit/test_render_adapter.py +++ b/tests/unit/test_render_adapter.py @@ -670,20 +670,46 @@ class TestDownloadAssets: assert "c2" in rendered_ids @patch("video_processing.render_adapter.download_asset") - def test_asset_without_storage_key_skipped(self, mock_download, tmp_path): - """素材在assets表中无storage_key时跳过下载,标记为失败。""" - # 构造返回 asset 但 storage_key 为空 + def test_asset_without_storage_key_fallback_to_file_url(self, mock_download, tmp_path): + """storage_key为空但file_url有值时,fallback到file_url下载成功。""" + mock_download.return_value = True mock_db = MagicMock() mock_query = MagicMock() mock_asset = MagicMock() - mock_asset.id = "asset_no_url" + mock_asset.id = "asset_fallback" mock_asset.storage_key = "" + mock_asset.file_url = "videos/test_fallback.mp4" mock_query.all.return_value = [mock_asset] mock_query.filter.return_value = mock_query mock_db.query.return_value = mock_query adapter = RenderAdapter(mock_db) - clips = [_make_clip("c1", order=0, asset_id="asset_no_url")] + clips = [_make_clip("c1", order=0, asset_id="asset_fallback")] + + asset_path_map, rendered_ids, failed_ids = adapter._download_assets(clips, tmp_path) + + assert len(asset_path_map) == 1 + assert "c1" in rendered_ids + mock_download.assert_called_once() + # 验证用的是file_url的值 + call_args = mock_download.call_args + assert call_args[0][0] == "videos/test_fallback.mp4" + + @patch("video_processing.render_adapter.download_asset") + def test_asset_without_storage_key_and_file_url_skipped(self, mock_download, tmp_path): + """storage_key和file_url都为空时跳过下载,标记为失败。""" + mock_db = MagicMock() + mock_query = MagicMock() + mock_asset = MagicMock() + mock_asset.id = "asset_no_key" + mock_asset.storage_key = "" + mock_asset.file_url = "" + mock_query.all.return_value = [mock_asset] + mock_query.filter.return_value = mock_query + mock_db.query.return_value = mock_query + + adapter = RenderAdapter(mock_db) + clips = [_make_clip("c1", order=0, asset_id="asset_no_key")] asset_path_map, rendered_ids, failed_ids = adapter._download_assets(clips, tmp_path)