@danhachuel/thunderbolt 0.4.74 → 0.4.76
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 +66 -3
- package/hermes_ui/update_manager.py +3 -2
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -4371,6 +4371,22 @@ def _task_thumbnail_path(task: dict[str, Any]) -> Path | None:
|
|
|
4371
4371
|
return None
|
|
4372
4372
|
|
|
4373
4373
|
|
|
4374
|
+
def _task_artifact_path(task: dict[str, Any], *names: str) -> Path | None:
|
|
4375
|
+
"""Resolve um artefacto local, incluindo caminhos relativos e legados."""
|
|
4376
|
+
artifacts = task.get("artifacts") if isinstance(task.get("artifacts"), dict) else {}
|
|
4377
|
+
for name in names:
|
|
4378
|
+
raw = str(artifacts.get(name) or "").strip()
|
|
4379
|
+
if not raw or raw.startswith(("http://", "https://")):
|
|
4380
|
+
continue
|
|
4381
|
+
candidates = [Path(raw)]
|
|
4382
|
+
if not Path(raw).is_absolute():
|
|
4383
|
+
candidates.extend([STORAGE / raw, STORAGE / "videos" / raw])
|
|
4384
|
+
for candidate in candidates:
|
|
4385
|
+
if candidate.is_file():
|
|
4386
|
+
return candidate
|
|
4387
|
+
return None
|
|
4388
|
+
|
|
4389
|
+
|
|
4374
4390
|
def _is_music_task(task: dict[str, Any]) -> bool:
|
|
4375
4391
|
"""Identify music pipeline tasks without conflating them with ordinary video tasks."""
|
|
4376
4392
|
return bool(task.get("music_mode")) or str(task.get("style_wide") or task.get("style") or "").strip().casefold() in {"music", "música"}
|
|
@@ -4921,15 +4937,41 @@ def render_automation():
|
|
|
4921
4937
|
st.info("Ainda não existem vídeos cadastrados.")
|
|
4922
4938
|
for task in tasks:
|
|
4923
4939
|
with st.container(border=True):
|
|
4924
|
-
task_cols = st.columns([2.25, 1.
|
|
4940
|
+
task_cols = st.columns([2.25, 1.55, 1.05, 2.15], gap="small")
|
|
4941
|
+
script_path = _task_artifact_path(task, "script")
|
|
4942
|
+
video_path = _task_artifact_path(task, "video")
|
|
4943
|
+
thumbnail_path = _task_thumbnail_path(task)
|
|
4944
|
+
thumbnail_prompt_path = _task_artifact_path(task, "thumbnail_prompt_json")
|
|
4945
|
+
thumbnail_prompt = str(task.get("thumbnail_prompt") or "").strip()
|
|
4925
4946
|
with task_cols[0]:
|
|
4926
|
-
thumbnail_path = _task_thumbnail_path(task)
|
|
4927
4947
|
if thumbnail_path:
|
|
4928
|
-
st.image(thumbnail_path, width=180, caption="Thumbnail")
|
|
4948
|
+
st.image(str(thumbnail_path), width=180, caption="Thumbnail")
|
|
4929
4949
|
else:
|
|
4930
4950
|
st.caption("Thumbnail ainda não pronta")
|
|
4931
4951
|
st.write(f"**{task.get('topic', 'Sem tópico')}**")
|
|
4932
4952
|
st.caption(f"{task.get('channel_name', 'Canal')} · {task.get('id', '')}")
|
|
4953
|
+
thumbnail_download_col, prompt_download_col = st.columns(2, gap="small")
|
|
4954
|
+
with thumbnail_download_col:
|
|
4955
|
+
st.download_button(
|
|
4956
|
+
"Baixar Thumbnail",
|
|
4957
|
+
data=thumbnail_path.read_bytes() if thumbnail_path else b"",
|
|
4958
|
+
file_name=thumbnail_path.name if thumbnail_path else "thumbnail.png",
|
|
4959
|
+
mime="image/png",
|
|
4960
|
+
key=f"automation_download_thumbnail_{task['id']}",
|
|
4961
|
+
use_container_width=True,
|
|
4962
|
+
disabled=thumbnail_path is None,
|
|
4963
|
+
)
|
|
4964
|
+
with prompt_download_col:
|
|
4965
|
+
prompt_data = thumbnail_prompt_path.read_bytes() if thumbnail_prompt_path else thumbnail_prompt.encode("utf-8")
|
|
4966
|
+
st.download_button(
|
|
4967
|
+
"Baixar Thumbnail Prompt",
|
|
4968
|
+
data=prompt_data,
|
|
4969
|
+
file_name=thumbnail_prompt_path.name if thumbnail_prompt_path else "thumbnail-prompt.txt",
|
|
4970
|
+
mime="application/json" if thumbnail_prompt_path else "text/plain",
|
|
4971
|
+
key=f"automation_download_thumbnail_prompt_{task['id']}",
|
|
4972
|
+
use_container_width=True,
|
|
4973
|
+
disabled=thumbnail_prompt_path is None and not thumbnail_prompt,
|
|
4974
|
+
)
|
|
4933
4975
|
with task_cols[1]:
|
|
4934
4976
|
_render_video_task_state(task)
|
|
4935
4977
|
with task_cols[2]:
|
|
@@ -4949,6 +4991,27 @@ def render_automation():
|
|
|
4949
4991
|
if st.button("Stop", key=f"automation_stop_{task['id']}", use_container_width=True, disabled=state != "doing"):
|
|
4950
4992
|
stop_task_by_user(task["id"])
|
|
4951
4993
|
st.rerun()
|
|
4994
|
+
script_download_col, video_download_col = st.columns(2, gap="small")
|
|
4995
|
+
with script_download_col:
|
|
4996
|
+
st.download_button(
|
|
4997
|
+
"Baixar Roteiro",
|
|
4998
|
+
data=script_path.read_bytes() if script_path else b"",
|
|
4999
|
+
file_name=script_path.name if script_path else "roteiro.md",
|
|
5000
|
+
mime="text/markdown",
|
|
5001
|
+
key=f"automation_download_script_{task['id']}",
|
|
5002
|
+
use_container_width=True,
|
|
5003
|
+
disabled=script_path is None,
|
|
5004
|
+
)
|
|
5005
|
+
with video_download_col:
|
|
5006
|
+
st.download_button(
|
|
5007
|
+
"Baixar Vídeo",
|
|
5008
|
+
data=video_path.read_bytes() if video_path else b"",
|
|
5009
|
+
file_name=video_path.name if video_path else "video.mp4",
|
|
5010
|
+
mime="video/mp4",
|
|
5011
|
+
key=f"automation_download_video_{task['id']}",
|
|
5012
|
+
use_container_width=True,
|
|
5013
|
+
disabled=video_path is None,
|
|
5014
|
+
)
|
|
4952
5015
|
with delete_col:
|
|
4953
5016
|
confirm_delete_key = f"automation_confirm_delete_{task['id']}"
|
|
4954
5017
|
if st.button("Apagar", key=f"automation_delete_{task['id']}", use_container_width=True, disabled=state == "doing"):
|
|
@@ -44,10 +44,11 @@ class UpdateResult:
|
|
|
44
44
|
restart_required: bool = False
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def restart_current_process(*, exec_fn: Callable[..., Any] = os.execv) -> None:
|
|
47
|
+
def restart_current_process(*, exec_fn: Callable[..., Any] = os.execv, exit_fn: Callable[[int], Any] = os._exit) -> None:
|
|
48
48
|
"""Ask the launcher to restart Streamlit, or re-exec standalone callers."""
|
|
49
49
|
if os.environ.get("THUNDERBOLT_LAUNCHER_RESTART") == "1":
|
|
50
|
-
|
|
50
|
+
exit_fn(LAUNCHER_RESTART_EXIT_CODE)
|
|
51
|
+
return
|
|
51
52
|
executable = sys.executable
|
|
52
53
|
exec_fn(executable, [executable, *sys.argv])
|
|
53
54
|
|
package/package.json
CHANGED