@danhachuel/thunderbolt 0.4.85 → 0.4.87

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 CHANGED
@@ -4466,6 +4466,28 @@ def load_video_tasks_for_catalog() -> list[dict[str, Any]]:
4466
4466
  return [task for task in saved if isinstance(task, dict) and str(task.get("id") or "").strip()]
4467
4467
 
4468
4468
 
4469
+ def task_platform(task: dict[str, Any]) -> str:
4470
+ """Resolve a plataforma da tarefa, incluindo tarefas legadas sem platform."""
4471
+ explicit = str(task.get("platform") or "").strip().casefold()
4472
+ if explicit in {"youtube", "yt"}:
4473
+ return "youtube"
4474
+ if explicit in {"tiktok", "tik-tok", "tt"}:
4475
+ return "tiktok"
4476
+ channel_id = str(task.get("channel_id") or "")
4477
+ if channel_id:
4478
+ channel = next((item for item in read_json("channels.json", []) if isinstance(item, dict) and str(item.get("id")) == channel_id), None)
4479
+ if channel:
4480
+ return classify_channel_platform(channel)
4481
+ format_value = str(task.get("format") or "").strip().casefold()
4482
+ return "tiktok" if format_value in {"portrait", "portrait 9:16", "9:16"} else "youtube"
4483
+
4484
+
4485
+ def load_automation_tasks_for_platform(platform_name: str) -> list[dict[str, Any]]:
4486
+ """Return only automation/catalog tasks belonging to one publishing platform."""
4487
+ target = str(platform_name or "").strip().casefold()
4488
+ return [task for task in load_video_tasks_for_catalog() if task_platform(task) == target]
4489
+
4490
+
4469
4491
  def _task_thumbnail_path(task: dict[str, Any]) -> Path | None:
4470
4492
  """Resolve a generated thumbnail from current and legacy task fields."""
4471
4493
  artifacts = task.get("artifacts") if isinstance(task.get("artifacts"), dict) else {}
@@ -4968,6 +4990,36 @@ def render_tiktok_automation():
4968
4990
  update_channel(channel_id, {"automation_on": bool(enabled), "automation_time": schedule_time.strip(), "default_prompt_master": prompt, "prompt_master": prompt, "platform": "tiktok", "video_aspect_ratio": "Portrait 9:16", "style_wide": "portrait", "avatar_url": avatar_url, "thumbnail_url": avatar_url})
4969
4991
  st.success("Automação TikTok guardada.")
4970
4992
  st.rerun()
4993
+ st.divider()
4994
+ st.subheader("Vídeos cadastrados TikTok")
4995
+ st.caption("Esta fila mostra exclusivamente tarefas associadas a canais TikTok.")
4996
+ tiktok_tasks = load_automation_tasks_for_platform("tiktok")
4997
+ if not tiktok_tasks:
4998
+ st.info("Ainda não existem vídeos TikTok cadastrados.")
4999
+ for task in tiktok_tasks:
5000
+ task_id = str(task["id"])
5001
+ with st.container(border=True):
5002
+ cols = st.columns([2.4, 1.4, 1.1, 1.6])
5003
+ with cols[0]:
5004
+ thumbnail_path = _task_thumbnail_path(task)
5005
+ if thumbnail_path:
5006
+ st.image(str(thumbnail_path), width=150, caption="Thumbnail")
5007
+ st.write(f"**{task.get('title') or task.get('topic') or 'Vídeo TikTok'}**")
5008
+ st.caption(f"{task.get('channel_name') or 'Canal TikTok'} · {task_id}")
5009
+ with cols[1]:
5010
+ _render_video_task_state(task)
5011
+ with cols[2]:
5012
+ st.caption("Plataforma")
5013
+ st.write("TikTok")
5014
+ st.caption("Portrait 9:16")
5015
+ with cols[3]:
5016
+ state = str(task.get("state") or "")
5017
+ if st.button("Start", key=f"tiktok_automation_start_{task_id}", disabled=state not in {"to_do", "blocked", "failed"}, use_container_width=True):
5018
+ retry_task_with_current_settings(task_id) if state in {"blocked", "failed"} else transition_task(task_id, "doing")
5019
+ st.rerun()
5020
+ if st.button("Stop", key=f"tiktok_automation_stop_{task_id}", disabled=state != "doing", use_container_width=True):
5021
+ stop_task_by_user(task_id)
5022
+ st.rerun()
4971
5023
 
4972
5024
 
4973
5025
  def render_automation():
@@ -5049,7 +5101,7 @@ def render_automation():
5049
5101
  st.divider()
5050
5102
  st.subheader("Vídeos cadastrados")
5051
5103
  st.caption("Start retoma as etapas já concluídas e só gera novamente o que ainda não estiver pronto. Em tarefas falhadas ou bloqueadas, a nova tentativa lê as chaves, prioridades e configurações actualmente guardadas. Apagar remove o card da fila após confirmação e preserva os artefactos locais.")
5052
- tasks = load_video_tasks_for_catalog()
5104
+ tasks = load_automation_tasks_for_platform("youtube")
5053
5105
  if not tasks:
5054
5106
  st.info("Ainda não existem vídeos cadastrados.")
5055
5107
  for task in tasks:
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import base64
6
+ import re
6
7
  from functools import lru_cache
7
8
  from typing import Any
8
9
 
@@ -1130,7 +1131,11 @@ def translate_ui_content(value: Any, language: Any = "pt") -> Any:
1130
1131
  translated = value
1131
1132
  for source, target in _sorted_translations(code):
1132
1133
  if source != target and source in translated:
1133
- translated = translated.replace(source, target)
1134
+ # Only replace complete tokens. This prevents the English token
1135
+ # ``Font`` from matching the Portuguese word ``Fonte`` and
1136
+ # producing malformed labels such as ``Fontee...``.
1137
+ pattern = rf"(?<!\w){re.escape(source)}(?!\w)"
1138
+ translated = re.sub(pattern, target, translated)
1134
1139
  return translated
1135
1140
 
1136
1141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.4.85",
3
+ "version": "0.4.87",
4
4
  "description": "Thunderbolt — interface local para operação de canais faceless e motor MoneyPrinterTurbo",
5
5
  "license": "MIT",
6
6
  "main": "scripts/cli.mjs",