拆镜复刻、爆款开头复刻容灾优化
This commit is contained in:
@@ -4,6 +4,10 @@ Celery autodiscover imports ``app.tasks``; importing task modules here ensures
|
||||
custom named tasks are registered when workers start.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("video_gen")
|
||||
|
||||
try:
|
||||
from app.tasks import ( # noqa: F401
|
||||
generation_create_tasks,
|
||||
@@ -12,7 +16,9 @@ try:
|
||||
generation_recovery_tasks,
|
||||
hot_opening_replicate_tasks,
|
||||
shot_replicate_tasks,
|
||||
shot_replicate_flow_tasks
|
||||
shot_replicate_flow_tasks,
|
||||
module_async_recovery_tasks,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
logger.exception("Celery 任务模块导入失败,worker 可能出现 unregistered task。")
|
||||
raise
|
||||
|
||||
@@ -59,6 +59,7 @@ if broker_url:
|
||||
"shot_replicate.recover_split_tasks_once": {"queue": "gen_result_download"},
|
||||
"generation.recover_download_tasks_once": {"queue": "gen_result_download"},
|
||||
"generation.recover_generation_tasks_once": {"queue": "gen_result_download"},
|
||||
"module_async.recover_module_async_tasks_once": {"queue": "gen_result_download"},
|
||||
"app.tasks.cleanup.*": {"queue": "default"},
|
||||
},
|
||||
)
|
||||
@@ -109,6 +110,7 @@ def on_worker_ready(sender=None, **kwargs):
|
||||
recover_generation_tasks_once,
|
||||
)
|
||||
from app.tasks.shot_replicate_tasks import recover_split_tasks_once
|
||||
from app.tasks.module_async_recovery_tasks import recover_module_async_tasks_once_task
|
||||
|
||||
countdown = max(0, int(settings.DOWNLOAD_RECOVERY_STARTUP_DELAY_SECONDS or 0))
|
||||
|
||||
@@ -127,6 +129,11 @@ def on_worker_ready(sender=None, **kwargs):
|
||||
queue="gen_result_download",
|
||||
priority=settings.DOWNLOAD_TASK_PRIORITY_RECOVER,
|
||||
)
|
||||
recover_module_async_tasks_once_task.apply_async(
|
||||
countdown=countdown + 15,
|
||||
queue="gen_result_download",
|
||||
priority=settings.DOWNLOAD_TASK_PRIORITY_RECOVER,
|
||||
)
|
||||
|
||||
logger.info("启动容灾恢复任务已投递。countdown=%s", countdown)
|
||||
except Exception:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from app.tasks.async_runner import run_async
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Optional
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.config import settings
|
||||
from app.models.base import async_session
|
||||
from app.models.chat_generation_task import ChatGenerationTask
|
||||
from app.services.error_codes import extract_error_message
|
||||
@@ -16,6 +17,8 @@ from app.tasks.celery_app import celery_app
|
||||
|
||||
ALLOWED_GENERATION_MODES = {"chatapi_async", "hot_opening_replicate", "shot_replicate"}
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
def _get_first_value(obj: Any, *field_names: str) -> Optional[Any]:
|
||||
"""
|
||||
@@ -242,8 +245,13 @@ async def _run(task_id: str):
|
||||
|
||||
await enqueue_download_task(db, task, reason="create_result_ready")
|
||||
else:
|
||||
from app.tasks.generation_poll_tasks import poll_generation_task
|
||||
from app.tasks.generation_poll_tasks import poll_generation_task, register_poll_active
|
||||
|
||||
await register_poll_active(
|
||||
task,
|
||||
reason="create_provider_success",
|
||||
check_at=_now() + timedelta(seconds=int(settings.POLL_TASK_LEASE_SECONDS or 300)),
|
||||
)
|
||||
poll_generation_task.apply_async(
|
||||
args=[task.id],
|
||||
queue="gen_provider_poll",
|
||||
|
||||
@@ -1,45 +1,95 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.enums.hot_opening_replicate import HotOpeningStepCodeEnum, ModuleCodeEnum
|
||||
from app.models.base import async_session
|
||||
from app.services.hot_opening_replicate_service import run_image_prompt_optimize, run_video_prompt_optimize
|
||||
from app.services.module_async_recovery_service import (
|
||||
OBJECT_MODULE_STEP,
|
||||
TASK_HOT_IMAGE_PROMPT,
|
||||
TASK_HOT_VIDEO_PROMPT,
|
||||
acquire_object_lock,
|
||||
cleanup_active_if_terminal,
|
||||
mark_active_started,
|
||||
release_object_lock,
|
||||
register_module_step_task,
|
||||
)
|
||||
from app.tasks.async_runner import run_async
|
||||
from app.tasks.celery_app import celery_app
|
||||
|
||||
MODULE = ModuleCodeEnum.HOT_OPENING_REPLICATE.value
|
||||
|
||||
|
||||
async def _run_image_prompt(project_id: str, step_id: str | None = None):
|
||||
async with async_session() as db:
|
||||
await run_image_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
lock_token: str | None = None
|
||||
if step_id:
|
||||
lock_token = await acquire_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
if not lock_token:
|
||||
# 同一个 step 的重复消息正在被其它 worker 处理:当前消息直接忽略,active registry 保留等待真正任务完成或 lease 过期。
|
||||
return None
|
||||
await register_module_step_task(
|
||||
module=MODULE,
|
||||
project_id=project_id,
|
||||
step_id=step_id,
|
||||
step_code=HotOpeningStepCodeEnum.IMAGE_PROMPT_OPTIMIZE.value,
|
||||
task_name=TASK_HOT_IMAGE_PROMPT,
|
||||
)
|
||||
await mark_active_started(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
try:
|
||||
async with async_session() as db:
|
||||
result = await run_image_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
if step_id:
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
return result
|
||||
finally:
|
||||
if step_id:
|
||||
await release_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id, token=lock_token)
|
||||
|
||||
|
||||
async def _run_video_prompt(project_id: str, step_id: str | None = None):
|
||||
async with async_session() as db:
|
||||
await run_video_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
lock_token: str | None = None
|
||||
if step_id:
|
||||
lock_token = await acquire_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
if not lock_token:
|
||||
return None
|
||||
await register_module_step_task(
|
||||
module=MODULE,
|
||||
project_id=project_id,
|
||||
step_id=step_id,
|
||||
step_code=HotOpeningStepCodeEnum.VIDEO_PROMPT_OPTIMIZE.value,
|
||||
task_name=TASK_HOT_VIDEO_PROMPT,
|
||||
)
|
||||
await mark_active_started(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
try:
|
||||
async with async_session() as db:
|
||||
result = await run_video_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
if step_id:
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
return result
|
||||
finally:
|
||||
if step_id:
|
||||
await release_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id, token=lock_token)
|
||||
|
||||
|
||||
if celery_app:
|
||||
@celery_app.task(name="hot_opening.start_image_prompt_optimize", bind=True, max_retries=3, default_retry_delay=30)
|
||||
def start_image_prompt_optimize(self, project_id: str, step_id: str | None = None):
|
||||
"""手动触发后的图片 AI 提词任务。
|
||||
|
||||
该任务路由到现有 gen_chatapi_create 队列,不需要新增 hot_opening worker。
|
||||
"""
|
||||
"""手动触发后的图片 AI 提词任务。"""
|
||||
return run_async(_run_image_prompt(project_id, step_id))
|
||||
|
||||
@celery_app.task(name="hot_opening.start_video_prompt_optimize", bind=True, max_retries=3, default_retry_delay=30)
|
||||
def start_video_prompt_optimize(self, project_id: str, step_id: str | None = None):
|
||||
"""手动触发后的视频 AI 提词任务。
|
||||
|
||||
该任务路由到现有 gen_chatapi_create 队列,不需要新增 hot_opening worker。
|
||||
"""
|
||||
"""手动触发后的视频 AI 提词任务。"""
|
||||
return run_async(_run_video_prompt(project_id, step_id))
|
||||
else:
|
||||
class _DisabledTask:
|
||||
def delay(self, *args, **kwargs):
|
||||
def delay(self, *args: Any, **kwargs: Any):
|
||||
raise RuntimeError("Celery is disabled")
|
||||
|
||||
def apply_async(self, *args, **kwargs):
|
||||
def apply_async(self, *args: Any, **kwargs: Any):
|
||||
raise RuntimeError("Celery is disabled")
|
||||
|
||||
start_image_prompt_optimize = _DisabledTask()
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.models.base import async_session
|
||||
from app.services.module_async_recovery_service import recover_module_async_tasks_once
|
||||
from app.tasks.async_runner import run_async
|
||||
from app.tasks.celery_app import celery_app
|
||||
|
||||
|
||||
async def _run_recover_module_async_tasks_once() -> dict[str, Any]:
|
||||
async with async_session() as db:
|
||||
return await recover_module_async_tasks_once(db)
|
||||
|
||||
|
||||
if celery_app:
|
||||
|
||||
@celery_app.task(name="module_async.recover_module_async_tasks_once")
|
||||
def recover_module_async_tasks_once_task() -> dict[str, Any]:
|
||||
return run_async(_run_recover_module_async_tasks_once())
|
||||
|
||||
else:
|
||||
|
||||
class _DisabledTask:
|
||||
def delay(self, *args: Any, **kwargs: Any) -> None:
|
||||
raise RuntimeError("Celery is disabled")
|
||||
|
||||
def apply_async(self, *args: Any, **kwargs: Any) -> None:
|
||||
raise RuntimeError("Celery is disabled")
|
||||
|
||||
recover_module_async_tasks_once_task = _DisabledTask()
|
||||
@@ -1,21 +1,76 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.enums.shot_replicate import ModuleCodeEnum, ShotReplicateStepCodeEnum
|
||||
from app.models.base import async_session
|
||||
from app.services.module_async_recovery_service import (
|
||||
OBJECT_MODULE_STEP,
|
||||
TASK_SHOT_IMAGE_PROMPT,
|
||||
TASK_SHOT_VIDEO_PROMPT,
|
||||
acquire_object_lock,
|
||||
cleanup_active_if_terminal,
|
||||
mark_active_started,
|
||||
release_object_lock,
|
||||
register_module_step_task,
|
||||
)
|
||||
from app.services.shot_replicate_flow_service import run_image_prompt_optimize, run_video_prompt_optimize
|
||||
from app.tasks.async_runner import run_async
|
||||
from app.tasks.celery_app import celery_app
|
||||
|
||||
MODULE = ModuleCodeEnum.SHOT_REPLICATE.value
|
||||
|
||||
|
||||
async def _run_image_prompt(project_id: str, step_id: str | None = None):
|
||||
async with async_session() as db:
|
||||
await run_image_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
lock_token: str | None = None
|
||||
if step_id:
|
||||
lock_token = await acquire_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
if not lock_token:
|
||||
return None
|
||||
await register_module_step_task(
|
||||
module=MODULE,
|
||||
project_id=project_id,
|
||||
step_id=step_id,
|
||||
step_code=ShotReplicateStepCodeEnum.IMAGE_PROMPT_OPTIMIZE.value,
|
||||
task_name=TASK_SHOT_IMAGE_PROMPT,
|
||||
)
|
||||
await mark_active_started(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
try:
|
||||
async with async_session() as db:
|
||||
result = await run_image_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
if step_id:
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
return result
|
||||
finally:
|
||||
if step_id:
|
||||
await release_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id, token=lock_token)
|
||||
|
||||
|
||||
async def _run_video_prompt(project_id: str, step_id: str | None = None):
|
||||
async with async_session() as db:
|
||||
await run_video_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
lock_token: str | None = None
|
||||
if step_id:
|
||||
lock_token = await acquire_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
if not lock_token:
|
||||
return None
|
||||
await register_module_step_task(
|
||||
module=MODULE,
|
||||
project_id=project_id,
|
||||
step_id=step_id,
|
||||
step_code=ShotReplicateStepCodeEnum.VIDEO_PROMPT_OPTIMIZE.value,
|
||||
task_name=TASK_SHOT_VIDEO_PROMPT,
|
||||
)
|
||||
await mark_active_started(object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
try:
|
||||
async with async_session() as db:
|
||||
result = await run_video_prompt_optimize(db, project_id=project_id, step_id=step_id)
|
||||
await db.commit()
|
||||
if step_id:
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_MODULE_STEP, object_id=step_id)
|
||||
return result
|
||||
finally:
|
||||
if step_id:
|
||||
await release_object_lock(object_type=OBJECT_MODULE_STEP, object_id=step_id, token=lock_token)
|
||||
|
||||
|
||||
if celery_app:
|
||||
@@ -32,10 +87,10 @@ if celery_app:
|
||||
else:
|
||||
|
||||
class _DisabledTask:
|
||||
def delay(self, *args, **kwargs):
|
||||
def delay(self, *args: Any, **kwargs: Any):
|
||||
raise RuntimeError("Celery is disabled")
|
||||
|
||||
def apply_async(self, *args, **kwargs):
|
||||
def apply_async(self, *args: Any, **kwargs: Any):
|
||||
raise RuntimeError("Celery is disabled")
|
||||
|
||||
start_image_prompt_optimize = _DisabledTask()
|
||||
|
||||
@@ -20,6 +20,20 @@ from app.models.shot_replicate_segment import ShotReplicateSegment
|
||||
from app.models.shot_replicate_task_set import ShotReplicateTaskSet
|
||||
from app.services.module_generation_log_service import log_module_error, log_module_event_file, log_module_prompt_event
|
||||
from app.services.redis_registry_service import redis_acquire_lock, redis_release_lock
|
||||
from app.services.module_async_recovery_service import (
|
||||
OBJECT_SHOT_SEGMENT_ANALYSIS,
|
||||
OBJECT_SHOT_SPLIT_SEGMENT,
|
||||
OBJECT_SHOT_TASK_SET_ANALYSIS,
|
||||
acquire_object_lock,
|
||||
cleanup_active_if_terminal,
|
||||
mark_active_started,
|
||||
postpone_active_task,
|
||||
register_shot_segment_analysis_task,
|
||||
register_shot_split_task,
|
||||
register_shot_task_set_analysis_task,
|
||||
release_object_lock,
|
||||
remove_active_task,
|
||||
)
|
||||
from app.services.shot_replicate_taskset_service import refresh_task_set_split_summary
|
||||
from app.services.shot_video_analysis_service import analyze_video_for_shot_split
|
||||
from app.services.shot_video_split_service import split_video_segment_async
|
||||
@@ -65,6 +79,12 @@ async def _release_split_semaphore(lock_key: str | None, segment_id: str) -> Non
|
||||
|
||||
|
||||
async def _run_analyze_original_video(task_set_id: str) -> None:
|
||||
lock_token = await acquire_object_lock(object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
if not lock_token:
|
||||
return
|
||||
await register_shot_task_set_analysis_task(task_set_id)
|
||||
await mark_active_started(object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
|
||||
task_set_user_id: str | None = None
|
||||
video_url: str | None = None
|
||||
try:
|
||||
@@ -77,8 +97,10 @@ async def _run_analyze_original_video(task_set_id: str) -> None:
|
||||
)
|
||||
task_set = result.scalar_one_or_none()
|
||||
if not task_set:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
return
|
||||
if task_set.analysis_status == ShotAnalysisStatusEnum.COMPLETED.value:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
return
|
||||
task_set_user_id = task_set.user_id
|
||||
video_url = task_set.video_url
|
||||
@@ -107,6 +129,7 @@ async def _run_analyze_original_video(task_set_id: str) -> None:
|
||||
task_set = result.scalar_one_or_none()
|
||||
if not task_set:
|
||||
await db.rollback()
|
||||
await remove_active_task(object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
return
|
||||
result_json = analyzed.result
|
||||
task_set.original_video_content = str(result_json.get("原视频内容") or "无")
|
||||
@@ -119,6 +142,7 @@ async def _run_analyze_original_video(task_set_id: str) -> None:
|
||||
task_set.status = ShotTaskSetStatusEnum.ANALYSIS_COMPLETED.value
|
||||
task_set.analysis_error_message = None
|
||||
await db.commit()
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
|
||||
log_module_prompt_event(
|
||||
event_type="SHOT_ANALYSIS_SUCCESS",
|
||||
@@ -154,6 +178,9 @@ async def _run_analyze_original_video(task_set_id: str) -> None:
|
||||
task_set.analysis_status = ShotAnalysisStatusEnum.FAILED.value
|
||||
task_set.analysis_error_message = str(exc)
|
||||
await db.commit()
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
else:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id)
|
||||
log_module_error(
|
||||
module=MODULE,
|
||||
event_type="SHOT_ANALYSIS_FAILED",
|
||||
@@ -163,9 +190,17 @@ async def _run_analyze_original_video(task_set_id: str) -> None:
|
||||
detail={"task_set_id": task_set_id, "video_url": video_url, "analysis_mode": "full_breakdown"},
|
||||
exc=exc,
|
||||
)
|
||||
finally:
|
||||
await release_object_lock(object_type=OBJECT_SHOT_TASK_SET_ANALYSIS, object_id=task_set_id, token=lock_token)
|
||||
|
||||
|
||||
async def _run_analyze_custom_segment_video(segment_id: str) -> None:
|
||||
lock_token = await acquire_object_lock(object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
if not lock_token:
|
||||
return
|
||||
await register_shot_segment_analysis_task(segment_id)
|
||||
await mark_active_started(object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
|
||||
user_id: str | None = None
|
||||
task_set_id: str | None = None
|
||||
video_url: str | None = None
|
||||
@@ -179,12 +214,15 @@ async def _run_analyze_custom_segment_video(segment_id: str) -> None:
|
||||
)
|
||||
segment = result.scalar_one_or_none()
|
||||
if not segment or not segment.segment_video_url:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
return
|
||||
if segment.analysis_status == ShotSegmentAnalysisStatusEnum.COMPLETED.value:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
return
|
||||
user_id = segment.user_id
|
||||
task_set_id = segment.task_set_id
|
||||
video_url = segment.segment_video_url
|
||||
await register_shot_segment_analysis_task(segment_id, task_set_id=task_set_id)
|
||||
segment.analysis_status = ShotSegmentAnalysisStatusEnum.PROCESSING.value
|
||||
segment.analysis_error_message = None
|
||||
await db.commit()
|
||||
@@ -210,6 +248,7 @@ async def _run_analyze_custom_segment_video(segment_id: str) -> None:
|
||||
segment = result.scalar_one_or_none()
|
||||
if not segment:
|
||||
await db.rollback()
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
return
|
||||
result_json = analyzed.result
|
||||
segment.original_video_content = str(result_json.get("原视频内容") or "无")
|
||||
@@ -222,10 +261,11 @@ async def _run_analyze_custom_segment_video(segment_id: str) -> None:
|
||||
segment.analysis_status = ShotSegmentAnalysisStatusEnum.COMPLETED.value
|
||||
segment.analysis_error_message = None
|
||||
await db.commit()
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
|
||||
log_module_prompt_event(
|
||||
event_type="SHOT_SEGMENT_ANALYSIS_SUCCESS",
|
||||
project_id=task_set_id or segment_id,
|
||||
project_id=task_set_id,
|
||||
step_id=segment_id,
|
||||
user_id=user_id or "",
|
||||
module=MODULE,
|
||||
@@ -258,6 +298,9 @@ async def _run_analyze_custom_segment_video(segment_id: str) -> None:
|
||||
segment.analysis_status = ShotSegmentAnalysisStatusEnum.FAILED.value
|
||||
segment.analysis_error_message = str(exc)
|
||||
await db.commit()
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
else:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id)
|
||||
log_module_error(
|
||||
module=MODULE,
|
||||
event_type="SHOT_SEGMENT_ANALYSIS_FAILED",
|
||||
@@ -268,6 +311,8 @@ async def _run_analyze_custom_segment_video(segment_id: str) -> None:
|
||||
detail={"segment_id": segment_id, "task_set_id": task_set_id, "video_url": video_url, "analysis_mode": "summary_only"},
|
||||
exc=exc,
|
||||
)
|
||||
finally:
|
||||
await release_object_lock(object_type=OBJECT_SHOT_SEGMENT_ANALYSIS, object_id=segment_id, token=lock_token)
|
||||
|
||||
|
||||
async def _run_split_one_segment(segment_id: str) -> None:
|
||||
@@ -280,6 +325,9 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
if not segment_lock_token:
|
||||
return
|
||||
|
||||
await register_shot_split_task(segment_id)
|
||||
await mark_active_started(object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
|
||||
semaphore_key: str | None = None
|
||||
user_id: str | None = None
|
||||
task_set_id: str | None = None
|
||||
@@ -294,8 +342,15 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
message="拆镜 ffmpeg 并发闸门已满,稍后重试",
|
||||
detail={"segment_id": segment_id, "reason": "semaphore_full"},
|
||||
)
|
||||
delay = max(1, int(settings.MODULE_ASYNC_REQUEUE_DELAY_SECONDS or 10))
|
||||
await postpone_active_task(
|
||||
object_type=OBJECT_SHOT_SPLIT_SEGMENT,
|
||||
object_id=segment_id,
|
||||
delay_seconds=delay,
|
||||
reason="semaphore_full",
|
||||
)
|
||||
if celery_app:
|
||||
split_one_segment.apply_async(args=[segment_id], queue=SPLIT_QUEUE, countdown=10, priority=settings.DOWNLOAD_TASK_PRIORITY_NORMAL)
|
||||
split_one_segment.apply_async(args=[segment_id], queue=SPLIT_QUEUE, countdown=delay, priority=settings.DOWNLOAD_TASK_PRIORITY_NORMAL)
|
||||
return
|
||||
|
||||
async with async_session() as db:
|
||||
@@ -307,6 +362,7 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
)
|
||||
segment = result.scalar_one_or_none()
|
||||
if not segment:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
return
|
||||
user_id = segment.user_id
|
||||
task_set_id = segment.task_set_id
|
||||
@@ -318,8 +374,10 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
)
|
||||
task_set = task_set_result.scalar_one_or_none()
|
||||
if not task_set:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
return
|
||||
if segment.split_status == ShotSplitStatusEnum.COMPLETED.value and segment.segment_video_url:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
return
|
||||
|
||||
validate_split_range(
|
||||
@@ -338,6 +396,8 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
task_set.status = ShotTaskSetStatusEnum.SPLITTING.value
|
||||
task_set.split_status = ShotSplitStatusEnum.PROCESSING.value
|
||||
await db.commit()
|
||||
await register_shot_split_task(segment_id, task_set_id=segment.task_set_id)
|
||||
await mark_active_started(object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
|
||||
source_path = task_set.video_path
|
||||
date_dir = (segment.created_at or now).strftime("%Y/%m/%d")
|
||||
@@ -379,6 +439,7 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
)
|
||||
segment = result.scalar_one_or_none()
|
||||
if not segment:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
return
|
||||
segment.segment_video_url = split_result.url
|
||||
segment.segment_video_path = split_result.path
|
||||
@@ -389,6 +450,7 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
segment.split_last_error = None
|
||||
await refresh_task_set_split_summary(db, segment.task_set_id)
|
||||
await db.commit()
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
|
||||
log_module_event_file(
|
||||
module=MODULE,
|
||||
@@ -407,6 +469,7 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
)
|
||||
|
||||
if segment.source_mode == ShotSegmentSourceModeEnum.CUSTOM.value and celery_app:
|
||||
await register_shot_segment_analysis_task(segment.id, task_set_id=segment.task_set_id)
|
||||
analyze_custom_segment_video.apply_async(args=[segment.id], queue=ANALYSIS_QUEUE, countdown=0)
|
||||
|
||||
except Exception as exc:
|
||||
@@ -421,6 +484,7 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
)
|
||||
segment = result.scalar_one_or_none()
|
||||
if not segment:
|
||||
await remove_active_task(object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
return
|
||||
user_id = user_id or segment.user_id
|
||||
task_set_id = task_set_id or segment.task_set_id
|
||||
@@ -437,9 +501,18 @@ async def _run_split_one_segment(segment_id: str) -> None:
|
||||
await refresh_task_set_split_summary(db, segment.task_set_id)
|
||||
await db.commit()
|
||||
|
||||
if segment.split_status == ShotSplitStatusEnum.RETRY_WAITING.value and celery_app:
|
||||
if segment.split_status == ShotSplitStatusEnum.RETRY_WAITING.value:
|
||||
next_retry_delay = max(1, int(((segment.split_next_retry_at or _now()) - _now()).total_seconds()))
|
||||
split_one_segment.apply_async(args=[segment_id], queue=SPLIT_QUEUE, countdown=next_retry_delay, priority=settings.DOWNLOAD_TASK_PRIORITY_RECOVER)
|
||||
await postpone_active_task(
|
||||
object_type=OBJECT_SHOT_SPLIT_SEGMENT,
|
||||
object_id=segment_id,
|
||||
delay_seconds=next_retry_delay,
|
||||
reason="split_retry_waiting",
|
||||
)
|
||||
if celery_app:
|
||||
split_one_segment.apply_async(args=[segment_id], queue=SPLIT_QUEUE, countdown=next_retry_delay, priority=settings.DOWNLOAD_TASK_PRIORITY_RECOVER)
|
||||
elif final_failed:
|
||||
await cleanup_active_if_terminal(db, object_type=OBJECT_SHOT_SPLIT_SEGMENT, object_id=segment_id)
|
||||
|
||||
log_module_error(
|
||||
module=MODULE,
|
||||
|
||||
Reference in New Issue
Block a user