rev 4
This commit is contained in:
+192
-12
@@ -96,6 +96,10 @@ MESSAGE_COMPLETED = "Завершено"
|
||||
MESSAGE_PROBING_MEDIA = "Анализ медиафайла"
|
||||
MESSAGE_PROCESSING = "Обработка"
|
||||
MESSAGE_CONVERTING = "Конвертация"
|
||||
MESSAGE_PREPARING_FILE = "Подготовка файла"
|
||||
MESSAGE_CONVERTING_VIDEO = "Конвертация видео"
|
||||
MESSAGE_UPLOADING_RESULT = "Загрузка результата"
|
||||
MESSAGE_SAVING_RESULT = "Сохранение результата"
|
||||
MESSAGE_RENDERING_CAPTIONS = "Рендеринг субтитров"
|
||||
MESSAGE_CANCELLED = "Отменено пользователем"
|
||||
MESSAGE_EXTRACTING_FRAMES = "Извлечение кадров"
|
||||
@@ -106,6 +110,14 @@ PROGRESS_COMPLETE = 100.0
|
||||
PROGRESS_MEDIA_PROBE = 50.0
|
||||
PROGRESS_SILENCE_REMOVE = 30.0
|
||||
PROGRESS_MEDIA_CONVERT = 30.0
|
||||
PROGRESS_MEDIA_CONVERT_PREPARING = 5.0
|
||||
PROGRESS_MEDIA_CONVERT_START = 10.0
|
||||
PROGRESS_MEDIA_CONVERT_END = 95.0
|
||||
PROGRESS_MEDIA_CONVERT_SAVING = 99.0
|
||||
PROGRESS_SILENCE_APPLY_PREPARING = 5.0
|
||||
PROGRESS_SILENCE_APPLY_START = 10.0
|
||||
PROGRESS_SILENCE_APPLY_END = 95.0
|
||||
PROGRESS_SILENCE_APPLY_SAVING = 99.0
|
||||
PROGRESS_TRANSCRIPTION_START = 20.0
|
||||
PROGRESS_TRANSCRIPTION_END = 95.0
|
||||
PROGRESS_CAPTIONS = 30.0
|
||||
@@ -119,6 +131,7 @@ MESSAGE_DETECTING_SILENCE = "Обнаружение тишины"
|
||||
MESSAGE_APPLYING_CUTS = "Применение вырезок"
|
||||
|
||||
PROGRESS_THROTTLE_SECONDS = 3.0
|
||||
PROGRESS_CONVERT_THROTTLE_SECONDS = 1.0
|
||||
|
||||
ACTIVE_JOB_STATUSES = (JOB_STATUS_PENDING, JOB_STATUS_RUNNING)
|
||||
DRAMATIQ_BROKER_REF_SEPARATOR = ":"
|
||||
@@ -481,20 +494,56 @@ def silence_apply_actor(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
status=JOB_STATUS_RUNNING,
|
||||
current_message=MESSAGE_STARTING,
|
||||
current_message=MESSAGE_PREPARING_FILE,
|
||||
progress_pct=PROGRESS_SILENCE_APPLY_PREPARING,
|
||||
started_at=_utc_now(),
|
||||
),
|
||||
)
|
||||
|
||||
try:
|
||||
storage = _get_storage_service()
|
||||
_send_webhook_event(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
current_message=MESSAGE_APPLYING_CUTS,
|
||||
progress_pct=PROGRESS_SILENCE_APPLY,
|
||||
),
|
||||
)
|
||||
last_report_time = 0.0
|
||||
last_progress = PROGRESS_SILENCE_APPLY_PREPARING
|
||||
|
||||
def _emit_silence_apply_progress(stage: str, pct: float | None) -> None:
|
||||
nonlocal last_report_time, last_progress
|
||||
|
||||
if stage == "applying_cuts":
|
||||
raw_pct = min(max(pct or 0.0, 0.0), 100.0)
|
||||
if raw_pct >= 100.0:
|
||||
return
|
||||
mapped = PROGRESS_SILENCE_APPLY_START + (raw_pct / 100.0) * (
|
||||
PROGRESS_SILENCE_APPLY_END - PROGRESS_SILENCE_APPLY_START
|
||||
)
|
||||
message = MESSAGE_APPLYING_CUTS
|
||||
force = raw_pct == 0.0
|
||||
elif stage == "uploading":
|
||||
mapped = PROGRESS_SILENCE_APPLY_END
|
||||
message = MESSAGE_UPLOADING_RESULT
|
||||
force = True
|
||||
else:
|
||||
return
|
||||
|
||||
mapped = round(mapped, 1)
|
||||
now = time.monotonic()
|
||||
if not force:
|
||||
if mapped <= last_progress:
|
||||
return
|
||||
if mapped - last_progress < 1.0:
|
||||
return
|
||||
if now - last_report_time < PROGRESS_CONVERT_THROTTLE_SECONDS:
|
||||
return
|
||||
|
||||
last_report_time = now
|
||||
last_progress = max(last_progress, mapped)
|
||||
_send_webhook_event(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
current_message=message,
|
||||
progress_pct=last_progress,
|
||||
),
|
||||
)
|
||||
|
||||
result = _run_async(
|
||||
apply_silence_cuts(
|
||||
storage,
|
||||
@@ -502,8 +551,16 @@ def silence_apply_actor(
|
||||
out_folder=out_folder,
|
||||
cuts=cuts,
|
||||
output_name=output_name,
|
||||
on_progress=_emit_silence_apply_progress,
|
||||
)
|
||||
)
|
||||
_send_webhook_event(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
current_message=MESSAGE_SAVING_RESULT,
|
||||
progress_pct=PROGRESS_SILENCE_APPLY_SAVING,
|
||||
),
|
||||
)
|
||||
_send_webhook_event(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
@@ -554,7 +611,8 @@ def media_convert_actor(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
status=JOB_STATUS_RUNNING,
|
||||
current_message=MESSAGE_STARTING,
|
||||
current_message=MESSAGE_PREPARING_FILE,
|
||||
progress_pct=PROGRESS_MEDIA_CONVERT_PREPARING,
|
||||
started_at=_utc_now(),
|
||||
),
|
||||
)
|
||||
@@ -564,14 +622,63 @@ def media_convert_actor(
|
||||
raise ValueError(f"Неподдерживаемый формат: {output_format}")
|
||||
|
||||
storage = _get_storage_service()
|
||||
last_report_time = 0.0
|
||||
last_progress = PROGRESS_MEDIA_CONVERT_PREPARING
|
||||
|
||||
def _emit_convert_progress(stage: str, pct: float | None) -> None:
|
||||
nonlocal last_report_time, last_progress
|
||||
|
||||
if stage == "converting":
|
||||
raw_pct = min(max(pct or 0.0, 0.0), 100.0)
|
||||
if raw_pct >= 100.0:
|
||||
return
|
||||
mapped = PROGRESS_MEDIA_CONVERT_START + (raw_pct / 100.0) * (
|
||||
PROGRESS_MEDIA_CONVERT_END - PROGRESS_MEDIA_CONVERT_START
|
||||
)
|
||||
message = MESSAGE_CONVERTING_VIDEO
|
||||
force = raw_pct == 0.0
|
||||
elif stage == "uploading":
|
||||
mapped = PROGRESS_MEDIA_CONVERT_END
|
||||
message = MESSAGE_UPLOADING_RESULT
|
||||
force = True
|
||||
else:
|
||||
return
|
||||
|
||||
mapped = round(mapped, 1)
|
||||
now = time.monotonic()
|
||||
if not force:
|
||||
if mapped <= last_progress:
|
||||
return
|
||||
if mapped - last_progress < 1.0:
|
||||
return
|
||||
if now - last_report_time < PROGRESS_CONVERT_THROTTLE_SECONDS:
|
||||
return
|
||||
|
||||
last_report_time = now
|
||||
last_progress = max(last_progress, mapped)
|
||||
_send_webhook_event(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
current_message=message,
|
||||
progress_pct=last_progress,
|
||||
),
|
||||
)
|
||||
|
||||
result = _run_async(
|
||||
convert_to_mp4(
|
||||
storage,
|
||||
file_key=file_key,
|
||||
out_folder=out_folder,
|
||||
on_progress=_emit_convert_progress,
|
||||
)
|
||||
)
|
||||
_send_webhook_event(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
current_message=MESSAGE_CONVERTING,
|
||||
progress_pct=PROGRESS_MEDIA_CONVERT,
|
||||
current_message=MESSAGE_SAVING_RESULT,
|
||||
progress_pct=PROGRESS_MEDIA_CONVERT_SAVING,
|
||||
),
|
||||
)
|
||||
result = _run_async(convert_to_mp4(storage, file_key=file_key, out_folder=out_folder))
|
||||
_send_webhook_event(
|
||||
webhook_url,
|
||||
TaskWebhookEvent(
|
||||
@@ -1213,6 +1320,12 @@ class TaskService:
|
||||
except Exception:
|
||||
logger.exception("Failed to save convert artifacts for job %s", job_id)
|
||||
|
||||
if job.job_type == JOB_TYPE_SILENCE_APPLY and event.status == JOB_STATUS_DONE:
|
||||
try:
|
||||
await self._save_silence_apply_artifacts(job)
|
||||
except Exception:
|
||||
logger.exception("Failed to save silence apply artifacts for job %s", job_id)
|
||||
|
||||
if job.job_type == JOB_TYPE_CAPTIONS_GENERATE and event.status == JOB_STATUS_DONE:
|
||||
try:
|
||||
await self._save_captions_artifacts(job)
|
||||
@@ -1427,8 +1540,75 @@ class TaskService:
|
||||
),
|
||||
)
|
||||
|
||||
updated_output = dict(output_data)
|
||||
updated_output["file_id"] = str(converted_file.id)
|
||||
await self._job_repo.update(job, JobUpdate(output_data=updated_output))
|
||||
|
||||
logger.info("Saved convert artifacts for job %s", job.id)
|
||||
|
||||
async def _save_silence_apply_artifacts(self, job: Job) -> None:
|
||||
"""Create File and ArtifactMediaFile records for silence-applied video."""
|
||||
input_data = job.input_data or {}
|
||||
output_data = job.output_data or {}
|
||||
|
||||
file_key: str = input_data["file_key"]
|
||||
project_id: uuid.UUID | None = (
|
||||
uuid.UUID(input_data["project_id"]) if input_data.get("project_id") else None
|
||||
)
|
||||
|
||||
file_path: str = output_data["file_path"]
|
||||
file_size: int = output_data.get("file_size", 0)
|
||||
|
||||
user_repo = UserRepository(self._session)
|
||||
user = await user_repo.get_by_id(job.user_id) # type: ignore[arg-type]
|
||||
if user is None:
|
||||
logger.warning(
|
||||
"User %s not found, skipping silence apply artifact save", job.user_id
|
||||
)
|
||||
return
|
||||
|
||||
file_repo = FileRepository(self._session)
|
||||
source_file = await file_repo.get_by_path(file_key)
|
||||
if source_file is not None:
|
||||
stem = Path(source_file.original_filename).stem
|
||||
else:
|
||||
stem = Path(file_key).stem
|
||||
processed_filename = f"Видео без тишины {stem}.mp4"
|
||||
|
||||
processed_file = await file_repo.create(
|
||||
requester=user,
|
||||
data=FileCreate(
|
||||
project_id=project_id,
|
||||
original_filename=processed_filename,
|
||||
path=file_path,
|
||||
storage_backend="S3",
|
||||
mime_type="video/mp4",
|
||||
size_bytes=file_size,
|
||||
file_format="mp4",
|
||||
is_uploaded=True,
|
||||
),
|
||||
)
|
||||
|
||||
artifact_repo = ArtifactRepository(self._session)
|
||||
await artifact_repo.create(
|
||||
data=ArtifactMediaFileCreate(
|
||||
project_id=project_id,
|
||||
file_id=processed_file.id,
|
||||
media_file_id=None,
|
||||
artifact_type="SILENCE_REMOVED_VIDEO",
|
||||
),
|
||||
)
|
||||
|
||||
updated_output = dict(output_data)
|
||||
updated_output["file_id"] = str(processed_file.id)
|
||||
await self._job_repo.update(job, JobUpdate(output_data=updated_output))
|
||||
|
||||
logger.info(
|
||||
"Saved silence apply artifacts for job %s (file_id=%s)",
|
||||
job.id,
|
||||
processed_file.id,
|
||||
)
|
||||
|
||||
async def _save_captions_artifacts(self, job: Job) -> None:
|
||||
"""Create File and ArtifactMediaFile records for captioned video."""
|
||||
input_data = job.input_data or {}
|
||||
|
||||
Reference in New Issue
Block a user