fix(worker): 素材下载改用storage_key字段,修复file_url为URL导致下载无效文件
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Failing after 0s
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 13s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 40s
AI Code Review / AI Code Review (pull_request) Failing after 1m23s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 2m47s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 3m6s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m27s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1m5s

- AssetModel新增storage_key字段(OSS存储键)
- render_adapter _download_assets改用a.storage_key
- render_adapter BGM下载改用storage_key
- edit_plan_generation legacy路径同步修复
- generation.py BGM下载同步修复
- asset_repository映射同步更新storage_key字段
- 单元测试同步更新
This commit is contained in:
CI Bot
2026-07-17 18:04:51 +08:00
parent 91ba55e210
commit 8e2e352cfe
6 changed files with 23 additions and 19 deletions
@@ -366,12 +366,12 @@ class RenderAdapter:
failed_clip_ids: list[str] = []
seen_asset_ids: set[str] = set()
# 批量查询素材的 file_urlOSS 存储路径)
# 批量查询素材的 storage_keyOSS 存储路径)
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.file_url for a in assets if a.file_url}
asset_storage_map = {a.id: a.storage_key for a in assets if a.storage_key}
for clip in clips:
asset_id = clip.asset_id
@@ -468,8 +468,8 @@ 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.file_url:
storage_key = model.file_url
if model and model.storage_key:
storage_key = model.storage_key
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:
@@ -472,14 +472,14 @@ def render_edit_plan(self, plan_id: str) -> dict:
rendered_clip_ids: list[str] = []
failed_clip_ids: list[str] = []
# 预先批量查询所有素材的 storage_keyfile_url
# 预先批量查询所有素材的 storage_key
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.file_url for a in assets if a.file_url}
asset_storage_map = {a.id: a.storage_key for a in assets if a.storage_key}
for clip in clips:
if not clip.asset_id:
+2 -2
View File
@@ -537,8 +537,8 @@ def _prepare_bgm_track(
session = SessionLocal()
try:
model = session.query(AssetModel).filter(AssetModel.id == asset_id).first()
if model and model.file_url:
storage_key = model.file_url
if model and model.storage_key:
storage_key = model.storage_key
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:
@@ -72,6 +72,7 @@ class SQLAlchemyAssetRepository:
file_type=(asset.mime_type.split("/")[0] if "/" in asset.mime_type else asset.mime_type),
file_size=asset.file_size,
file_url=asset.storage_key,
storage_key=asset.storage_key,
thumbnail_url=asset.thumbnail_url,
duration=asset.duration,
width=asset.width,
@@ -100,6 +101,7 @@ class SQLAlchemyAssetRepository:
model.name = asset.name
model.file_size = asset.file_size
model.file_url = asset.storage_key
model.storage_key = asset.storage_key
model.thumbnail_url = asset.thumbnail_url
model.duration = asset.duration
model.width = asset.width
@@ -299,7 +301,7 @@ class SQLAlchemyAssetRepository:
project_id=model.project_id,
library_id=model.asset_library_id,
name=model.name,
storage_key=model.file_url,
storage_key=model.storage_key or model.file_url,
mime_type=mime_type,
file_size=int(model.file_size or 0),
thumbnail_url=model.thumbnail_url,
@@ -75,6 +75,8 @@ class AssetModel(Base):
file_size = Column(Integer, nullable=False)
# file_url: 完整可访问的 URL,用于客户端直接访问文件
file_url = Column(String(1000), nullable=False)
# storage_key: OSS 存储键,用于内部下载上传
storage_key = Column(String(500), nullable=False, default="", server_default="")
thumbnail_url = Column(String(1000), nullable=True)
duration = Column(Float, nullable=True)
width = Column(Float, nullable=True)
+10 -10
View File
@@ -77,7 +77,7 @@ def _make_adapter(
Args:
plan: 模拟的剪辑计划
clips: 模拟的片段列表
asset_url_map: asset_id → file_url 映射,用于 mock assets 表查询
asset_url_map: asset_id → storage_key 映射,用于 mock assets 表查询
Returns:
(adapter, mock_plan_repo, mock_clip_repo)
@@ -91,7 +91,7 @@ def _make_adapter(
for aid, url in asset_url_map.items():
m = MagicMock()
m.id = aid
m.file_url = url
m.storage_key = url
mock_assets.append(m)
mock_query.all.return_value = mock_assets
mock_query.filter.return_value = mock_query
@@ -521,17 +521,17 @@ class TestRenderPlan:
class TestDownloadAssets:
@staticmethod
def _make_mock_db(asset_url_map: dict[str, str]):
"""构造 mock db,根据 asset_id 返回对应的 AssetModel.file_url"""
"""构造 mock db,根据 asset_id 返回对应的 AssetModel.storage_key"""
mock_db = MagicMock()
mock_query = MagicMock()
def _fake_filter(query):
# 模拟 .filter(AssetModel.id.in_([...])).all()
mock_assets = []
for asset_id, file_url in asset_url_map.items():
for asset_id, storage_key in asset_url_map.items():
mock_asset = MagicMock()
mock_asset.id = asset_id
mock_asset.file_url = file_url
mock_asset.storage_key = storage_key
mock_assets.append(mock_asset)
mock_query.all.return_value = mock_assets
return mock_query
@@ -565,7 +565,7 @@ class TestDownloadAssets:
assert len(rendered_ids) == 2
assert len(failed_ids) == 0
assert mock_download.call_count == 2
# 验证传给 download_asset 的是 file_url 而非 asset_id
# 验证传给 download_asset 的是 storage_key 而非 asset_id
call_keys = [call[0][0] for call in mock_download.call_args_list]
assert "https://bucket.oss.com/videos/key1.mp4" in call_keys
assert "https://bucket.oss.com/videos/key2.mp4" in call_keys
@@ -670,14 +670,14 @@ class TestDownloadAssets:
assert "c2" in rendered_ids
@patch("video_processing.render_adapter.download_asset")
def test_asset_without_file_url_skipped(self, mock_download, tmp_path):
"""素材在assets表中无file_url时跳过下载,标记为失败。"""
# 构造返回 asset 但 file_url 为空
def test_asset_without_storage_key_skipped(self, mock_download, tmp_path):
"""素材在assets表中无storage_key时跳过下载,标记为失败。"""
# 构造返回 asset 但 storage_key 为空
mock_db = MagicMock()
mock_query = MagicMock()
mock_asset = MagicMock()
mock_asset.id = "asset_no_url"
mock_asset.file_url = ""
mock_asset.storage_key = ""
mock_query.all.return_value = [mock_asset]
mock_query.filter.return_value = mock_query
mock_db.query.return_value = mock_query