fix(cover): 封面生成前校验视频URL可达性,避免MediaKit超时120秒 (#1356)
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m41s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m14s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m42s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m42s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m36s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m40s
CI/CD Pipeline / Unit Tests (push) Successful in 10m23s
CI/CD Pipeline / Integration Tests (push) Successful in 4m27s
CI/CD Pipeline / Build Staging API Image (push) Successful in 12m35s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m30s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 38s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m5s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m46s
CI/CD Pipeline / Canary Release to Production (push) Failing after 815h33m54s
CI/CD Pipeline / CI Gate (push) Failing after 815h39m39s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 815h40m23s
CI/CD Pipeline / Build Production Web Image (push) Failing after 815h40m23s
CI/CD Pipeline / Production Browser E2E (push) Failing after 815h40m22s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 815h50m45s
CI/CD Pipeline / PR Build Web Image (push) Failing after 815h50m46s
CI/CD Pipeline / PR Build API Image (push) Failing after 815h50m47s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 815h50m49s
CI/CD Pipeline / Deploy Production (push) Failing after 816h13m54s
CI/CD Pipeline / Build Production API Image (push) Failing after 816h13m55s
CI/CD Pipeline / Frontend Lint (push) Failing after 816h24m19s

Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
This commit was merged in pull request #1356.
This commit is contained in:
2026-08-13 15:36:00 +08:00
committed by auto-approve-bot
parent 0529c61347
commit 7ba46cb9c0
2 changed files with 57 additions and 3 deletions
+22
View File
@@ -13,6 +13,8 @@ import random
import time
from typing import Any, Dict, List, Optional
import requests as http_requests
from packages.domain.config_schemas import DEFAULT_EDIT_PLAN_CONFIG
from packages.shared.ai_client import get_doubao_client
@@ -398,6 +400,26 @@ def _call_ai_cover_service(
client = get_mediakit_client()
if client.is_available:
# 先检查视频 URL 是否可访问,避免 MediaKit 下载失败后超时
try:
head_resp = http_requests.head(primary_video_url, timeout=10, allow_redirects=True)
if head_resp.status_code != 200:
logger.error(
"封面视频URL不可访问: plan_id=%s url=%s status=%d",
plan_id,
primary_video_url,
head_resp.status_code,
)
raise RuntimeError(
f"封面生成失败: 预览视频URL不可访问 (HTTP {head_resp.status_code})。"
f"请重新生成预览视频后再试。"
)
except http_requests.RequestException as e:
logger.error("封面视频URL连通性检查失败: plan_id=%s url=%s error=%s", plan_id, primary_video_url, e)
raise RuntimeError(
f"封面生成失败: 无法访问预览视频 ({e.__class__.__name__})。" f"请重新生成预览视频后再试。"
) from e
try:
logger.info("调用 MediaKit 抽帧: plan_id=%s video=%s", plan_id, primary_video_url[:80])
frames = client.extract_frames(
+35 -3
View File
@@ -142,9 +142,13 @@ class TestMediaKitClient:
class TestAICoverService:
"""AI 封面服务测试."""
@patch("packages.shared.ai_service.http_requests.head")
@patch("packages.shared.mediakit_client.get_mediakit_client")
def test_call_ai_cover_with_mediakit_success(self, mock_get_client):
def test_call_ai_cover_with_mediakit_success(self, mock_get_client, mock_head):
"""MediaKit 抽帧成功."""
# Mock HEAD request to verify URL is accessible
mock_head.return_value.status_code = 200
mock_client = Mock()
mock_client.is_available = True
mock_client.extract_frames.return_value = [{"image_url": "https://example.com/frame.jpg", "timestamp": 3.5}]
@@ -166,9 +170,34 @@ class TestAICoverService:
mock_client.extract_frames.assert_called_once()
@patch("packages.shared.ai_service.http_requests.head")
@patch("packages.shared.mediakit_client.get_mediakit_client")
def test_call_ai_cover_with_mediakit_failure_raises(self, mock_get_client):
def test_call_ai_cover_video_url_unreachable(self, mock_get_client, mock_head):
"""视频 URL 不可访问时抛出 RuntimeError."""
# Mock HEAD request to return 404
mock_head.return_value.status_code = 404
mock_client = Mock()
mock_client.is_available = True
mock_get_client.return_value = mock_client
from packages.shared.ai_service import _call_ai_cover_service
with pytest.raises(RuntimeError, match="预览视频URL不可访问"):
_call_ai_cover_service(
plan_id="plan-123",
asset_ids=["asset-1"],
cover_type="ai_frame",
primary_video_url="https://example.com/nonexistent.mp4",
)
@patch("packages.shared.ai_service.http_requests.head")
@patch("packages.shared.mediakit_client.get_mediakit_client")
def test_call_ai_cover_with_mediakit_failure_raises(self, mock_get_client, mock_head):
"""MediaKit 失败时抛出 RuntimeError(不再降级到 stub)."""
# Mock HEAD request to return 200 (URL is accessible, but MediaKit fails)
mock_head.return_value.status_code = 200
mock_client = Mock()
mock_client.is_available = True
mock_client.extract_frames.side_effect = Exception("API error")
@@ -243,9 +272,12 @@ class TestAICoverService:
primary_video_url="https://example.com/video.mp4",
)
@patch("packages.shared.ai_service.http_requests.head")
@patch("packages.shared.mediakit_client.get_mediakit_client")
def test_call_ai_cover_empty_frames_raises(self, mock_get_client):
def test_call_ai_cover_empty_frames_raises(self, mock_get_client, mock_head):
"""MediaKit 返回空帧列表时抛出 RuntimeError(不再降级)."""
mock_head.return_value.status_code = 200 # URL accessible
mock_client = Mock()
mock_client.is_available = True
mock_client.extract_frames.return_value = []