@danhachuel/thunderbolt 0.6.73 → 0.6.75
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 +105 -18
- package/package.json +1 -1
package/app/main.py
CHANGED
|
@@ -4969,6 +4969,21 @@ def _task_artifact_path(task: dict[str, Any], *names: str) -> Path | None:
|
|
|
4969
4969
|
return None
|
|
4970
4970
|
|
|
4971
4971
|
|
|
4972
|
+
def _download_title(task: dict[str, Any]) -> str:
|
|
4973
|
+
"""Return a filesystem-safe title for automation downloads."""
|
|
4974
|
+
raw_title = str(task.get("title") or task.get("topic") or task.get("name") or "Vídeo").strip()
|
|
4975
|
+
cleaned = re.sub(r'[<>:"/\\|?*\\x00-\\x1f]+', "_", raw_title)
|
|
4976
|
+
cleaned = " ".join(cleaned.split()).strip(" ._")
|
|
4977
|
+
return cleaned or "Vídeo"
|
|
4978
|
+
|
|
4979
|
+
|
|
4980
|
+
def _automation_download_name(prefix: str, task: dict[str, Any], path: Path | None, fallback_suffix: str) -> str:
|
|
4981
|
+
suffix = path.suffix if path and path.suffix else fallback_suffix
|
|
4982
|
+
if not suffix.startswith("."):
|
|
4983
|
+
suffix = f".{suffix}"
|
|
4984
|
+
return f"{prefix}_{_download_title(task)}{suffix}"
|
|
4985
|
+
|
|
4986
|
+
|
|
4972
4987
|
def _is_music_task(task: dict[str, Any]) -> bool:
|
|
4973
4988
|
"""Identify music pipeline tasks without conflating them with ordinary video tasks."""
|
|
4974
4989
|
return bool(task.get("music_mode")) or str(task.get("style_wide") or task.get("style") or "").strip().casefold() in {"music", "música"}
|
|
@@ -5508,27 +5523,99 @@ def _render_tiktok_automation_cards():
|
|
|
5508
5523
|
for task in tiktok_tasks:
|
|
5509
5524
|
task_id = str(task["id"])
|
|
5510
5525
|
with st.container(border=True):
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5526
|
+
task_cols = st.columns([2.25, 1.55, 1.05, 2.15], gap="small")
|
|
5527
|
+
thumbnail_path = _task_thumbnail_path(task)
|
|
5528
|
+
script_path = _task_artifact_path(task, "script")
|
|
5529
|
+
video_path = _task_artifact_path(task, "video")
|
|
5530
|
+
thumbnail_prompt_path = _task_artifact_path(task, "thumbnail_prompt_json")
|
|
5531
|
+
thumbnail_prompt = str(task.get("thumbnail_prompt") or "").strip()
|
|
5532
|
+
with task_cols[0]:
|
|
5514
5533
|
if thumbnail_path:
|
|
5515
|
-
st.image(str(thumbnail_path), width=
|
|
5534
|
+
st.image(str(thumbnail_path), width=180, caption="Thumbnail")
|
|
5535
|
+
else:
|
|
5536
|
+
st.caption("Thumbnail ainda não pronta")
|
|
5516
5537
|
st.write(f"**{task.get('title') or task.get('topic') or 'Vídeo TikTok'}**")
|
|
5517
5538
|
st.caption(f"{task.get('channel_name') or 'Canal TikTok'} · {task_id}")
|
|
5518
|
-
|
|
5539
|
+
thumbnail_download_col, prompt_download_col = st.columns(2, gap="small")
|
|
5540
|
+
with thumbnail_download_col:
|
|
5541
|
+
st.download_button(
|
|
5542
|
+
"Baixar Thumbnail 9:16",
|
|
5543
|
+
data=thumbnail_path.read_bytes() if thumbnail_path else b"",
|
|
5544
|
+
file_name=_automation_download_name("Thumbnail9:16", task, thumbnail_path, ".png"),
|
|
5545
|
+
mime="image/png",
|
|
5546
|
+
key=f"tiktok_automation_download_thumbnail_{task_id}",
|
|
5547
|
+
use_container_width=True,
|
|
5548
|
+
disabled=thumbnail_path is None,
|
|
5549
|
+
)
|
|
5550
|
+
with prompt_download_col:
|
|
5551
|
+
prompt_data = thumbnail_prompt_path.read_bytes() if thumbnail_prompt_path else thumbnail_prompt.encode("utf-8")
|
|
5552
|
+
st.download_button(
|
|
5553
|
+
"Baixar Thumbnail Prompt",
|
|
5554
|
+
data=prompt_data,
|
|
5555
|
+
file_name=_automation_download_name("Thumbnail-Prompt", task, thumbnail_prompt_path, ".txt"),
|
|
5556
|
+
mime="application/json" if thumbnail_prompt_path else "text/plain",
|
|
5557
|
+
key=f"tiktok_automation_download_thumbnail_prompt_{task_id}",
|
|
5558
|
+
use_container_width=True,
|
|
5559
|
+
disabled=thumbnail_prompt_path is None and not thumbnail_prompt,
|
|
5560
|
+
)
|
|
5561
|
+
with task_cols[1]:
|
|
5519
5562
|
_render_video_task_state(task)
|
|
5520
|
-
with
|
|
5563
|
+
with task_cols[2]:
|
|
5521
5564
|
st.caption("Plataforma")
|
|
5522
5565
|
st.write("TikTok")
|
|
5523
5566
|
st.caption("Portrait 9:16")
|
|
5524
|
-
with
|
|
5567
|
+
with task_cols[3]:
|
|
5525
5568
|
state = str(task.get("state") or "")
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
st.
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5569
|
+
start_col, stop_col, delete_col = st.columns(3)
|
|
5570
|
+
with start_col:
|
|
5571
|
+
if st.button("Start", key=f"tiktok_automation_start_{task_id}", use_container_width=True, disabled=state not in {"to_do", "blocked", "failed"}):
|
|
5572
|
+
if _start_pipeline_task(task_id, state):
|
|
5573
|
+
st.rerun()
|
|
5574
|
+
with stop_col:
|
|
5575
|
+
if st.button("Stop", key=f"tiktok_automation_stop_{task_id}", use_container_width=True, disabled=state != "doing"):
|
|
5576
|
+
stop_task_by_user(task_id)
|
|
5577
|
+
st.rerun()
|
|
5578
|
+
script_download_col, video_download_col = st.columns(2, gap="small")
|
|
5579
|
+
with script_download_col:
|
|
5580
|
+
st.download_button(
|
|
5581
|
+
"Baixar Roteiro",
|
|
5582
|
+
data=script_path.read_bytes() if script_path else b"",
|
|
5583
|
+
file_name=_automation_download_name("Script", task, script_path, ".md"),
|
|
5584
|
+
mime="text/markdown",
|
|
5585
|
+
key=f"tiktok_automation_download_script_{task_id}",
|
|
5586
|
+
use_container_width=True,
|
|
5587
|
+
disabled=script_path is None,
|
|
5588
|
+
)
|
|
5589
|
+
with video_download_col:
|
|
5590
|
+
st.download_button(
|
|
5591
|
+
"Baixar Vídeo9:16",
|
|
5592
|
+
data=video_path.read_bytes() if video_path else b"",
|
|
5593
|
+
file_name=_automation_download_name("Vídeo9:16", task, video_path, ".mp4"),
|
|
5594
|
+
mime="video/mp4",
|
|
5595
|
+
key=f"tiktok_automation_download_video_{task_id}",
|
|
5596
|
+
use_container_width=True,
|
|
5597
|
+
disabled=video_path is None,
|
|
5598
|
+
)
|
|
5599
|
+
with delete_col:
|
|
5600
|
+
confirm_delete_key = f"tiktok_automation_confirm_delete_{task_id}"
|
|
5601
|
+
if st.button("Apagar", key=f"tiktok_automation_delete_{task_id}", use_container_width=True, disabled=state == "doing"):
|
|
5602
|
+
st.session_state[confirm_delete_key] = True
|
|
5603
|
+
st.rerun()
|
|
5604
|
+
if st.session_state.get(confirm_delete_key):
|
|
5605
|
+
st.warning("Remover este vídeo da fila? Os ficheiros de artefactos serão preservados.")
|
|
5606
|
+
confirm_col, cancel_col = st.columns(2)
|
|
5607
|
+
with confirm_col:
|
|
5608
|
+
if st.button("Confirmar", key=f"tiktok_automation_confirm_delete_button_{task_id}", use_container_width=True, type="primary"):
|
|
5609
|
+
try:
|
|
5610
|
+
delete_task(task["id"])
|
|
5611
|
+
st.session_state.pop(confirm_delete_key, None)
|
|
5612
|
+
st.rerun()
|
|
5613
|
+
except ValueError as exc:
|
|
5614
|
+
st.error(str(exc))
|
|
5615
|
+
with cancel_col:
|
|
5616
|
+
if st.button("Cancelar", key=f"tiktok_automation_cancel_delete_{task_id}", use_container_width=True):
|
|
5617
|
+
st.session_state.pop(confirm_delete_key, None)
|
|
5618
|
+
st.rerun()
|
|
5532
5619
|
|
|
5533
5620
|
|
|
5534
5621
|
def render_tiktok_automation():
|
|
@@ -5626,7 +5713,7 @@ def _render_youtube_automation_cards():
|
|
|
5626
5713
|
st.download_button(
|
|
5627
5714
|
"Baixar Thumbnail",
|
|
5628
5715
|
data=thumbnail_path.read_bytes() if thumbnail_path else b"",
|
|
5629
|
-
file_name=
|
|
5716
|
+
file_name=_automation_download_name("Thumbnail9:16", task, thumbnail_path, ".png"),
|
|
5630
5717
|
mime="image/png",
|
|
5631
5718
|
key=f"automation_download_thumbnail_{task['id']}",
|
|
5632
5719
|
use_container_width=True,
|
|
@@ -5637,7 +5724,7 @@ def _render_youtube_automation_cards():
|
|
|
5637
5724
|
st.download_button(
|
|
5638
5725
|
"Baixar Thumbnail Prompt",
|
|
5639
5726
|
data=prompt_data,
|
|
5640
|
-
file_name=
|
|
5727
|
+
file_name=_automation_download_name("Thumbnail-Prompt", task, thumbnail_prompt_path, ".txt"),
|
|
5641
5728
|
mime="application/json" if thumbnail_prompt_path else "text/plain",
|
|
5642
5729
|
key=f"automation_download_thumbnail_prompt_{task['id']}",
|
|
5643
5730
|
use_container_width=True,
|
|
@@ -5664,7 +5751,7 @@ def _render_youtube_automation_cards():
|
|
|
5664
5751
|
st.download_button(
|
|
5665
5752
|
"Baixar Roteiro",
|
|
5666
5753
|
data=script_path.read_bytes() if script_path else b"",
|
|
5667
|
-
file_name=
|
|
5754
|
+
file_name=_automation_download_name("Script", task, script_path, ".md"),
|
|
5668
5755
|
mime="text/markdown",
|
|
5669
5756
|
key=f"automation_download_script_{task['id']}",
|
|
5670
5757
|
use_container_width=True,
|
|
@@ -5672,9 +5759,9 @@ def _render_youtube_automation_cards():
|
|
|
5672
5759
|
)
|
|
5673
5760
|
with video_download_col:
|
|
5674
5761
|
st.download_button(
|
|
5675
|
-
"Baixar
|
|
5762
|
+
"Baixar Vídeo9:16",
|
|
5676
5763
|
data=video_path.read_bytes() if video_path else b"",
|
|
5677
|
-
file_name=
|
|
5764
|
+
file_name=_automation_download_name("Vídeo9:16", task, video_path, ".mp4"),
|
|
5678
5765
|
mime="video/mp4",
|
|
5679
5766
|
key=f"automation_download_video_{task['id']}",
|
|
5680
5767
|
use_container_width=True,
|
package/package.json
CHANGED