fix(worker): 素材下载storage_key为空时fallback到file_url,兼容存量数据 #485
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user