@danhachuel/thunderbolt 0.2.83 → 0.2.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.
@@ -196,6 +196,8 @@ O Thunderbolt inclui as dez opções de idioma usadas pelo fluxo MoneyPrinterTur
196
196
 
197
197
  No topo da área principal, o menu nativo de idioma segue o padrão do MoneyPrinterTurbo e mostra o rótulo **Language** acima do selector. Cada opção apresenta o nome e o código, acompanhados por uma imagem SVG local da bandeira; não depende de emojis nem de siglas da fonte do sistema. A escolha fica guardada como `ui_language` em `storage/state/settings.json` e, quando existe um caminho MoneyPrinterTurbo configurado, é sincronizada com `[ui].language` no `config.toml`. O dashboard inicial e as abas da barra lateral usam o mesmo idioma seleccionado. O toolbar do Streamlit não é manipulado: o indicador de execução, **Deploy** e o menu de três pontos permanecem nativos e clicáveis. Na Criação de Vídeos e em Roteiros, o selector **Script Language** mantém o catálogo de vídeo independente, mas guarda o código curto; essa escolha é sincronizada com `[ui].video_language`.
198
198
 
199
+ Todas as subabas internas também são traduzidas nos dez idiomas: Blueprints/Brandings, Pesquisa pública/Cadastro manual/Contas cadastradas, Upload/Biblioteca, Importar do YouTube/Canais em lote gmail/Cadastro manual, Criar vídeo/Vídeos, Novo roteiro/letra/Histórico guardado, análise de clusters, edição de cortes, Vídeos/Código Python, as quatro opções de Upload, API Keys/Teste de vozes, Serviços e modelos/Fontes de materiais e Client MCP/Servidor MCP/Skill. A ordem e as chaves internas dos widgets permanecem estáveis.
200
+
199
201
  ## 4. Diagnóstico antes de iniciar
200
202
 
201
203
  Depois da instalação, execute:
@@ -351,7 +353,7 @@ Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
351
353
 
352
354
  ## 8. Configuração inicial da UI
353
355
 
354
- Na primeira execução, a barra lateral apresenta **Início**, **Niche Finder**, **Pipeline**, **Pipeline TikTok**, **Automação**, **Edição**, **AI Influencers** e **Configurações**. O menu expansível **Automação** contém a subaba **Automação Youtube**. O menu expansível **Edição** contém **Limpador de Metadados**, **Cortes** e **Editor Python**. O menu expansível **AI Influencers** contém **Personagens**, **Redes Sociais** e **Tutorial Meta**, nessa ordem. O **Tutorial Meta** apresenta o guia de configuração de Instagram e credenciais Meta para automações com n8n. O menu expansível **Niche Finder** contém **Niche Finder Kaggle** e **Niche Finder Apify**. A aba **Niche Finder Kaggle** mantém a análise do dataset Kaggle; **Niche Finder Apify** é uma segunda alternativa independente, com actor, dataset, parâmetros, credencial, execução e resultados próprios. O menu **Pipeline** contém **Criação de Vídeos**, **Criação de Músicas**, **Roteiros** e **Upload**. O menu **Configurações** contém **Canais Youtube**, **Blueprints Youtube**, **MCP**, **Contas Google**, **Configuração API** e **Notificações**. Abra **Configurações > Contas Google** para gerir contas Google/YouTube, cartões, documentos de Upload directo, `sessionInfo`, `INNERTUBE_API_KEY`, contas adicionais e a configuração global do YouTube. Abra **Configurações > Configuração API** para configurar as restantes API Keys, providers, modelos, serviços e o **Teste de vozes**. A página **Configurações > Notificações** está reservada para futuros eventos locais. Para YouTube, preencha primeiro o par **OAuth Client ID + OAuth Client Secret** em **Contas Google** se pretende autorizar uploads. A **YouTube Data API Key** é uma credencial Google Cloud diferente e fica em **Contas Google**, apenas para o método oficial de métricas; Client ID + Client Secret não formam uma API Key.
356
+ Na primeira execução, a barra lateral apresenta **Início**, **Niche Finder**, **Pipeline**, **Pipeline TikTok**, **Automação**, **Edição**, **AI Influencers** e **Configurações**. O menu expansível **Automação** contém a subaba **Automação Youtube**. O menu expansível **Edição** contém **Limpador de Metadados**, **Cortes** e **Editor Python**. O menu expansível **AI Influencers** contém **Personagens**, **Redes Sociais**, **Tutorial Meta** e **Tutorial Supabase**, nessa ordem. O **Tutorial Meta** apresenta o guia de configuração de Instagram e credenciais Meta para automações com n8n. O **Tutorial Supabase** apresenta o guia para criar as tabelas `plans` e `posts` e o bucket `instagram-images`, incluindo a ligação para a fonte original no GitHub; as duas frases promocionais da comunidade foram omitidas. O menu expansível **Niche Finder** contém **Niche Finder Kaggle** e **Niche Finder Apify**. A aba **Niche Finder Kaggle** mantém a análise do dataset Kaggle; **Niche Finder Apify** é uma segunda alternativa independente, com actor, dataset, parâmetros, credencial, execução e resultados próprios. O menu **Pipeline** contém **Criação de Vídeos**, **Criação de Músicas**, **Roteiros** e **Upload**. O menu **Configurações** contém **Canais Youtube**, **Blueprints Youtube**, **MCP**, **Contas Google**, **Configuração API** e **Notificações**. Abra **Configurações > Contas Google** para gerir contas Google/YouTube, cartões, documentos de Upload directo, `sessionInfo`, `INNERTUBE_API_KEY`, contas adicionais e a configuração global do YouTube. Abra **Configurações > Configuração API** para configurar as restantes API Keys, providers, modelos, serviços e o **Teste de vozes**. A página **Configurações > Notificações** está reservada para futuros eventos locais. Para YouTube, preencha primeiro o par **OAuth Client ID + OAuth Client Secret** em **Contas Google** se pretende autorizar uploads. A **YouTube Data API Key** é uma credencial Google Cloud diferente e fica em **Contas Google**, apenas para o método oficial de métricas; Client ID + Client Secret não formam uma API Key.
355
357
 
356
358
  | Configuração | Finalidade |
357
359
  |---|---|
package/README.md CHANGED
@@ -52,9 +52,11 @@ O Thunderbolt mantém os idiomas históricos da criação de vídeos e acrescent
52
52
 
53
53
  No topo da área principal da aplicação existe o menu nativo de idioma no padrão do MoneyPrinterTurbo, sem sobrepor o toolbar do Streamlit. O selector mostra o rótulo visível **Language** acima do campo, cada opção aparece como **nome do idioma + código**, e a bandeira é uma imagem SVG local real, não um emoji ou sigla dependente da fonte do sistema. Ao escolher outra opção, a preferência é guardada em `storage/state/settings.json` como `ui_language`, e a navegação lateral e o dashboard inicial actualizam-se sem alterar o idioma seleccionado para os vídeos. O indicador de execução, o botão **Deploy** e o menu principal do Streamlit permanecem totalmente nativos e clicáveis.
54
54
 
55
- ## Navegação da UI 0.2.82
55
+ ## Navegação da UI 0.2.84
56
56
 
57
- A barra lateral mantém os níveis principais, nesta ordem: **Início**, **Niche Finder**, **Pipeline**, **Pipeline TikTok**, **Automação**, **Edição**, **AI Influencers** e **Configurações**. **Pipeline** é expansível e contém **Criação de Vídeos**, **Criação de Músicas**, **Roteiros** e **Upload**. **Automação** também é expansível e contém **Automação Youtube**. **Edição** é expansível e contém **Limpador de Metadados**, **Cortes**, **Editor Python** e **Download Mídia**, nessa ordem. **AI Influencers** é expansível e contém **Personagens**, **Redes Sociais** e **Tutorial Meta**, nessa ordem. **Niche Finder** é expansível e contém **Niche Finder Kaggle** e **Niche Finder Apify**. **Configurações** é expansível e contém **Canais Youtube**, **Blueprints Youtube**, **MCP**, **Contas Google**, **Configuração API** e **Notificações**. O Início reúne o dashboard e as filas do Pipeline, sem botões de acções rápidas.
57
+ A barra lateral mantém os níveis principais, nesta ordem: **Início**, **Niche Finder**, **Pipeline**, **Pipeline TikTok**, **Automação**, **Edição**, **AI Influencers** e **Configurações**. **Pipeline** é expansível e contém **Criação de Vídeos**, **Criação de Músicas**, **Roteiros** e **Upload**. **Automação** também é expansível e contém **Automação Youtube**. **Edição** é expansível e contém **Limpador de Metadados**, **Cortes**, **Editor Python** e **Download Mídia**, nessa ordem. **AI Influencers** é expansível e contém **Personagens**, **Redes Sociais**, **Tutorial Meta** e **Tutorial Supabase**, nessa ordem. **Niche Finder** é expansível e contém **Niche Finder Kaggle** e **Niche Finder Apify**. **Configurações** é expansível e contém **Canais Youtube**, **Blueprints Youtube**, **MCP**, **Contas Google**, **Configuração API** e **Notificações**. O Início reúne o dashboard e as filas do Pipeline, sem botões de acções rápidas.
58
+
59
+ A subaba **Tutorial Supabase** apresenta o guia de criação das tabelas `plans` e `posts` e do bucket `instagram-images`, com ligação para a fonte original no GitHub. As duas frases promocionais da comunidade foram omitidas. Todas as subabas internas também são localizadas pelo helper nativo de tabs: **Blueprints/Brandings**, **Pesquisa pública/Cadastro manual/Contas cadastradas**, **Upload/Biblioteca**, **Importar do YouTube/Canais em lote gmail/Cadastro manual**, **Criar vídeo/Vídeos**, **Novo roteiro/letra/Histórico guardado**, as três abas de análise de clusters, as quatro abas de cortes, **Vídeos/Código Python**, as quatro opções de Upload, **API Keys/Teste de vozes**, **Serviços e modelos/Fontes de materiais** e **Client MCP/Servidor MCP/Skill**. As chaves técnicas e a ordem funcional permanecem inalteradas.
58
60
 
59
61
  Dentro de **Configurações > Contas Google**, a UI contém os cartões expansíveis de contas Google/YouTube, `sessionInfo`, documentos de Upload directo, `INNERTUBE_API_KEY`, o formulário **Adicionar outra conta Gmail** e a configuração global do YouTube (OAuth Client ID, OAuth Client Secret e YouTube Data API Key). A página **Configuração API** contém as restantes credenciais, providers, modelos, serviços, Nano Banana, TikTok e Postiz.
60
62
 
package/app/main.py CHANGED
@@ -218,6 +218,17 @@ def current_ui_language() -> str:
218
218
  return normalized
219
219
 
220
220
 
221
+ def localized_tab_labels(labels: list[str], language: str | None = None) -> list[str]:
222
+ """Translate Streamlit tab labels while keeping widget keys and behaviour stable."""
223
+ selected_language = language_code(language or current_ui_language())
224
+ return [ui_text(label, selected_language) for label in labels]
225
+
226
+
227
+ def render_localized_tabs(labels: list[str], language: str | None = None):
228
+ """Render a tab group with labels translated for the selected UI language."""
229
+ return st.tabs(localized_tab_labels(labels, language))
230
+
231
+
221
232
  def save_ui_language(value: str) -> str:
222
233
  normalized = language_code(value)
223
234
  settings = read_json("settings.json", {})
@@ -750,7 +761,7 @@ def render_dashboard():
750
761
  def render_blueprints():
751
762
  st.title("Blueprints Youtube")
752
763
  st.caption(f"Biblioteca local lida directamente de `{BLUEPRINTS}`")
753
- blueprint_tab, branding_tab = st.tabs(["Blueprints", "Brandings"])
764
+ blueprint_tab, branding_tab = render_localized_tabs(["Blueprints", "Brandings"])
754
765
  with blueprint_tab:
755
766
  st.subheader("Criar blueprint a partir de link")
756
767
  with st.form("create_blueprint_from_link"):
@@ -915,7 +926,7 @@ def render_tiktok_accounts():
915
926
  settings = read_json("settings.json", {})
916
927
  accounts = _tiktok_accounts_from_settings(settings)
917
928
 
918
- search_tab, manual_tab, library_tab = st.tabs(["Pesquisa pública", "Cadastro manual", "Contas cadastradas"])
929
+ search_tab, manual_tab, library_tab = render_localized_tabs(["Pesquisa pública", "Cadastro manual", "Contas cadastradas"])
919
930
  with search_tab:
920
931
  st.subheader("Pesquisar perfil público")
921
932
  st.info("A pesquisa consulta apenas a página pública do perfil e pode devolver dados incompletos. Se o TikTok bloquear a consulta, utilize o cadastro manual.")
@@ -1012,7 +1023,7 @@ def render_tiktok_prompt_masters():
1012
1023
  st.title("Prompts Master")
1013
1024
  st.caption(f"Biblioteca exclusiva para vídeos TikTok. Os ficheiros ficam em `{TIKTOK_PROMPT_MASTERS}` e nunca entram na pasta de Blueprints YouTube.")
1014
1025
 
1015
- upload_tab, library_tab = st.tabs(["Upload", "Biblioteca"])
1026
+ upload_tab, library_tab = render_localized_tabs(["Upload", "Biblioteca"])
1016
1027
  with upload_tab:
1017
1028
  st.subheader("Adicionar Prompt Master")
1018
1029
  st.info("Use ficheiros Markdown `.md`. Cada Prompt Master é guardado como um ficheiro independente no storage TikTok.")
@@ -1097,7 +1108,7 @@ def render_channels():
1097
1108
  youtube_account_labels = {"": "Sem conta Google associada"}
1098
1109
  youtube_account_labels.update({str(account["id"]): f"{account.get('label', 'Canais YouTube')} — {account.get('email', 'sem e-mail')}" for account in youtube_accounts})
1099
1110
  youtube_accounts_by_id = {str(account["id"]): account for account in youtube_accounts}
1100
- import_tab, batch_tab, manual_tab = st.tabs(["Importar do YouTube", "Canais em lote gmail", "Cadastro manual"])
1111
+ import_tab, batch_tab, manual_tab = render_localized_tabs(["Importar do YouTube", "Canais em lote gmail", "Cadastro manual"])
1101
1112
 
1102
1113
  with import_tab:
1103
1114
  st.caption("A pesquisa pública funciona sem API Key. A Data API é opcional e fica disponível numa opção separada para métricas oficiais.")
@@ -1430,7 +1441,7 @@ def render_channels():
1430
1441
 
1431
1442
  def render_new_video(page_title: str = "Criação de Vídeos"):
1432
1443
  st.title(page_title)
1433
- create_tab, videos_tab = st.tabs(["Criar vídeo", "Vídeos"])
1444
+ create_tab, videos_tab = render_localized_tabs(["Criar vídeo", "Vídeos"])
1434
1445
  with create_tab:
1435
1446
  all_channels = [c for c in read_json("channels.json", []) if isinstance(c, dict)]
1436
1447
  active_channels = [c for c in all_channels if c.get("active", True)]
@@ -1790,7 +1801,7 @@ def render_scripts():
1790
1801
  script_dir = script_storage_path()
1791
1802
  st.info(f"**Ficheiros guardados em:** `{script_dir}` · o conteúdo fica no storage local do Thunderbolt e não é enviado automaticamente para plataformas.")
1792
1803
 
1793
- create_tab, history_tab = st.tabs(["Novo roteiro/letra", "Histórico guardado"])
1804
+ create_tab, history_tab = render_localized_tabs(["Novo roteiro/letra", "Histórico guardado"])
1794
1805
  with create_tab:
1795
1806
  all_channels = [channel for channel in read_json("channels.json", []) if isinstance(channel, dict)]
1796
1807
  active_channels = [channel for channel in all_channels if channel.get("active", True)]
@@ -2030,7 +2041,7 @@ def render_niche_finder():
2030
2041
  keyword = st.text_input("Filtrar palavras-chave nos clusters", key="niche_cluster_keyword", placeholder="Ex.: música, gaming, receitas")
2031
2042
  if keyword.strip():
2032
2043
  cluster_table = cluster_table[cluster_table["palavras"].str.contains(keyword.strip(), case=False, na=False)]
2033
- tab_clusters, tab_rules, tab_data = st.tabs(["Clusters encontrados", "Regras de associação", "Dados analisados"])
2044
+ tab_clusters, tab_rules, tab_data = render_localized_tabs(["Clusters encontrados", "Regras de associação", "Dados analisados"])
2034
2045
  with tab_clusters:
2035
2046
  st.dataframe(cluster_table, use_container_width=True, hide_index=True)
2036
2047
  if not points.empty:
@@ -2323,7 +2334,7 @@ def render_cuts():
2323
2334
  )
2324
2335
 
2325
2336
  source_path = None
2326
- source_tab, url_tab, generated_tab, folder_tab = st.tabs(["Upload ficheiro", "URL de vídeo", "Vídeos gerados", "Pasta local"])
2337
+ source_tab, url_tab, generated_tab, folder_tab = render_localized_tabs(["Upload ficheiro", "URL de vídeo", "Vídeos gerados", "Pasta local"])
2327
2338
  with source_tab:
2328
2339
  uploaded_video = st.file_uploader(
2329
2340
  "Clique para carregar ou arraste um vídeo",
@@ -2501,7 +2512,7 @@ def render_python_editor():
2501
2512
  st.caption("Editor local inspirado no PYEdit para scripts Python e edição manual de vídeos do Thunderbolt.")
2502
2513
  st.info("Escolha um vídeo gerado, indique uma pasta local ou faça upload de um vídeo. Nenhum código Python é executado nesta aba; as operações de vídeo só começam depois de clicar no botão da operação.")
2503
2514
 
2504
- video_tab, code_tab = st.tabs(["Vídeos", "Código Python"])
2515
+ video_tab, code_tab = render_localized_tabs(["Vídeos", "Código Python"])
2505
2516
  with video_tab:
2506
2517
  st.subheader("Escolher vídeo")
2507
2518
  source_mode = st.radio("Origem", ["Vídeos gerados", "Pasta local", "Upload manual"], horizontal=True, key="python_editor_source_mode")
@@ -2809,7 +2820,7 @@ def render_upload_direct():
2809
2820
 
2810
2821
  def render_upload():
2811
2822
  st.title("Upload")
2812
- upload_tab, direct_tab, postiz_tab, upload_post_tab = st.tabs(["Upload convencional", "Upload directo", "Postiz", "Upload-Post"])
2823
+ upload_tab, direct_tab, postiz_tab, upload_post_tab = render_localized_tabs(["Upload convencional", "Upload directo", "Postiz", "Upload-Post"])
2813
2824
  with direct_tab:
2814
2825
  render_upload_direct()
2815
2826
  with postiz_tab:
@@ -3459,10 +3470,10 @@ def render_settings():
3459
3470
  key=f"settings_{key}",
3460
3471
  )
3461
3472
 
3462
- api_keys_tab, voice_test_tab = st.tabs(["API Keys", "Teste de vozes"])
3473
+ api_keys_tab, voice_test_tab = render_localized_tabs(["API Keys", "Teste de vozes"])
3463
3474
 
3464
3475
  with api_keys_tab:
3465
- api_service_tab, material_sources_tab = st.tabs(["Serviços e modelos", "Fontes de materiais"])
3476
+ api_service_tab, material_sources_tab = render_localized_tabs(["Serviços e modelos", "Fontes de materiais"])
3466
3477
  with api_service_tab:
3467
3478
  with st.form("settings_form"):
3468
3479
  st.subheader("API Keys")
@@ -3786,11 +3797,28 @@ def render_models_ai_tutorial():
3786
3797
  return
3787
3798
  st.markdown(tutorial_content, unsafe_allow_html=True)
3788
3799
 
3800
+ def render_supabase_tutorial():
3801
+ tutorial_url = "https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-supabase.md"
3802
+ tutorial_path = ROOT / "seed" / "references" / "guide-supabase.md"
3803
+ ui_language = current_ui_language()
3804
+ st.title(ui_text("Tutorial Supabase", ui_language))
3805
+ st.caption(ui_text("Guia de configuração do Supabase para automações com n8n.", ui_language))
3806
+ st.markdown(f"[Abrir fonte original no GitHub]({tutorial_url})")
3807
+ try:
3808
+ tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
3809
+ except OSError:
3810
+ tutorial_content = ""
3811
+ if not tutorial_content:
3812
+ st.error("O conteúdo local do tutorial não está disponível. Consulte a fonte original no GitHub.")
3813
+ return
3814
+ st.markdown(tutorial_content, unsafe_allow_html=True)
3815
+
3816
+
3789
3817
  def render_mcp():
3790
3818
  st.title("MCP")
3791
3819
  st.caption("Clientes externos, servidor MCP do Thunderbolt e a skill local ficam separados para evitar confundir funções diferentes.")
3792
3820
 
3793
- client_tab, server_tab, skill_tab = st.tabs(["Client MCP", "Servidor MCP", "Skill"])
3821
+ client_tab, server_tab, skill_tab = render_localized_tabs(["Client MCP", "Servidor MCP", "Skill"])
3794
3822
 
3795
3823
  with client_tab:
3796
3824
  st.subheader("Client MCP")
@@ -4074,6 +4102,7 @@ def main():
4074
4102
  ("Personagens", ":material/person:", "Personagens"),
4075
4103
  ("Redes Sociais", ":material/share:", "Redes Sociais"),
4076
4104
  ("Tutorial Meta", ":material/menu_book:", "Tutorial Meta"),
4105
+ ("Tutorial Supabase", ":material/storage:", "Tutorial Supabase"),
4077
4106
  ]
4078
4107
  settings_items = [
4079
4108
  ("Canais Youtube", ":material/ondemand_video:", "Canais Youtube"),
@@ -4181,6 +4210,7 @@ def main():
4181
4210
  "Download Mídia": render_media_download,
4182
4211
  "AI Influencers": lambda: render_edit_placeholder("AI Influencers", "Seleccione uma das abas AI Influencers no menu expansível."),
4183
4212
  "Tutorial Meta": render_models_ai_tutorial,
4213
+ "Tutorial Supabase": render_supabase_tutorial,
4184
4214
  "Personagens": lambda: render_edit_placeholder("Personagens", "Área reservada para a futura funcionalidade de personagens."),
4185
4215
  "Redes Sociais": lambda: render_edit_placeholder("Redes Sociais", "Área reservada para a futura funcionalidade de redes sociais."),
4186
4216
  "Upload": render_upload,
@@ -100,7 +100,7 @@ _CORE_UI_TEXT_KEYS = (
100
100
  "Início", "Niche Finder", "Pipeline", "Pipeline TikTok", "Automação", "Edição", "AI Influencers", "Configurações",
101
101
  "Criação de Vídeos", "Criação de Músicas", "Roteiros", "Upload", "Prompts Master", "Contas TikTok", "Automação Youtube",
102
102
  "Niche Finder Kaggle", "Niche Finder Apify", "Limpador de Metadados", "Cortes", "Editor Python", "Download Mídia",
103
- "Personagens", "Redes Sociais", "Tutorial Meta", "Canais Youtube", "Blueprints Youtube", "MCP", "Contas Google",
103
+ "Personagens", "Redes Sociais", "Tutorial Meta", "Tutorial Supabase", "Canais Youtube", "Blueprints Youtube", "MCP", "Contas Google",
104
104
  "Configuração API", "Notificações", "Interface local para operação e automação de conteúdo faceless", "Canais", "activos",
105
105
  "Tarefas", "total registado", "A fazer", "na pipeline", "Em execução", "a decorrer", "Concluídos", "artefactos prontos",
106
106
  "Falhas", "requerem atenção", "Filas locais e dependências da cascata", "Niche", "Blueprints", "Brand", "Script", "Title",
@@ -140,6 +140,66 @@ UI_TRANSLATIONS: dict[str, dict[str, str]] = {
140
140
  }
141
141
 
142
142
 
143
+ _TAB_LABELS = (
144
+ "Blueprints", "Brandings", "Pesquisa pública", "Cadastro manual", "Contas cadastradas", "Biblioteca",
145
+ "Importar do YouTube", "Canais em lote gmail", "Criar vídeo", "Vídeos", "Novo roteiro/letra", "Histórico guardado",
146
+ "Clusters encontrados", "Regras de associação", "Dados analisados", "Upload ficheiro", "URL de vídeo", "Vídeos gerados",
147
+ "Pasta local", "Código Python", "Upload convencional", "Upload directo", "Postiz", "Upload-Post", "API Keys",
148
+ "Teste de vozes", "Serviços e modelos", "Fontes de materiais", "Client MCP", "Servidor MCP", "Skill",
149
+ )
150
+
151
+ TAB_TRANSLATIONS: dict[str, dict[str, str]] = {
152
+ "pt": {label: label for label in _TAB_LABELS},
153
+ "en": {
154
+ "Blueprints": "Blueprints", "Brandings": "Brandings", "Pesquisa pública": "Public search", "Cadastro manual": "Manual registration", "Contas cadastradas": "Registered accounts", "Biblioteca": "Library", "Importar do YouTube": "Import from YouTube", "Canais em lote gmail": "Bulk Gmail channels", "Criar vídeo": "Create video", "Vídeos": "Videos", "Novo roteiro/letra": "New script/lyrics", "Histórico guardado": "Saved history", "Clusters encontrados": "Found clusters", "Regras de associação": "Association rules", "Dados analisados": "Analyzed data", "Upload ficheiro": "Upload file", "URL de vídeo": "Video URL", "Vídeos gerados": "Generated videos", "Pasta local": "Local folder", "Código Python": "Python code", "Upload convencional": "Conventional upload", "Upload directo": "Direct upload", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "API Keys", "Teste de vozes": "Voice testing", "Serviços e modelos": "Services and models", "Fontes de materiais": "Media sources", "Client MCP": "MCP client", "Servidor MCP": "MCP server", "Skill": "Skill",
155
+ },
156
+ "zh": {
157
+ "Blueprints": "蓝图", "Brandings": "品牌", "Pesquisa pública": "公开搜索", "Cadastro manual": "手动注册", "Contas cadastradas": "已注册账户", "Biblioteca": "库", "Importar do YouTube": "从 YouTube 导入", "Canais em lote gmail": "Gmail 批量频道", "Criar vídeo": "创建视频", "Vídeos": "视频", "Novo roteiro/letra": "新建脚本/歌词", "Histórico guardado": "已保存历史", "Clusters encontrados": "找到的聚类", "Regras de associação": "关联规则", "Dados analisados": "分析数据", "Upload ficheiro": "上传文件", "URL de vídeo": "视频 URL", "Vídeos gerados": "已生成视频", "Pasta local": "本地文件夹", "Código Python": "Python 代码", "Upload convencional": "常规上传", "Upload directo": "直接上传", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "API 密钥", "Teste de vozes": "语音测试", "Serviços e modelos": "服务与模型", "Fontes de materiais": "媒体来源", "Client MCP": "MCP 客户端", "Servidor MCP": "MCP 服务器", "Skill": "技能",
158
+ },
159
+ "de": {
160
+ "Blueprints": "Blueprints", "Brandings": "Brandings", "Pesquisa pública": "Öffentliche Suche", "Cadastro manual": "Manuelle Registrierung", "Contas cadastradas": "Registrierte Konten", "Biblioteca": "Bibliothek", "Importar do YouTube": "Von YouTube importieren", "Canais em lote gmail": "Gmail-Kanäle im Stapel", "Criar vídeo": "Video erstellen", "Vídeos": "Videos", "Novo roteiro/letra": "Neues Skript/Liedtext", "Histórico guardado": "Gespeicherter Verlauf", "Clusters encontrados": "Gefundene Cluster", "Regras de associação": "Assoziationsregeln", "Dados analisados": "Analysierte Daten", "Upload ficheiro": "Datei hochladen", "URL de vídeo": "Video-URL", "Vídeos gerados": "Erstellte Videos", "Pasta local": "Lokaler Ordner", "Código Python": "Python-Code", "Upload convencional": "Herkömmlicher Upload", "Upload directo": "Direkter Upload", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "API-Schlüssel", "Teste de vozes": "Stimmtest", "Serviços e modelos": "Dienste und Modelle", "Fontes de materiais": "Medienquellen", "Client MCP": "MCP-Client", "Servidor MCP": "MCP-Server", "Skill": "Skill",
161
+ },
162
+ "vi": {
163
+ "Blueprints": "Blueprint", "Brandings": "Thương hiệu", "Pesquisa pública": "Tìm kiếm công khai", "Cadastro manual": "Đăng ký thủ công", "Contas cadastradas": "Tài khoản đã đăng ký", "Biblioteca": "Thư viện", "Importar do YouTube": "Nhập từ YouTube", "Canais em lote gmail": "Kênh Gmail hàng loạt", "Criar vídeo": "Tạo video", "Vídeos": "Video", "Novo roteiro/letra": "Kịch bản/lời bài hát mới", "Histórico guardado": "Lịch sử đã lưu", "Clusters encontrados": "Cụm được tìm thấy", "Regras de associação": "Quy tắc liên kết", "Dados analisados": "Dữ liệu đã phân tích", "Upload ficheiro": "Tải tệp lên", "URL de vídeo": "URL video", "Vídeos gerados": "Video đã tạo", "Pasta local": "Thư mục cục bộ", "Código Python": "Mã Python", "Upload convencional": "Tải lên thông thường", "Upload directo": "Tải lên trực tiếp", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "Khóa API", "Teste de vozes": "Kiểm tra giọng nói", "Serviços e modelos": "Dịch vụ và mô hình", "Fontes de materiais": "Nguồn phương tiện", "Client MCP": "Máy khách MCP", "Servidor MCP": "Máy chủ MCP", "Skill": "Kỹ năng",
164
+ },
165
+ "tr": {
166
+ "Blueprints": "Blueprint'ler", "Brandings": "Markalar", "Pesquisa pública": "Herkese açık arama", "Cadastro manual": "Manuel kayıt", "Contas cadastradas": "Kayıtlı hesaplar", "Biblioteca": "Kütüphane", "Importar do YouTube": "YouTube'dan içe aktar", "Canais em lote gmail": "Toplu Gmail kanalları", "Criar vídeo": "Video oluştur", "Vídeos": "Videolar", "Novo roteiro/letra": "Yeni senaryo/şarkı sözü", "Histórico guardado": "Kayıtlı geçmiş", "Clusters encontrados": "Bulunan kümeler", "Regras de associação": "Birliktelik kuralları", "Dados analisados": "Analiz edilen veriler", "Upload ficheiro": "Dosya yükle", "URL de vídeo": "Video URL'si", "Vídeos gerados": "Oluşturulan videolar", "Pasta local": "Yerel klasör", "Código Python": "Python kodu", "Upload convencional": "Geleneksel yükleme", "Upload directo": "Doğrudan yükleme", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "API anahtarları", "Teste de vozes": "Ses testi", "Serviços e modelos": "Hizmetler ve modeller", "Fontes de materiais": "Medya kaynakları", "Client MCP": "MCP istemcisi", "Servidor MCP": "MCP sunucusu", "Skill": "Beceri",
167
+ },
168
+ "ru": {
169
+ "Blueprints": "Blueprints", "Brandings": "Брендинг", "Pesquisa pública": "Публичный поиск", "Cadastro manual": "Ручная регистрация", "Contas cadastradas": "Зарегистрированные аккаунты", "Biblioteca": "Библиотека", "Importar do YouTube": "Импорт из YouTube", "Canais em lote gmail": "Массовые каналы Gmail", "Criar vídeo": "Создать видео", "Vídeos": "Видео", "Novo roteiro/letra": "Новый сценарий/текст", "Histórico guardado": "Сохранённая история", "Clusters encontrados": "Найденные кластеры", "Regras de associação": "Правила ассоциаций", "Dados analisados": "Анализ данных", "Upload ficheiro": "Загрузить файл", "URL de vídeo": "URL видео", "Vídeos gerados": "Созданные видео", "Pasta local": "Локальная папка", "Código Python": "Код Python", "Upload convencional": "Обычная загрузка", "Upload directo": "Прямая загрузка", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "Ключи API", "Teste de vozes": "Тест голосов", "Serviços e modelos": "Сервисы и модели", "Fontes de materiais": "Источники материалов", "Client MCP": "Клиент MCP", "Servidor MCP": "Сервер MCP", "Skill": "Навык",
170
+ },
171
+ "es": {
172
+ "Blueprints": "Blueprints", "Brandings": "Marcas", "Pesquisa pública": "Búsqueda pública", "Cadastro manual": "Registro manual", "Contas cadastradas": "Cuentas registradas", "Biblioteca": "Biblioteca", "Importar do YouTube": "Importar de YouTube", "Canais em lote gmail": "Canales Gmail por lotes", "Criar vídeo": "Crear vídeo", "Vídeos": "Vídeos", "Novo roteiro/letra": "Nuevo guion/letra", "Histórico guardado": "Historial guardado", "Clusters encontrados": "Clústeres encontrados", "Regras de associação": "Reglas de asociación", "Dados analisados": "Datos analizados", "Upload ficheiro": "Subir archivo", "URL de vídeo": "URL del vídeo", "Vídeos gerados": "Vídeos generados", "Pasta local": "Carpeta local", "Código Python": "Código Python", "Upload convencional": "Carga convencional", "Upload directo": "Carga directa", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "Claves API", "Teste de vozes": "Prueba de voces", "Serviços e modelos": "Servicios y modelos", "Fontes de materiais": "Fuentes de medios", "Client MCP": "Cliente MCP", "Servidor MCP": "Servidor MCP", "Skill": "Habilidad",
173
+ },
174
+ "id": {
175
+ "Blueprints": "Blueprint", "Brandings": "Branding", "Pesquisa pública": "Pencarian publik", "Cadastro manual": "Pendaftaran manual", "Contas cadastradas": "Akun terdaftar", "Biblioteca": "Pustaka", "Importar do YouTube": "Impor dari YouTube", "Canais em lote gmail": "Kanal Gmail massal", "Criar vídeo": "Buat video", "Vídeos": "Video", "Novo roteiro/letra": "Skrip/lirik baru", "Histórico guardado": "Riwayat tersimpan", "Clusters encontrados": "Cluster ditemukan", "Regras de associação": "Aturan asosiasi", "Dados analisados": "Data yang dianalisis", "Upload ficheiro": "Unggah file", "URL de vídeo": "URL video", "Vídeos gerados": "Video yang dibuat", "Pasta local": "Folder lokal", "Código Python": "Kode Python", "Upload convencional": "Unggah konvensional", "Upload directo": "Unggah langsung", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "Kunci API", "Teste de vozes": "Uji suara", "Serviços e modelos": "Layanan dan model", "Fontes de materiais": "Sumber media", "Client MCP": "Klien MCP", "Servidor MCP": "Server MCP", "Skill": "Keahlian",
176
+ },
177
+ "it": {
178
+ "Blueprints": "Blueprint", "Brandings": "Branding", "Pesquisa pública": "Ricerca pubblica", "Cadastro manual": "Registrazione manuale", "Contas cadastradas": "Account registrati", "Biblioteca": "Libreria", "Importar do YouTube": "Importa da YouTube", "Canais em lote gmail": "Canali Gmail in batch", "Criar vídeo": "Crea video", "Vídeos": "Video", "Novo roteiro/letra": "Nuovo copione/testo", "Histórico guardado": "Cronologia salvata", "Clusters encontrados": "Cluster trovati", "Regras de associação": "Regole di associazione", "Dados analisados": "Dati analizzati", "Upload ficheiro": "Carica file", "URL de vídeo": "URL video", "Vídeos gerados": "Video generati", "Pasta local": "Cartella locale", "Código Python": "Codice Python", "Upload convencional": "Caricamento convenzionale", "Upload directo": "Caricamento diretto", "Postiz": "Postiz", "Upload-Post": "Upload-Post", "API Keys": "Chiavi API", "Teste de vozes": "Test delle voci", "Serviços e modelos": "Servizi e modelli", "Fontes de materiais": "Fonti multimediali", "Client MCP": "Client MCP", "Servidor MCP": "Server MCP", "Skill": "Skill",
179
+ },
180
+ }
181
+
182
+ for _language_code, _tab_translation in TAB_TRANSLATIONS.items():
183
+ UI_TRANSLATIONS[_language_code].update(_tab_translation)
184
+
185
+
186
+ TUTORIAL_TRANSLATIONS: dict[str, dict[str, str]] = {
187
+ "pt": {"Tutorial Supabase": "Tutorial Supabase", "Guia de configuração do Supabase para automações com n8n.": "Guia de configuração do Supabase para automações com n8n."},
188
+ "en": {"Tutorial Supabase": "Supabase Tutorial", "Guia de configuração do Supabase para automações com n8n.": "Supabase setup guide for n8n automations."},
189
+ "zh": {"Tutorial Supabase": "Supabase 教程", "Guia de configuração do Supabase para automações com n8n.": "用于 n8n 自动化的 Supabase 配置指南。"},
190
+ "de": {"Tutorial Supabase": "Supabase-Tutorial", "Guia de configuração do Supabase para automações com n8n.": "Anleitung zur Supabase-Konfiguration für n8n-Automatisierungen."},
191
+ "vi": {"Tutorial Supabase": "Hướng dẫn Supabase", "Guia de configuração do Supabase para automações com n8n.": "Hướng dẫn cấu hình Supabase cho tự động hóa n8n."},
192
+ "tr": {"Tutorial Supabase": "Supabase Eğitimi", "Guia de configuração do Supabase para automações com n8n.": "n8n otomasyonları için Supabase yapılandırma rehberi."},
193
+ "ru": {"Tutorial Supabase": "Руководство Supabase", "Guia de configuração do Supabase para automações com n8n.": "Руководство по настройке Supabase для автоматизации n8n."},
194
+ "es": {"Tutorial Supabase": "Tutorial de Supabase", "Guia de configuração do Supabase para automações com n8n.": "Guía de configuración de Supabase para automatizaciones con n8n."},
195
+ "id": {"Tutorial Supabase": "Tutorial Supabase", "Guia de configuração do Supabase para automações com n8n.": "Panduan konfigurasi Supabase untuk otomatisasi n8n."},
196
+ "it": {"Tutorial Supabase": "Tutorial Supabase", "Guia de configuração do Supabase para automações com n8n.": "Guida alla configurazione di Supabase per le automazioni n8n."},
197
+ }
198
+
199
+ for _language_code, _tutorial_translation in TUTORIAL_TRANSLATIONS.items():
200
+ UI_TRANSLATIONS[_language_code].update(_tutorial_translation)
201
+
202
+
143
203
  def ui_text(value: str, language: Any = "pt") -> str:
144
204
  code = language_code(language)
145
205
  return UI_TRANSLATIONS.get(code, {}).get(value, value)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.2.83",
3
+ "version": "0.2.85",
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",
@@ -0,0 +1,61 @@
1
+ # Guia para criar e configurar uma conta Supabase
2
+
3
+ Este guia mostra como criar uma conta gratuita no Supabase e preparar as tabelas e o armazenamento necessários para utilizar a base de dados com automações n8n.
4
+
5
+ Comece por abrir o [Supabase](https://supabase.com/) e criar uma nova conta gratuita.
6
+
7
+ ## 1. Criar as tabelas da base de dados
8
+
9
+ Quando a conta estiver pronta, abra o `Table Editor`.
10
+
11
+ ![Table Editor](https://github.com/user-attachments/assets/03f22c7c-18d5-4de0-81e0-f4fcee127647)
12
+
13
+ Para criar uma tabela nova, clique no botão `New table`.
14
+
15
+ ![New table](https://github.com/user-attachments/assets/2a8e59f5-d20c-470e-abc8-c15966f438c1)
16
+
17
+ ### Criar a tabela `plans`
18
+
19
+ A tabela `plans` deve ter quatro campos:
20
+
21
+ 1. `id` (`int8`) — chave primária; utilize as definições predefinidas.
22
+ 2. `period_type` (`text`) — identifica os períodos. Os valores possíveis são `quarter`, `month` e `week`.
23
+ 3. `period_index` (`text`) — guarda o índice do período, por exemplo `2025-Q2` para o segundo trimestre de 2025.
24
+ 4. `plan_value` (`text`) — guarda o valor efectivo do plano para o período indicado.
25
+
26
+ ![Campos da tabela plans](https://github.com/user-attachments/assets/d308dd34-8fde-4cd4-8347-d2e97db1d648)
27
+
28
+ Quando terminar, clique em `Save`.
29
+
30
+ ### Criar a tabela `posts`
31
+
32
+ Crie a tabela `posts` com os seguintes campos:
33
+
34
+ 1. `id` (`int8`) — chave primária; utilize as definições predefinidas.
35
+ 2. `created_at` (`timestamp`) — valor predefinido `now()`.
36
+ 3. `post_summary` (`text`) — guarda o resumo de cada publicação para manter consistência entre publicações.
37
+ 4. `image_path` (`text`) — guarda o caminho da imagem gerada e carregada para o bucket do Supabase Storage.
38
+
39
+ ![Campos da tabela posts](https://github.com/user-attachments/assets/917d2556-eb3d-48a1-9095-65332882fd0c)
40
+
41
+ ## 2. Criar o bucket de Storage
42
+
43
+ No menu do Supabase, clique em `Storage`.
44
+
45
+ ![Menu Storage](https://github.com/user-attachments/assets/e7cbd532-b7bd-4404-a169-2965932be24e)
46
+
47
+ Clique em `New bucket`.
48
+
49
+ ![New bucket](https://github.com/user-attachments/assets/9deca74e-1798-4733-8593-0b5593857ab4)
50
+
51
+ Dê ao bucket o nome `instagram-images` ou outro nome compatível com as regras de nomenclatura. Guarde esse nome, porque será necessário configurá-lo no n8n.
52
+
53
+ ![Nome do bucket](https://github.com/user-attachments/assets/10cf6666-63bb-4404-a169-2965932be24e)
54
+
55
+ Depois, carregue a imagem de referência para o bucket usando o botão `Upload files`.
56
+
57
+ ![Upload files](https://github.com/user-attachments/assets/6cc83262-b92c-4f74-86c3-a45c9c0456f8)
58
+
59
+ A conta Supabase está pronta para ser utilizada pela automação.
60
+
61
+ Fonte original: [guide-supabase.md no GitHub](https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-supabase.md)