@danhachuel/thunderbolt 0.3.83 → 0.3.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/main.py +1 -1
- package/hermes_ui/pipeline_worker.py +39 -1
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -3505,7 +3505,7 @@ def _render_pipeline_worker_banner(worker_status: dict[str, Any], active_count:
|
|
|
3505
3505
|
st.warning("Worker de vídeo sem heartbeat recente. O launcher deve estar aberto para processar as tarefas.")
|
|
3506
3506
|
heartbeat = worker_status.get("last_heartbeat_at") or worker_status.get("updated_at")
|
|
3507
3507
|
if heartbeat:
|
|
3508
|
-
st.caption(f"{_pipeline_time_age(heartbeat)} ·
|
|
3508
|
+
st.caption(f"{_pipeline_time_age(heartbeat)} · vídeo: até 90 min com watchdog de inactividade de 10 min")
|
|
3509
3509
|
if worker_status.get("last_error"):
|
|
3510
3510
|
st.error(f"Último erro do worker: {worker_status['last_error']}")
|
|
3511
3511
|
|
|
@@ -28,7 +28,8 @@ from hermes_ui.thumbnail_generation import ThumbnailGenerationError, generate_th
|
|
|
28
28
|
PIPELINE_LOCK_FILENAME = "pipeline_worker.lock"
|
|
29
29
|
PIPELINE_LOG_FILENAME = "pipeline_worker.json"
|
|
30
30
|
VIDEO_TIMEOUT_SECONDS = 20 * 60
|
|
31
|
-
LONG_STOCK_VIDEO_TIMEOUT_SECONDS =
|
|
31
|
+
LONG_STOCK_VIDEO_TIMEOUT_SECONDS = 90 * 60
|
|
32
|
+
VIDEO_IDLE_TIMEOUT_SECONDS = 10 * 60
|
|
32
33
|
STALE_TASK_SECONDS = VIDEO_TIMEOUT_SECONDS + 5 * 60
|
|
33
34
|
WORKER_HEARTBEAT_TIMEOUT_SECONDS = 15
|
|
34
35
|
CASCADE_STAGE_ORDER = ("topic", "script", "title", "keywords", "video", "thumbnail_prompt", "thumbnail", "upload")
|
|
@@ -636,6 +637,17 @@ def _task_stale_timeout_seconds(task: dict[str, Any]) -> int:
|
|
|
636
637
|
return STALE_TASK_SECONDS
|
|
637
638
|
|
|
638
639
|
|
|
640
|
+
def _latest_helper_log_activity(log_path: Path | None, previous_mtime: float) -> tuple[float, bool]:
|
|
641
|
+
"""Return whether the helper's file log advanced without reading its content."""
|
|
642
|
+
if log_path is None:
|
|
643
|
+
return previous_mtime, False
|
|
644
|
+
try:
|
|
645
|
+
current_mtime = log_path.stat().st_mtime
|
|
646
|
+
except OSError:
|
|
647
|
+
return previous_mtime, False
|
|
648
|
+
return (current_mtime, current_mtime > previous_mtime)
|
|
649
|
+
|
|
650
|
+
|
|
639
651
|
def _mpt_aspect_ratio(value: Any, format_value: Any = "") -> str:
|
|
640
652
|
raw = str(value or format_value or "").strip().casefold()
|
|
641
653
|
if raw in {"landscape 16:9", "wide", "16:9", "landscape"}:
|
|
@@ -763,6 +775,11 @@ def _moneyprinter_cli_args(task: dict[str, Any], route: str, settings: dict[str,
|
|
|
763
775
|
clip_duration = generation_settings.get("maximum_clip_duration")
|
|
764
776
|
if str(clip_duration or "").strip().isdigit() and int(clip_duration) > 0:
|
|
765
777
|
args.extend(["--video-clip-duration", str(int(clip_duration))])
|
|
778
|
+
elif route in {"pexels", "pixabay"} and len(script) >= 1_200:
|
|
779
|
+
# Vídeos longos com o padrão de 5 s podem exigir dezenas de downloads
|
|
780
|
+
# sequenciais. Para manter variedade e terminar de forma previsível,
|
|
781
|
+
# adoptamos 15 s apenas quando não há escolha explícita do utilizador.
|
|
782
|
+
args.extend(["--video-clip-duration", "15"])
|
|
766
783
|
if bool(generation_settings.get("match_visuals_to_script_order")):
|
|
767
784
|
args.append("--match-materials-to-script")
|
|
768
785
|
|
|
@@ -915,6 +932,9 @@ def _run_video_helper(task: dict[str, Any]) -> Path:
|
|
|
915
932
|
output_finished = False
|
|
916
933
|
last_heartbeat = 0.0
|
|
917
934
|
last_output_line = ""
|
|
935
|
+
last_activity_at = started_at
|
|
936
|
+
helper_log_path: Path | None = None
|
|
937
|
+
helper_log_mtime = 0.0
|
|
918
938
|
try:
|
|
919
939
|
while True:
|
|
920
940
|
try:
|
|
@@ -924,9 +944,19 @@ def _run_video_helper(task: dict[str, Any]) -> Path:
|
|
|
924
944
|
elif line:
|
|
925
945
|
last_output_line = _redact_helper_output(line).strip()
|
|
926
946
|
output_lines.append(line)
|
|
947
|
+
last_activity_at = time.monotonic()
|
|
948
|
+
match = re.search(r"full generation log:\s*(.+)$", last_output_line, flags=re.IGNORECASE)
|
|
949
|
+
if match:
|
|
950
|
+
candidate = Path(match.group(1).strip()).expanduser()
|
|
951
|
+
helper_log_path = candidate if candidate.is_file() else None
|
|
927
952
|
except queue.Empty:
|
|
928
953
|
pass
|
|
929
954
|
elapsed = time.monotonic() - started_at
|
|
955
|
+
helper_log_mtime, helper_log_advanced = _latest_helper_log_activity(helper_log_path, helper_log_mtime)
|
|
956
|
+
if helper_log_advanced:
|
|
957
|
+
last_activity_at = time.monotonic()
|
|
958
|
+
if not last_output_line:
|
|
959
|
+
last_output_line = "[MoneyPrinterTurbo] actividade de geração confirmada"
|
|
930
960
|
if elapsed - last_heartbeat >= 5:
|
|
931
961
|
# O helper expõe o resultado final, mas não uma percentagem estável.
|
|
932
962
|
# Mantemos uma faixa reservada para a etapa de vídeo e avançamos-a
|
|
@@ -958,6 +988,14 @@ def _run_video_helper(task: dict[str, Any]) -> Path:
|
|
|
958
988
|
message = f"A etapa Vídeo excedeu o limite de {timeout_seconds // 60} minutos e foi encerrada."
|
|
959
989
|
metadata = _failure_attribution(task, settings, "video", error=message)
|
|
960
990
|
raise PipelineError(_failure_message(message, metadata), failure_metadata=metadata)
|
|
991
|
+
if time.monotonic() - last_activity_at >= VIDEO_IDLE_TIMEOUT_SECONDS:
|
|
992
|
+
_stop_process(process)
|
|
993
|
+
message = (
|
|
994
|
+
"A etapa Vídeo não apresentou actividade comprovada do motor durante "
|
|
995
|
+
f"{VIDEO_IDLE_TIMEOUT_SECONDS // 60} minutos e foi encerrada."
|
|
996
|
+
)
|
|
997
|
+
metadata = _failure_attribution(task, settings, "video", error=message)
|
|
998
|
+
raise PipelineError(_failure_message(message, metadata), failure_metadata=metadata)
|
|
961
999
|
finally:
|
|
962
1000
|
reader.join(timeout=2)
|
|
963
1001
|
_persist_video_diagnostics(task, "\n".join(output_lines))
|
package/package.json
CHANGED