Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ba8e4b13f | |||
| 3d2e94b884 |
@@ -336,7 +336,7 @@ def get_project_asset_diagnosis(
|
||||
assets.extend(asset_repository.list_by_library(library.id))
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.exception("素材诊断查询失败: project_id=%s", project_id)
|
||||
# 返回空诊断结果,避免 500
|
||||
return _build_diagnosis(project_id, [])
|
||||
|
||||
@@ -178,7 +178,7 @@ def _cleanup_expired_uploads() -> int:
|
||||
cleaned += 1
|
||||
logger.info(f"Cleaned up expired upload: {upload_id}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to cleanup upload metadata {meta_file}: {e}")
|
||||
logger.exception("Failed to cleanup upload metadata: %s", meta_file)
|
||||
|
||||
return cleaned
|
||||
|
||||
|
||||
@@ -107,8 +107,8 @@ def create_variant_plans(
|
||||
)
|
||||
if _latest:
|
||||
source_plan_id = _latest.id
|
||||
except Exception:
|
||||
logger.warning("[variant-plans] 源 plan 解析失败", exc_info=True)
|
||||
except Exception as e:
|
||||
logger.exception("[variant-plans] 源 plan 解析失败")
|
||||
|
||||
if not source_plan_id:
|
||||
raise HTTPException(
|
||||
@@ -121,8 +121,8 @@ def create_variant_plans(
|
||||
from app.api.routes.generation_tasks import _query_voice_durations
|
||||
|
||||
voice_durations = _query_voice_durations(db, voices)
|
||||
except Exception:
|
||||
logger.warning("[variant-plans] 配音时长查询失败(按占位段长选片)", exc_info=True)
|
||||
except Exception as e:
|
||||
logger.exception("[variant-plans] 配音时长查询失败(按占位段长选片)")
|
||||
voice_durations = [0.0] * request.count
|
||||
|
||||
from app.services.edit_plan_service import EditPlanService
|
||||
@@ -143,7 +143,7 @@ def create_variant_plans(
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error("[variant-plans] 选片异常: %s", e, exc_info=True)
|
||||
logger.exception("[variant-plans] 选片异常")
|
||||
raise HTTPException(status_code=500, detail="选片失败,请稍后重试") from e
|
||||
|
||||
# 组装 clips 响应
|
||||
|
||||
@@ -66,7 +66,7 @@ def _build_asset_analyses(
|
||||
video_urls.append(url)
|
||||
valid_asset_ids.append(aid)
|
||||
except Exception as e:
|
||||
logger.warning("获取素材URL失败: asset_id=%s error=%s", aid, str(e))
|
||||
logger.exception("获取素材URL失败: asset_id=%s", aid)
|
||||
|
||||
if not video_urls:
|
||||
logger.info("无可用视频素材,跳过视频理解分析")
|
||||
@@ -108,7 +108,7 @@ def _build_asset_analyses(
|
||||
return analyses
|
||||
|
||||
except Exception as e:
|
||||
logger.warning("MediaKit 视频理解异常,将降级到无分析模式: %s", str(e))
|
||||
logger.exception("MediaKit 视频理解异常,将降级到无分析模式: %s", e)
|
||||
return {}
|
||||
|
||||
|
||||
@@ -176,8 +176,8 @@ def editor_ai_recommend(
|
||||
)
|
||||
try:
|
||||
db.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.exception("db rollback failed in ai_recommend")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="AI推荐结果保存失败,请稍后重试",
|
||||
|
||||
@@ -132,16 +132,16 @@ def _build_asset_url_map(
|
||||
result: dict[str, str | None] = {}
|
||||
try:
|
||||
storage = get_storage_service()
|
||||
except Exception:
|
||||
logger.warning("获取存储服务失败,跳过asset_url生成")
|
||||
except Exception as e:
|
||||
logger.exception("获取存储服务失败,跳过asset_url生成: %s", e)
|
||||
return {aid: None for aid in asset_ids}
|
||||
|
||||
# 批量查询所有 Asset(单次 SQL IN 查询,避免 N+1)
|
||||
try:
|
||||
assets = asset_repo.find_by_ids(unique_ids)
|
||||
asset_map = {a.id: a for a in assets}
|
||||
except Exception:
|
||||
logger.warning("批量查询素材失败: asset_ids=%s", asset_ids, exc_info=True)
|
||||
except Exception as e:
|
||||
logger.exception("批量查询素材失败: asset_ids=%s", asset_ids)
|
||||
return {aid: None for aid in asset_ids if aid}
|
||||
|
||||
for aid in unique_ids:
|
||||
@@ -155,8 +155,8 @@ def _build_asset_url_map(
|
||||
result[aid] = None
|
||||
continue
|
||||
result[aid] = storage.get_download_url(storage_key, expires_seconds=3600)
|
||||
except Exception:
|
||||
logger.warning("生成素材签名URL失败: asset_id=%s", aid, exc_info=True)
|
||||
except Exception as e:
|
||||
logger.exception("生成素材签名URL失败: asset_id=%s", aid)
|
||||
result[aid] = None
|
||||
|
||||
return result
|
||||
@@ -487,7 +487,7 @@ def _get_mediakit_recommendations(
|
||||
video_urls.append(url)
|
||||
valid_asset_ids.append(asset_id)
|
||||
except Exception as e:
|
||||
logger.warning("获取素材URL失败: asset_id=%s error=%s", asset_id, e)
|
||||
logger.exception("获取素材URL失败: asset_id=%s", asset_id)
|
||||
|
||||
if not video_urls:
|
||||
return {}
|
||||
@@ -563,7 +563,7 @@ def _get_mediakit_recommendations(
|
||||
return recommendations
|
||||
|
||||
except Exception as e:
|
||||
logger.warning("MediaKit 智能选片异常,降级为随机选择: %s", e)
|
||||
logger.exception("MediaKit 智能选片异常,降级为随机选择: %s", e)
|
||||
return {}
|
||||
|
||||
|
||||
@@ -860,7 +860,7 @@ def create_clips_from_assets_editor(
|
||||
duplicate_warning = None
|
||||
if dup_rate > 50:
|
||||
duplicate_warning = f"查重率 {dup_rate:.1f}% 超过50%,建议更换素材或模板"
|
||||
logger.warning(
|
||||
logger.exception(
|
||||
"from-assets 成片查重率超标: plan_id=%s dup_rate=%.1f%%",
|
||||
plan_id,
|
||||
dup_rate,
|
||||
@@ -960,8 +960,8 @@ def _update_mediakit_recommendations_async( # pragma: no cover
|
||||
# 尝试获取存储服务(用于生成视频 URL)
|
||||
try:
|
||||
storage = get_storage_service()
|
||||
except Exception:
|
||||
logger.warning("后台任务: 获取存储服务失败,跳过 SceneChange 更新")
|
||||
except Exception as e:
|
||||
logger.exception("后台任务: 获取存储服务失败,跳过 SceneChange 更新: %s", e)
|
||||
return
|
||||
|
||||
# 获取 MediaKit 客户端
|
||||
@@ -988,7 +988,7 @@ def _update_mediakit_recommendations_async( # pragma: no cover
|
||||
try:
|
||||
video_url = storage.get_download_url(storage_key)
|
||||
except Exception as e:
|
||||
logger.warning("后台任务: 获取素材URL失败: asset_id=%s error=%s", asset_id, e)
|
||||
logger.exception("后台任务: 获取素材URL失败: asset_id=%s", asset_id)
|
||||
|
||||
# 构建该素材的占用区间列表(排除已更新片段)
|
||||
def _get_other_segments(asset_id_inner, clip_id_inner):
|
||||
@@ -1041,10 +1041,9 @@ def _update_mediakit_recommendations_async( # pragma: no cover
|
||||
)
|
||||
except Exception as cache_err:
|
||||
# 缓存写入失败不影响本次片段更新
|
||||
logger.warning(
|
||||
"后台任务: 场景点缓存写入失败: asset_id=%s error=%s",
|
||||
logger.exception(
|
||||
"后台任务: 场景点缓存写入失败: asset_id=%s",
|
||||
asset_id,
|
||||
cache_err,
|
||||
)
|
||||
|
||||
# SceneChange 未获得有效结果 → 尝试 analyze_videos 作为 fallback
|
||||
@@ -1125,10 +1124,9 @@ def _update_mediakit_recommendations_async( # pragma: no cover
|
||||
plan_id,
|
||||
)
|
||||
except Exception as me:
|
||||
logger.warning(
|
||||
"后台任务: 同步素材区间记录失败,回滚本次片段更新: clip_id=%s error=%s",
|
||||
logger.exception(
|
||||
"后台任务: 同步素材区间记录失败,回滚本次片段更新: clip_id=%s",
|
||||
clip.id,
|
||||
me,
|
||||
)
|
||||
db.rollback()
|
||||
continue
|
||||
@@ -1145,10 +1143,10 @@ def _update_mediakit_recommendations_async( # pragma: no cover
|
||||
recommended_start,
|
||||
)
|
||||
except Exception as ue:
|
||||
logger.warning("后台任务: 单个片段更新失败: clip_id=%s error=%s", clip.id, ue)
|
||||
logger.exception("后台任务: 单个片段更新失败: clip_id=%s", clip.id)
|
||||
try:
|
||||
db.rollback()
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
pass
|
||||
continue
|
||||
|
||||
@@ -1156,15 +1154,15 @@ def _update_mediakit_recommendations_async( # pragma: no cover
|
||||
|
||||
except Exception as e:
|
||||
# 后台任务失败不影响已创建的片段,静默处理
|
||||
logger.warning("后台任务异常: plan_id=%s error=%s", plan_id, e, exc_info=True)
|
||||
logger.exception("后台任务异常: plan_id=%s", plan_id)
|
||||
if db:
|
||||
try:
|
||||
db.rollback()
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
pass
|
||||
finally:
|
||||
if db:
|
||||
try:
|
||||
db.close()
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
@@ -163,12 +163,12 @@ def create_voice_clone(
|
||||
celery_app.send_task("worker.process_voice_clone", args=[profile.id])
|
||||
logger.info(f"Celery task dispatched for voice clone {profile.id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to dispatch Celery task: {e}")
|
||||
logger.exception("Failed to dispatch Celery task")
|
||||
# P2-3: Celery 调度失败时标记 profile 为 failed,避免永久卡在 processing
|
||||
try:
|
||||
workflow.process_clone_failure(profile.id, f"Celery 任务调度失败: {e}")
|
||||
except Exception as inner_e:
|
||||
logger.error(f"Failed to mark profile as failed after dispatch error: {inner_e}")
|
||||
logger.exception("Failed to mark profile as failed after dispatch error")
|
||||
|
||||
return _to_response(profile)
|
||||
|
||||
@@ -277,12 +277,12 @@ def retry_voice_clone(
|
||||
celery_app.send_task("worker.process_voice_clone", args=[profile.id])
|
||||
logger.info(f"Celery task dispatched for voice clone retry {profile.id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to dispatch Celery task: {e}")
|
||||
logger.exception("Failed to dispatch Celery task")
|
||||
# P2-3: Celery 调度失败时标记 profile 为 failed,避免永久卡在 processing
|
||||
try:
|
||||
workflow.process_clone_failure(profile.id, f"Celery 任务调度失败: {e}")
|
||||
except Exception as inner_e:
|
||||
logger.error(f"Failed to mark profile as failed after dispatch error: {inner_e}")
|
||||
logger.exception("Failed to mark profile as failed after dispatch error")
|
||||
|
||||
return _to_response(profile)
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ def _resolve_preset_preview_url(
|
||||
logger.info("Preset voice preview generated: %s", voice_id)
|
||||
return audio_url
|
||||
except Exception as e:
|
||||
logger.warning("Failed to generate preview for %s, using fallback: %s", voice_id, e)
|
||||
logger.exception("Failed to generate preset voice preview: voice_id=%s", voice_id)
|
||||
return fallback_url
|
||||
|
||||
|
||||
@@ -126,7 +126,8 @@ def _resolve_all_preset_preview_urls(
|
||||
for p in presets:
|
||||
try:
|
||||
result_map[p.voice_id] = _resolve_preset_preview_url(p.voice_id, p.preview_url, cosyvoice)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.exception("Failed to resolve preset preview URL: voice_id=%s", p.voice_id)
|
||||
result_map[p.voice_id] = p.preview_url
|
||||
return result_map
|
||||
|
||||
@@ -731,8 +732,8 @@ def _find_or_create_voice_library_for_extract(*, user_id, project_repository, as
|
||||
if session is not None:
|
||||
try:
|
||||
session.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.exception("session rollback failed in _find_or_create_voice_library")
|
||||
for lib in asset_library_repository.find_by_project(project.id):
|
||||
kind = lib.kind.value if hasattr(lib.kind, "value") else lib.kind
|
||||
if kind == AssetLibraryKind.VOICE.value:
|
||||
|
||||
@@ -484,7 +484,6 @@
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
||||
.aa-lipsync-preview {
|
||||
width: 100%;
|
||||
max-width: 240px;
|
||||
@@ -1249,4 +1248,3 @@
|
||||
width: auto;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
|
||||
@@ -211,14 +211,7 @@ const AiAvatarPage: React.FC = () => {
|
||||
state.setIsGenerating(false)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
state.lipsyncJob,
|
||||
state.script,
|
||||
state.bRollSegments,
|
||||
state.titleConfig,
|
||||
state.coverConfig,
|
||||
|
||||
])
|
||||
}, [state.lipsyncJob, state.script, state.bRollSegments, state.titleConfig, state.coverConfig])
|
||||
|
||||
/* ── 智能封面:调后端 MediaKit 选帧接口(#1822) ── */
|
||||
const handleSmartCover = useCallback(async () => {
|
||||
@@ -375,7 +368,10 @@ const AiAvatarPage: React.FC = () => {
|
||||
<span className="aa-panel__toggle">▼</span>
|
||||
</div>
|
||||
<div className="aa-panel__body">
|
||||
<PanelTitleConfig titleConfig={state.titleConfig} onUpdate={state.updateTitleConfig} />
|
||||
<PanelTitleConfig
|
||||
titleConfig={state.titleConfig}
|
||||
onUpdate={state.updateTitleConfig}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ export function PanelLipsyncPreview({
|
||||
onRemoveBRoll,
|
||||
titleConfig,
|
||||
}: PanelLipsyncPreviewProps) {
|
||||
const isGenerating =
|
||||
lipsyncJob?.status === "pending" || lipsyncJob?.status === "processing"
|
||||
const isGenerating = lipsyncJob?.status === "pending" || lipsyncJob?.status === "processing"
|
||||
const isDone = lipsyncJob?.status === "completed"
|
||||
const isFailed = lipsyncJob?.status === "failed"
|
||||
|
||||
@@ -78,9 +77,7 @@ export function PanelLipsyncPreview({
|
||||
<div className="aa-lipsync-section__title">
|
||||
<span style={{ marginRight: 8 }}>🎞️ 插入画面</span>
|
||||
{bRollSegments.length > 0 && (
|
||||
<span className="aa-broll-badge">
|
||||
🎬 {bRollSegments.length} 个画面
|
||||
</span>
|
||||
<span className="aa-broll-badge">🎬 {bRollSegments.length} 个画面</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="aa-lipsync-actions">
|
||||
@@ -104,10 +101,7 @@ export function PanelLipsyncPreview({
|
||||
alt={seg.asset.name}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
className="aa-broll-item__thumb"
|
||||
style={{ padding: "6px 4px" }}
|
||||
>
|
||||
<span className="aa-broll-item__thumb" style={{ padding: "6px 4px" }}>
|
||||
🎬
|
||||
</span>
|
||||
)}
|
||||
@@ -189,11 +183,7 @@ export function PanelLipsyncPreview({
|
||||
|
||||
<div className="aa-lipsync-actions">
|
||||
{isDone ? (
|
||||
<button
|
||||
type="button"
|
||||
className="aa-btn aa-btn--full"
|
||||
onClick={onGenerateLipsync}
|
||||
>
|
||||
<button type="button" className="aa-btn aa-btn--full" onClick={onGenerateLipsync}>
|
||||
🔄 重新生成对口型
|
||||
</button>
|
||||
) : isGenerating ? (
|
||||
|
||||
@@ -55,9 +55,7 @@ export function PanelScript({
|
||||
value={scriptText}
|
||||
readOnly={scriptTab === "library"}
|
||||
placeholder={
|
||||
scriptTab === "library"
|
||||
? "点击上方按钮,从文案库选择文案…"
|
||||
: "请输入数字人口播文案…"
|
||||
scriptTab === "library" ? "点击上方按钮,从文案库选择文案…" : "请输入数字人口播文案…"
|
||||
}
|
||||
onChange={(e) => onScriptTextChange(e.target.value)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user