@danhachuel/thunderbolt 0.5.91 → 0.5.93

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
@@ -4874,12 +4874,16 @@ def _render_video_task_state(task: dict[str, Any]) -> None:
4874
4874
  st.caption(f"Manifesto: {task.get('video_result')}")
4875
4875
 
4876
4876
 
4877
- def _start_pipeline_task(task_id: str, state: str) -> None:
4877
+ def _start_pipeline_task(task_id: str, state: str) -> bool:
4878
4878
  """Start or retry a video task from any platform automation card."""
4879
4879
  if state in {"blocked", "failed"}:
4880
- retry_task_with_current_settings(task_id)
4880
+ updated = retry_task_with_current_settings(task_id)
4881
4881
  else:
4882
- transition_task(task_id, "doing")
4882
+ updated = transition_task(task_id, "doing")
4883
+ if not updated:
4884
+ st.error("Não foi possível iniciar este vídeo: a tarefa já não existe na fila.")
4885
+ return False
4886
+ return True
4883
4887
 
4884
4888
 
4885
4889
  def render_videos():
@@ -5386,11 +5390,8 @@ def _render_youtube_automation_cards():
5386
5390
  start_col, stop_col, delete_col = st.columns(3)
5387
5391
  with start_col:
5388
5392
  if st.button("Start", key=f"automation_start_{task['id']}", use_container_width=True, disabled=state not in {"to_do", "blocked", "failed"}):
5389
- if state in {"failed", "blocked"}:
5390
- retry_task_with_current_settings(task["id"])
5391
- else:
5392
- transition_task(task["id"], "doing")
5393
- st.rerun()
5393
+ if _start_pipeline_task(str(task["id"]), state):
5394
+ st.rerun()
5394
5395
  with stop_col:
5395
5396
  if st.button("Stop", key=f"automation_stop_{task['id']}", use_container_width=True, disabled=state != "doing"):
5396
5397
  stop_task_by_user(task["id"])
@@ -8340,17 +8341,15 @@ def render_notifications():
8340
8341
 
8341
8342
 
8342
8343
  def render_models_ai_tutorial():
8343
- tutorial_url = "https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-instagram.md"
8344
8344
  tutorial_path = ROOT / "seed" / "references" / "guide-instagram.md"
8345
8345
  st.title("Tutorial Meta")
8346
8346
  st.caption("Guia de configuração de uma conta Instagram profissional e das credenciais Meta para automações com n8n.")
8347
- st.markdown(f"[Abrir fonte original no GitHub]({tutorial_url})")
8348
8347
  try:
8349
8348
  tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
8350
8349
  except OSError:
8351
8350
  tutorial_content = ""
8352
8351
  if not tutorial_content:
8353
- st.error("O conteúdo local do tutorial não está disponível. Consulte a fonte original no GitHub.")
8352
+ st.error("O conteúdo local do tutorial não está disponível.")
8354
8353
  return
8355
8354
  st.markdown(tutorial_content, unsafe_allow_html=True)
8356
8355
 
@@ -8362,18 +8361,16 @@ def render_niche_tutorial(tutorial_kind: str):
8362
8361
 
8363
8362
 
8364
8363
  def render_supabase_tutorial():
8365
- tutorial_url = "https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-supabase.md"
8366
8364
  tutorial_path = ROOT / "seed" / "references" / "guide-supabase.md"
8367
8365
  ui_language = current_ui_language()
8368
8366
  st.title(ui_text("Tutorial Supabase", ui_language))
8369
8367
  st.caption(ui_text("Guia de configuração do Supabase para automações com n8n.", ui_language))
8370
- st.markdown(f"[Abrir fonte original no GitHub]({tutorial_url})")
8371
8368
  try:
8372
8369
  tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
8373
8370
  except OSError:
8374
8371
  tutorial_content = ""
8375
8372
  if not tutorial_content:
8376
- st.error("O conteúdo local do tutorial não está disponível. Consulte a fonte original no GitHub.")
8373
+ st.error("O conteúdo local do tutorial não está disponível.")
8377
8374
  return
8378
8375
  st.markdown(tutorial_content, unsafe_allow_html=True)
8379
8376
 
@@ -8383,13 +8380,12 @@ def render_google_oauth_tutorial():
8383
8380
  tutorial_path = ROOT / "seed" / "references" / "tutorial-oauth-google.md"
8384
8381
  st.title("Tutorial OAuth do Google")
8385
8382
  st.caption("Guia completo para configurar a autenticação OAuth do Google e o acesso à YouTube Data API v3.")
8386
- st.markdown("[Abrir Google Cloud Console](https://console.cloud.google.com/)")
8387
8383
  try:
8388
8384
  tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
8389
8385
  except OSError:
8390
8386
  tutorial_content = ""
8391
8387
  if not tutorial_content:
8392
- st.error("O conteúdo local do tutorial não está disponível. Consulte a documentação OAuth do Google.")
8388
+ st.error("O conteúdo local do tutorial não está disponível.")
8393
8389
  return
8394
8390
  st.markdown(tutorial_content, unsafe_allow_html=False)
8395
8391
 
@@ -8399,13 +8395,12 @@ def render_youtube_frontend_upload_tutorial():
8399
8395
  tutorial_path = ROOT / "seed" / "references" / "youtube-video-upload-frontend.md"
8400
8396
  st.title("Tutorial YouTube Video-Upload Frontend")
8401
8397
  st.caption("Guia prático e seguro para rever metadados, sessão e envio directo de vídeos concluídos.")
8402
- st.markdown("[Abrir referência técnica no GitHub](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api)")
8403
8398
  try:
8404
8399
  tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
8405
8400
  except OSError:
8406
8401
  tutorial_content = ""
8407
8402
  if not tutorial_content:
8408
- st.error("O conteúdo local do tutorial não está disponível. Consulte a referência técnica no GitHub.")
8403
+ st.error("O conteúdo local do tutorial não está disponível.")
8409
8404
  return
8410
8405
  st.markdown(tutorial_content, unsafe_allow_html=False)
8411
8406
 
@@ -8415,13 +8410,12 @@ def render_youtube_data_api_key_tutorial():
8415
8410
  tutorial_path = ROOT / "seed" / "references" / "tutorial-youtube-data-api-key.md"
8416
8411
  st.title("Tutorial YouTube Data API Key (Public Data)")
8417
8412
  st.caption("Guia para criar e configurar uma YouTube Data API Key para consultas de dados públicos.")
8418
- st.markdown("[Abrir documento fonte no Google Drive](https://drive.google.com/file/d/1ypzaR1v8khBxyUQmZ12ZAY0pGTD49mqt/view)")
8419
8413
  try:
8420
8414
  tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
8421
8415
  except OSError:
8422
8416
  tutorial_content = ""
8423
8417
  if not tutorial_content:
8424
- st.error("O conteúdo local do tutorial não está disponível. Consulte o documento fonte no Google Drive.")
8418
+ st.error("O conteúdo local do tutorial não está disponível.")
8425
8419
  return
8426
8420
  st.markdown(tutorial_content, unsafe_allow_html=False)
8427
8421
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.5.91",
3
+ "version": "0.5.93",
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",
package/scripts/cli.mjs CHANGED
@@ -322,7 +322,7 @@ function stopPipelineWorker() {
322
322
  }
323
323
 
324
324
  function startPipelineWorker() {
325
- if (shuttingDown || pipelineWorker || !hasPendingPipelineWork()) return;
325
+ if (shuttingDown || pipelineWorker) return;
326
326
  pipelineWorker = spawn(python, ["-m", "hermes_ui.pipeline_worker"], {
327
327
  cwd: root,
328
328
  stdio: "inherit",
@@ -334,19 +334,16 @@ function startPipelineWorker() {
334
334
  pipelineWorker = null;
335
335
  if (shuttingDown) return;
336
336
  console.error(`Thunderbolt pipeline worker: terminou (código ${code ?? "-"}, sinal ${signal ?? "-"}).`);
337
- if (hasPendingPipelineWork()) {
338
- pipelineRestartTimer = setTimeout(() => {
339
- pipelineRestartTimer = null;
340
- startPipelineWorker();
341
- }, 5000);
342
- }
337
+ pipelineRestartTimer = setTimeout(() => {
338
+ pipelineRestartTimer = null;
339
+ startPipelineWorker();
340
+ }, 5000);
343
341
  });
344
342
  }
345
343
 
346
344
  function monitorWorkers() {
347
345
  if (shuttingDown) return;
348
- if (hasPendingPipelineWork()) startPipelineWorker();
349
- else if (pipelineWorker) stopPipelineWorker();
346
+ startPipelineWorker();
350
347
  startAutomationWorker();
351
348
  }
352
349
 
@@ -396,6 +393,6 @@ const stopWorker = () => {
396
393
  process.on("SIGINT", stopWorker);
397
394
  process.on("SIGTERM", stopWorker);
398
395
  monitorWorkers();
399
- // O launcher verifica apenas os JSONs locais; não existe timer no Streamlit
400
- // nem worker persistente quando não há tarefa pendente.
396
+ // O pipeline worker permanece disponível para recolher imediatamente tarefas
397
+ // colocadas em `doing` pelo botão Start, mesmo quando a fila estava vazia.
401
398
  workerMonitorTimer = setInterval(monitorWorkers, 2000);
@@ -171,5 +171,3 @@ You'll see this interface below.
171
171
  2. The Instagram account id which you'll need to set in the `Configure` node in the n8n workflow
172
172
 
173
173
  ---
174
-
175
- > **Source:** [ai_agents_az/episode_8/guide-instagram.md](https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-instagram.md)
@@ -65,5 +65,3 @@ No Thunderbolt, abra **Configurações > Configuração API > AI Influencers > B
65
65
  O Thunderbolt usa o Supabase como backend seleccionado, não como executor de n8n: os assets são enviados ao Storage, os metadados ficam nas tabelas e os estados de geração ficam em `influencer_content`. Para uma execução totalmente local, seleccione **SQLite** no mesmo painel; a alternativa cria `storage/state/ai_influencers.db` e não utiliza a conta Supabase.
66
66
 
67
67
  A conta Supabase está pronta para ser utilizada pela automação.
68
-
69
- Fonte original: [guide-supabase.md no GitHub](https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-supabase.md)
@@ -1,9 +1,5 @@
1
- # Tutorial YouTube Data API Key (Public Data)
2
-
3
1
  Este tutorial explica como criar uma **YouTube Data API Key** no Google Cloud e configurá-la no Thunderbolt para consultar dados públicos do YouTube, como vídeos, canais e estatísticas públicas.
4
2
 
5
- > **Fonte:** [Tutorial YouTube Data API Key (Public Data) no Google Drive](https://drive.google.com/file/d/1ypzaR1v8khBxyUQmZ12ZAY0pGTD49mqt/view)
6
-
7
3
  ## Quando usar uma API Key
8
4
 
9
5
  Uma API Key é suficiente para operações com dados públicos, como pesquisar vídeos, consultar canais públicos e obter estatísticas públicas. Ela **não concede acesso a dados privados** da conta nem substitui o OAuth 2.0 para playlists privadas, inscrições privadas ou uploads.
@@ -93,4 +89,3 @@ Uma API Key só identifica o projecto e permite chamadas autorizadas para dados
93
89
  - [Google Cloud Console](https://console.cloud.google.com/)
94
90
  - [YouTube Data API v3](https://developers.google.com/youtube/v3)
95
91
  - [Documentação de quotas da YouTube Data API](https://developers.google.com/youtube/v3/determine_quota_cost)
96
- - [Documento fonte no Google Drive](https://drive.google.com/file/d/1ypzaR1v8khBxyUQmZ12ZAY0pGTD49mqt/view)
@@ -42,7 +42,3 @@ Depois de confirmar o canal, a privacidade e os metadados, seleccione o fluxo de
42
42
  | Falha de rede | Não repita automaticamente sem confirmar se o vídeo foi criado no YouTube Studio. |
43
43
  | Metadados incompletos | Corrija título, descrição, tags, idioma ou privacidade no formulário antes de enviar. |
44
44
  | Limite ou política da plataforma | Pare o processo e siga as políticas e limites actualmente aplicáveis do YouTube. |
45
-
46
- ## Referência técnica
47
-
48
- O material fornecido pelo utilizador cita o repositório [Nojus10/YouTube-Video-Upload-Frontend-Api](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api). O Thunderbolt apresenta este tutorial como orientação de integração segura; não replica segredos, valores de autenticação nem instruções de extracção de credenciais.