@danhachuel/thunderbolt 0.3.69 → 0.3.70

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Este manual descreve a instalação local da UI Thunderbolt, baseada no MoneyPrinterTurbo, utilizando o pacote npm `@danhachuel/thunderbolt`. O fluxo recomendado instala automaticamente o ambiente Python, as dependências da aplicação, as dependências do MoneyPrinterTurbo, o Streamlit e o suporte FFmpeg através de `imageio-ffmpeg`.
4
4
 
5
- > **Versão deste manual:** 0.3.69
5
+ > **Versão deste manual:** 0.3.70
6
6
  > **Pacote npm:** `@danhachuel/thunderbolt`
7
7
  > **Porta padrão da UI:** `localhost:3030`
8
8
  > **Repositório:** [github.com/DanHachuel/thunderbolt](https://github.com/DanHachuel/thunderbolt)
@@ -128,13 +128,13 @@ Execute:
128
128
  Windows PowerShell ou MobaXterm:
129
129
 
130
130
  ```powershell
131
- npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.69 install
131
+ npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.70 install
132
132
  ```
133
133
 
134
134
  Linux/macOS:
135
135
 
136
136
  ```bash
137
- npx --yes --prefer-online @danhachuel/thunderbolt@0.3.69 install
137
+ npx --yes --prefer-online @danhachuel/thunderbolt@0.3.70 install
138
138
  ```
139
139
 
140
140
  A instalação normal é **segura para actualizações**: preserva `storage`, Blueprints, Brandings, configurações e artefactos do utilizador. Remove apenas `.venv`, o clone técnico do MoneyPrinterTurbo e dependências que serão recriadas. Uma pasta antiga sem dados do utilizador, como `C:\Users\<utilizador>\AppData\Local\hermes` da tentativa incompleta, pode ser removida; uma pasta antiga que contenha Blueprints, Brandings ou storage é preservada e apenas avisada no terminal. Feche processos Python, Node, Streamlit e MobaXterm que estejam a usar as pastas antes de executar.
@@ -142,7 +142,7 @@ A instalação normal é **segura para actualizações**: preserva `storage`, Bl
142
142
  Se quiser apagar absolutamente tudo de forma intencional, use o comando destrutivo separado:
143
143
 
144
144
  ```powershell
145
- npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.69 install --purge-data
145
+ npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.70 install --purge-data
146
146
  ```
147
147
 
148
148
  O parâmetro `--purge-data` apaga Blueprints, Brandings, configurações, storage e artefactos locais. Não o use numa actualização normal.
@@ -391,15 +391,17 @@ Em **Configuração API > API Keys**, o expander **LLM — providers e modelos**
391
391
 
392
392
  Dentro do expander **LLM — providers e modelos**, acima dos cartões de providers, está o card **Limite LLM NVIDIA NIM**. Ele permite ligar **Activar limitador NVIDIA NIM — 40 RPM**. O valor padrão é 40 pedidos numa janela de 60 segundos. O contador é persistido localmente por cartão/chave e aplica-se à criação manual, aos workers e às automações. A opção vem desligada para não alterar instalações existentes; ligue-a quando o endpoint activo for NVIDIA NIM e a sua conta tiver esse limite.
393
393
 
394
- O expander **Imagem e Video** substitui o antigo bloco isolado da Nano Banana. Os cartões disponíveis são **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Em cada cartão, configure a API key/token, o modelo, a Base URL, o estado **Provider activo**, a participação no **Pool Imagem**, a participação no **Pool Vídeo** e a prioridade. **Image Size** e **Aspect Ratio** não aparecem nos cartões e não são editáveis: são defaults internos aplicados automaticamente no prompt da geração. O botão **Testar Chamada API** faz apenas uma verificação read-only; não inicia geração de imagem ou vídeo.
394
+ O expander **Imagem e Video Montagem/MoviePy** aparece primeiro dentro de **Configuração API > API Keys**. Ele reúne as fontes usadas pela montagem local de vídeo com MoviePy/FFmpeg, incluindo os cartões de Pexels, Pixabay, Coverr, WaveSpeed AI, LoomLoom, TwelveLabs e ficheiro local. Pode manter várias API keys do mesmo provedor, seleccionar a fonte activa e testar cada fonte remota; a opção local não apresenta um diagnóstico remoto artificial.
395
+
396
+ Logo abaixo fica o expander **Imagem e Video IA**, que substitui o antigo bloco isolado da Nano Banana. Os cartões disponíveis são **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Em cada cartão, configure a API key/token, o modelo, a Base URL, o estado **Provider activo**, a participação no **Pool Imagem**, a participação no **Pool Vídeo** e a prioridade. **Image Size** e **Aspect Ratio** não aparecem nos cartões e não são editáveis: são defaults internos aplicados automaticamente no prompt da geração. O botão **Testar Chamada API** faz apenas uma verificação read-only; não inicia geração de imagem ou vídeo.
395
397
 
396
398
  A área **Voz, TTS e música — Azure Speech, restantes serviços e Suno** está dividida em cartões independentes para **Azure Speech**, **ElevenLabs**, **SiliconFlow**, **MiniMax TTS**, **Chatterbox**, **Sonilo** e **Suno**. Cada cartão agrupa apenas as credenciais, parâmetros e diagnóstico do serviço correspondente. O Suno aparece num cartão próprio porque é uma integração de criação musical, enquanto os restantes providers são serviços de voz/TTS.
397
399
 
398
- Na subaba **Imagem e Video**, a lista **Provider de media** inclui **FAL AI, KIE AI, Agnes AI, Nano Banana, Replicate AI, Pollinations.ai, Hugging Face Inference API, InferencePort Proxy e HeyGen** para a rota Full IA. O cartão **HeyGen** pede a API key, o **Avatar ID** e, opcionalmente, o **Voice ID**; a opção **Provider activo**, **Pool Vídeo**, **Prioridade** e **Testar Chamada API** funcionam como nos restantes cartões. O teste HeyGen usa apenas `GET /v3/users/me` e não consome uma geração. Nano Banana e Hugging Face podem permanecer no catálogo sem serem seleccionados para vídeo quando o cartão não declara capacidade de vídeo.
400
+ No expander **Imagem e Video IA**, a lista **Provider de media** inclui **FAL AI, KIE AI, Agnes AI, Nano Banana, Replicate AI, Pollinations.ai, Hugging Face Inference API, InferencePort Proxy e HeyGen** para a rota Full IA. O cartão **HeyGen** pede a API key, o **Avatar ID** e, opcionalmente, o **Voice ID**; a opção **Provider activo**, **Pool Vídeo**, **Prioridade** e **Testar Chamada API** funcionam como nos restantes cartões. O teste HeyGen usa apenas `GET /v3/users/me` e não consome uma geração. Nano Banana e Hugging Face podem permanecer no catálogo sem serem seleccionados para vídeo quando o cartão não declara capacidade de vídeo.
399
401
 
400
402
  ### API Tiktok — várias aplicações
401
403
 
402
- Em **Configuração API > API Tiktok**, situada entre **Contas Google** e **Fontes de Materiais**, cada aplicação TikTok é configurada num card independente. O card contém exclusivamente **TikTok Client ID** e **TikTok Client Secret** e apresenta os botões **Testar chamada API**, **Guardar card** e **Apagar card**. Use **Adicionar nova API** no final da lista para criar outra aplicação sem substituir as anteriores.
404
+ Em **Configuração API > API Tiktok**, situada entre **Contas Google** e **AI Influencers**, cada aplicação TikTok é configurada num card independente. O card contém exclusivamente **TikTok Client ID** e **TikTok Client Secret** e apresenta os botões **Testar chamada API**, **Guardar card** e **Apagar card**. Use **Adicionar nova API** no final da lista para criar outra aplicação sem substituir as anteriores.
403
405
 
404
406
  O botão de teste é read-only. Sem um token OAuth TikTok já autorizado, o Thunderbolt informa que é necessário concluir o Playground/autorização do TikTok for Developers, sem inventar um resultado nem enviar credenciais. O token e a autorização não são campos dos cards. Instalações que ainda tenham `tiktok_client_key` e `tiktok_client_secret` são migradas automaticamente para o primeiro card; o adapter usa o primeiro card completo e mantém fallback para os campos antigos.
405
407
 
@@ -568,7 +570,7 @@ Ao abrir a página, o Thunderbolt não prepara dados públicos, não descarrega
568
570
 
569
571
  Os parâmetros da UI são número de clusters entre 2 e 10, suporte mínimo entre 0,01 e 0,50, país, engagement, intervalo de datas e tags, todos dentro da área principal da aba. O núcleo normaliza os dados, calcula engagement, aplica filtros, faz transformação logarítmica e standardização, executa K-Means e calcula itemsets/regras com FP-Growth. Não são apresentados resultados até ao primeiro clique em **Analisar Nichos**; o mesmo botão aplica alterações posteriores aos filtros. Os resultados são DataFrames de clusters, itemsets frequentes, regras de associação e dados analisados; o gráfico de dispersão é criado nativamente com Plotly.
570
572
 
571
- As dependências adicionais — `scikit-learn`, `mlxtend`, `plotly`, `seaborn`, `matplotlib` e `kagglehub` — são instaladas pelo procedimento normal de `npx`. Em instalações existentes, execute novamente `npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.69 install`; o instalador detecta e reutiliza o que já estiver válido.
573
+ As dependências adicionais — `scikit-learn`, `mlxtend`, `plotly`, `seaborn`, `matplotlib` e `kagglehub` — são instaladas pelo procedimento normal de `npx`. Em instalações existentes, execute novamente `npx.cmd --yes --prefer-online @danhachuel/thunderbolt@0.3.70 install`; o instalador detecta e reutiliza o que já estiver válido.
572
574
 
573
575
  ### Niche Finder Apify
574
576
 
package/README.md CHANGED
@@ -104,19 +104,19 @@ A subaba **Tutorial Supabase** apresenta o guia de criação das tabelas `plans`
104
104
 
105
105
  O conteúdo interno das páginas também é localizado, não apenas a navegação: títulos, subtítulos, descrições, labels de campos, placeholders, opções de selectores, botões, avisos, mensagens de sucesso/erro, estados vazios, métricas, expanderes e blocos Markdown/HTML são traduzidos no idioma seleccionado através da camada global de conteúdo. Valores técnicos, chaves de estado, IDs de formulários, nomes de ficheiros, URLs e dados introduzidos pelo utilizador permanecem inalterados.
106
106
 
107
- 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, **JewelMusic/Pushtunes/ytmusicapi**, **API Keys/Teste de vozes**, **Serviços e modelos/Fontes de materiais**, **Client MCP/Servidor MCP/Skill** e **Notificações/Geral/Telegram**. As chaves técnicas e a ordem funcional permanecem inalteradas.
107
+ 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, **JewelMusic/Pushtunes/ytmusicapi**, **API Keys/Teste de vozes**, os expanders **Imagem e Video Montagem/MoviePy** e **Imagem e Video IA**, **Client MCP/Servidor MCP/Skill** e **Notificações/Geral/Telegram**. As chaves técnicas e a ordem funcional permanecem inalteradas.
108
108
 
109
- Dentro de **Configurações > Contas Google**, a UI contém os cartões expansíveis de contas Google/YouTube, `sessionInfo` por conta, documentos de Upload directo, o formulário **Adicionar outra conta Gmail**, o bloco global de `INNERTUBE_API_KEY` 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, **Imagem e Video**, TikTok e Postiz.
109
+ Dentro de **Configurações > Contas Google**, a UI contém os cartões expansíveis de contas Google/YouTube, `sessionInfo` por conta, documentos de Upload directo, o formulário **Adicionar outra conta Gmail**, o bloco global de `INNERTUBE_API_KEY` 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, os expanders **Imagem e Video Montagem/MoviePy** e **Imagem e Video IA**, TikTok e Postiz.
110
110
 
111
- Em **Configuração API > API Keys**, a subaba **Fontes de materiais** segue o padrão do MoneyPrinterTurbo: seleccione uma fonte — **Pexels**, **Pixabay**, **Coverr**, **WaveSpeed AI**, **LoomLoom**, **TwelveLabs** ou **Ficheiros locais** — e introduza as API keys dessa fonte. **Adicionar outra chave** cria outra linha para a mesma fonte; as listas são guardadas separadamente por fonte, deduplicadas e exportadas para o `config.toml` do motor como arrays para rotação. A subaba não expõe endpoint, proxy, qualidade, codec, FFmpeg, Whisper, directório ou filtros técnicos: esses valores permanecem internalizados e a fonte local não requer credencial.
111
+ Em **Configuração API > API Keys**, o expander **Imagem e Video Montagem/MoviePy** segue o padrão do MoneyPrinterTurbo: seleccione uma fonte — **Pexels**, **Pixabay**, **Coverr**, **WaveSpeed AI**, **LoomLoom**, **TwelveLabs** ou **Ficheiros locais** — e introduza as API keys dessa fonte. **Adicionar outra chave** cria outra linha para a mesma fonte; as listas são guardadas separadamente por fonte, deduplicadas e exportadas para o `config.toml` do motor como arrays para rotação. O expander não expõe endpoint, proxy, qualidade, codec, FFmpeg, Whisper, directório ou filtros técnicos: esses valores permanecem internalizados e a fonte local não requer credencial. Ele aparece acima do expander **Imagem e Video IA**.
112
112
 
113
- A subaba **Serviços e modelos** mantém as credenciais de providers e serviços que não são fontes de materiais. A chave `gemini_image_api_key` da Nano Banana continua separada de `gemini_api_key` do LLM textual. `INNERTUBE_API_KEY` permanece exclusivamente em **Configurações > Contas Google**, no bloco de configuração global fora dos cartões, e não é duplicada em API Keys.
113
+ O expander **Imagem e Video IA** mantém as credenciais dos providers de geração que não são fontes de montagem. A chave `gemini_image_api_key` da Nano Banana continua separada de `gemini_api_key` do LLM textual. `INNERTUBE_API_KEY` permanece exclusivamente em **Configurações > Contas Google**, no bloco de configuração global fora dos cartões, e não é duplicada em API Keys.
114
114
 
115
115
  A área expansível **Voz, TTS e música — Azure Speech, restantes serviços e Suno** está organizada em cartões independentes para **Azure Speech**, **ElevenLabs**, **SiliconFlow**, **MiniMax TTS**, **Chatterbox**, **Sonilo** e **Suno**. Cada cartão contém apenas os campos do respectivo serviço e o seu próprio botão de diagnóstico; o Suno fica visualmente separado por ser um agente de criação musical, não um provider TTS. Em **Pipeline Vídeos > Criação de Vídeos > Configurações de áudio**, o modo **Upload** disponibiliza um uploader de narração, botão para guardar o ficheiro, pré-visualização e validação antes da criação. O caminho é passado ao MoneyPrinterTurbo através de `--custom-audio-file`, conforme o fluxo oficial documentado no [MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo).
116
116
 
117
117
  Nos cartões de **LLM — providers e modelos**, o campo **Prioridade** define a ordem de tentativa do pool textual. O valor **1** é usado primeiro; em quota, timeout, erro de transporte ou falha transitória, o router tenta a prioridade **2**, depois **3** e assim sucessivamente. O checkbox antigo **LLM principal** deixou de existir. O card **Limite LLM NVIDIA NIM** fica dentro deste mesmo expander, acima dos cartões de providers, e os seus controlos continuam partilhados pela UI, pipeline e automações. Quando **LLM Telegram** está marcado, o cartão fica reservado exclusivamente às notificações Telegram: o campo Prioridade é desactivado e o cartão é excluído do pool textual normal. A chave interna de cartão activo continua apenas como espelho de compatibilidade para instalações antigas.
118
118
 
119
- O expander **Imagem e Video** reúne cartões independentes para **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Cada cartão permite configurar API key/token quando necessário, modelo, Base URL, activar o provider, escolher participação no **Pool Imagem** e/ou **Pool Vídeo**, definir prioridade e executar **Testar Chamada API**. Os parâmetros técnicos de composição, como **Image Size** e **Aspect Ratio**, não são apresentados nem editáveis nos cartões: ficam como defaults internos e são acrescentados automaticamente ao prompt da geração de imagem e/ou vídeo. O InferencePort usa por defeito `http://localhost:8080/v1` e não exige chave; o Cloudflare requer Account ID; os restantes providers devem ser preenchidos de acordo com a conta e endpoint disponibilizados pelo serviço.
119
+ O expander **Imagem e Video IA** reúne cartões independentes para **Nano Banana**, **Pollinations.ai**, **Agnes AI**, **Hugging Face Inference API**, **Cloudflare Workers AI**, **InferencePort Proxy**, **阿里云 (Alibaba Cloud Model Studio)**, **KIE AI** e **FAL AI**. Cada cartão permite configurar API key/token quando necessário, modelo, Base URL, activar o provider, escolher participação no **Pool Imagem** e/ou **Pool Vídeo**, definir prioridade e executar **Testar Chamada API**. Os parâmetros técnicos de composição, como **Image Size** e **Aspect Ratio**, não são apresentados nem editáveis nos cartões: ficam como defaults internos e são acrescentados automaticamente ao prompt da geração de imagem e/ou vídeo. O InferencePort usa por defeito `http://localhost:8080/v1` e não exige chave; o Cloudflare requer Account ID; os restantes providers devem ser preenchidos de acordo com a conta e endpoint disponibilizados pelo serviço.
120
120
 
121
121
  O router mantém três pools independentes: **LLM textual**, **imagem** e **vídeo**. O failover só passa ao cartão seguinte em quota, timeout, erro de transporte ou HTTP transitório; erros de credencial, endpoint/modelo inexistente ou payload inválido ficam registados e não são mascarados por um provider diferente. O pool de vídeo externo é opcional: quando desligado, o worker continua a usar o motor local MoneyPrinterTurbo; quando ligado, usa apenas os cartões marcados para Pool Vídeo.
122
122
 
@@ -323,7 +323,7 @@ def render_ai_influencer_content(settings: dict[str, Any]) -> None:
323
323
  current = str(st.session_state.get("influencer_selected_id") or options[0])
324
324
  cards, provider_options = _provider_options(settings, "image")
325
325
  if not cards:
326
- st.warning("Não existem providers activos no pool de imagem. Configure um provider em Configuração API > API Keys > Imagem e Video.")
326
+ st.warning("Não existem providers activos no pool de imagem. Configure um provider em Configuração API > API Keys > Imagem e Video IA.")
327
327
  with st.form("influencer_image_content_form"):
328
328
  influencer_id = st.selectbox("Personagem", options, index=options.index(current) if current in options else 0, format_func=lambda value: _influencer_label(influencers, value), key="content_image_influencer")
329
329
  assets = repository.list_assets(influencer_id)
@@ -371,7 +371,7 @@ def render_ai_influencer_content(settings: dict[str, Any]) -> None:
371
371
  current = str(st.session_state.get("influencer_selected_id") or options[0])
372
372
  cards, provider_options = _provider_options(settings, "video")
373
373
  if not cards:
374
- st.warning("Não existem providers activos no pool de vídeo. Configure KIE AI, Replicate, FAL AI ou outro provider compatível em Configuração API > API Keys > Imagem e Video.")
374
+ st.warning("Não existem providers activos no pool de vídeo. Configure KIE AI, Replicate, FAL AI ou outro provider compatível em Configuração API > API Keys > Imagem e Video IA.")
375
375
  with st.form("influencer_video_content_form"):
376
376
  influencer_id = st.selectbox("Personagem", options, index=options.index(current) if current in options else 0, format_func=lambda value: _influencer_label(influencers, value), key="content_video_influencer")
377
377
  assets = repository.list_assets(influencer_id)
package/app/main.py CHANGED
@@ -5039,7 +5039,7 @@ def _material_source_card_definition(provider: str) -> dict[str, str]:
5039
5039
  }
5040
5040
 
5041
5041
 
5042
- def _render_material_source_card(settings: dict[str, Any], cards: list[dict[str, Any]], index: int) -> None:
5042
+ def _render_material_source_card(settings: dict[str, Any], cards: list[dict[str, Any]], index: int, *, embedded: bool = False) -> None:
5043
5043
  card = normalize_material_card(cards[index], index)
5044
5044
  cards[index] = card
5045
5045
  card_id = str(card["id"])
@@ -5054,7 +5054,8 @@ def _render_material_source_card(settings: dict[str, Any], cards: list[dict[str,
5054
5054
  st.caption(definition["description"])
5055
5055
  with header_cols[1]:
5056
5056
  _render_credential_status("" if is_local else card.get("api_key"), local=is_local, required=not is_local)
5057
- with st.form(f"material_source_card_form_{card_id}"):
5057
+ card_form = nullcontext() if embedded else st.form(f"material_source_card_form_{card_id}")
5058
+ with card_form:
5058
5059
  content_cols = st.columns(2)
5059
5060
  with content_cols[0]:
5060
5061
  if is_local:
@@ -5085,29 +5086,30 @@ def _render_material_source_card(settings: dict[str, Any], cards: list[dict[str,
5085
5086
  st.rerun()
5086
5087
 
5087
5088
 
5088
- def render_material_source_api_keys(settings: dict[str, Any]) -> None:
5089
- st.subheader("Fontes de Materiais")
5090
- st.caption("Configure cada provedor num cartão independente. Pode repetir o mesmo provedor para guardar várias API keys; a fonte seleccionada será usada pela pipeline.")
5091
- migrated, changed = ensure_material_source_cards(settings)
5092
- cards = [dict(item) for item in migrated.get("material_source_cards", [])]
5093
- if changed:
5094
- write_json("settings.json", settings)
5095
- for index in range(len(cards)):
5096
- _render_material_source_card(settings, cards, index)
5089
+ def render_material_source_api_keys(settings: dict[str, Any], *, embedded: bool = False) -> None:
5090
+ with st.expander("Imagem e Video Montagem/MoviePy", expanded=False):
5091
+ st.caption("Configure as fontes usadas pela montagem de vídeo com MoviePy/FFmpeg num cartão independente. Pode repetir o mesmo provedor para guardar várias API keys; a fonte seleccionada será usada pela pipeline.")
5092
+ migrated, changed = ensure_material_source_cards(settings)
5093
+ cards = [dict(item) for item in migrated.get("material_source_cards", [])]
5094
+ if changed:
5095
+ write_json("settings.json", settings)
5096
+ for index in range(len(cards)):
5097
+ _render_material_source_card(settings, cards, index, embedded=embedded)
5097
5098
 
5098
- st.divider()
5099
- st.markdown("**Adicionar fonte de materiais**")
5100
- provider_codes = [item["code"] for item in material_source_catalog()] + ["local"]
5101
- provider_to_add = st.selectbox(
5102
- "Provedor de materiais",
5103
- provider_codes,
5104
- format_func=lambda value: _material_source_card_definition(value)["label"],
5105
- key="material_new_provider_choice",
5106
- )
5107
- if st.button("Configurar Nova Fonte de Materiais", type="primary", use_container_width=True, key="add_material_source_card"):
5108
- cards.append(new_material_card(provider_to_add, card_id=f"material-{provider_to_add}-{uuid.uuid4().hex[:8]}"))
5109
- _persist_material_source_cards(settings, cards, str(settings.get("material_active_card_id") or ""))
5110
- st.rerun()
5099
+ st.divider()
5100
+ st.markdown("**Adicionar fonte de materiais**")
5101
+ provider_codes = [item["code"] for item in material_source_catalog()] + ["local"]
5102
+ provider_to_add = st.selectbox(
5103
+ "Provedor de materiais",
5104
+ provider_codes,
5105
+ format_func=lambda value: _material_source_card_definition(value)["label"],
5106
+ key="material_new_provider_choice",
5107
+ )
5108
+ add_source_clicked = st.form_submit_button("Configurar Nova Fonte de Materiais", type="primary", use_container_width=True, key="add_material_source_card") if embedded else st.button("Configurar Nova Fonte de Materiais", type="primary", use_container_width=True, key="add_material_source_card")
5109
+ if add_source_clicked:
5110
+ cards.append(new_material_card(provider_to_add, card_id=f"material-{provider_to_add}-{uuid.uuid4().hex[:8]}"))
5111
+ _persist_material_source_cards(settings, cards, str(settings.get("material_active_card_id") or ""))
5112
+ st.rerun()
5111
5113
 
5112
5114
 
5113
5115
  def _api_status_badge(label: str, kind: str = "missing") -> None:
@@ -5490,7 +5492,7 @@ def render_media_provider_cards(settings: dict[str, Any], *, embedded: bool = Fa
5490
5492
  if changed:
5491
5493
  settings.update(migrated)
5492
5494
  write_json("settings.json", settings)
5493
- with st.expander("Imagem e Video", expanded=False):
5495
+ with st.expander("Imagem e Video IA", expanded=False):
5494
5496
  full_ia_labels = ", ".join(media_provider_definition(code).label for code in FULL_IA_VIDEO_PROVIDER_CODES)
5495
5497
  st.caption(f"Configure providers de imagem e vídeo em cartões independentes. O router usa apenas o pool correspondente e faz failover entre providers activos. Pool Full IA: {full_ia_labels}.")
5496
5498
  image_cards = [card for card in cards if card.get("supports_image")]
@@ -5537,7 +5539,7 @@ def render_settings():
5537
5539
  key=f"settings_{key}",
5538
5540
  )
5539
5541
 
5540
- api_keys_tab, google_accounts_tab, tiktok_api_tab, material_sources_tab, ai_influencers_tab, voice_test_tab = render_localized_tabs(["API Keys", "Contas Google", "API Tiktok", "Fontes de Materiais", "AI Influencers", "Teste de Voz"])
5542
+ api_keys_tab, google_accounts_tab, tiktok_api_tab, ai_influencers_tab, voice_test_tab = render_localized_tabs(["API Keys", "Contas Google", "API Tiktok", "AI Influencers", "Teste de Voz"])
5541
5543
 
5542
5544
  with api_keys_tab:
5543
5545
  with st.container(border=True):
@@ -5586,6 +5588,8 @@ def render_settings():
5586
5588
  llm_rpm_limit = int(llm_rpm_settings["llm_rpm_limit"])
5587
5589
  llm_rpm_window_seconds = int(llm_rpm_settings["llm_rpm_window_seconds"])
5588
5590
 
5591
+ render_material_source_api_keys(settings, embedded=True)
5592
+
5589
5593
  render_media_provider_cards(settings, embedded=True)
5590
5594
 
5591
5595
  with st.expander("Voz, TTS e música — Azure Speech, restantes serviços e Suno", expanded=False):
@@ -5758,9 +5762,6 @@ def render_settings():
5758
5762
  with tiktok_api_tab:
5759
5763
  render_tiktok_api_cards(settings)
5760
5764
 
5761
- with material_sources_tab:
5762
- render_material_source_api_keys(settings)
5763
-
5764
5765
  with ai_influencers_tab:
5765
5766
  st.subheader("AI Influencers")
5766
5767
  st.caption("Estado do backend usado por Personagens e Geração de Conteúdo IA. O selector e as credenciais são editados nesta aba, em Banco de Dados Influencers.")
@@ -242,6 +242,8 @@ for _language_code, _api_key_values in _API_KEY_EXPANDER_TRANSLATIONS.items():
242
242
  # provider cards themselves expose their capability labels in the current UI.
243
243
  for _language_code in UI_TRANSLATIONS:
244
244
  UI_TRANSLATIONS[_language_code].setdefault("Imagem e Video", "Imagem e Video")
245
+ UI_TRANSLATIONS[_language_code].setdefault("Imagem e Video IA", "Imagem e Video IA")
246
+ UI_TRANSLATIONS[_language_code].setdefault("Imagem e Video Montagem/MoviePy", "Imagem e Video Montagem/MoviePy")
245
247
 
246
248
 
247
249
  # Labels introduced by the reorganised sidebar.
@@ -982,7 +984,8 @@ _CONTENT_TRANSLATION_ROWS = (
982
984
  ("Niche Finder — execução remota no Kaggle", "Niche Finder — execução remota no Kaggle", "Niche Finder — remote execution on Kaggle", "Niche Finder — Kaggle 远程执行", "Niche Finder — Remoteausführung auf Kaggle", "Niche Finder — chạy từ xa trên Kaggle", "Niche Finder — Kaggle'da uzaktan çalıştırma", "Niche Finder — удалённый запуск на Kaggle", "Niche Finder — ejecución remota en Kaggle", "Niche Finder — eksekusi jarak jauh di Kaggle", "Niche Finder — esecuzione remota su Kaggle"),
983
985
  ("O dataset permanece no Kaggle. O Thunderbolt usa estas credenciais apenas para publicar/executar a kernel e obter os resultados pequenos da análise.", "O dataset permanece no Kaggle. O Thunderbolt usa estas credenciais apenas para publicar/executar a kernel e obter os resultados pequenos da análise.", "The dataset remains on Kaggle. Thunderbolt uses these credentials only to publish/run the kernel and retrieve small analysis results.", "数据集保留在 Kaggle。Thunderbolt 仅使用这些凭证发布/运行 kernel 并获取小型分析结果。", "Der Datensatz bleibt auf Kaggle. Thunderbolt verwendet diese Zugangsdaten nur zum Veröffentlichen/Ausführen des Kernels und zum Abrufen kleiner Analyseergebnisse.", "Dataset vẫn ở Kaggle. Thunderbolt chỉ dùng thông tin xác thực để xuất bản/chạy kernel và lấy kết quả phân tích nhỏ.", "Veri kümesi Kaggle'da kalır. Thunderbolt bu kimlik bilgilerini yalnızca kernel'ı yayınlamak/çalıştırmak ve küçük analiz sonuçlarını almak için kullanır.", "Датасет остаётся на Kaggle. Thunderbolt использует эти данные только для публикации/запуска kernel и получения небольших результатов анализа.", "El dataset permanece en Kaggle. Thunderbolt usa estas credenciales solo para publicar/ejecutar el kernel y obtener pequeños resultados del análisis.", "Dataset tetap di Kaggle. Thunderbolt menggunakan kredensial ini hanya untuk menerbitkan/menjalankan kernel dan mengambil hasil analisis kecil.", "Il dataset resta su Kaggle. Thunderbolt usa queste credenziali solo per pubblicare/eseguire il kernel e ottenere piccoli risultati dell'analisi."),
984
986
  ("Niche Finder — execução através da Apify", "Niche Finder — execução através da Apify", "Niche Finder — execution through Apify", "Niche Finder — 通过 Apify 执行", "Niche Finder — Ausführung über Apify", "Niche Finder — chạy qua Apify", "Niche Finder — Apify üzerinden çalıştırma", "Niche Finder — запуск через Apify", "Niche Finder — ejecución mediante Apify", "Niche Finder — eksekusi melalui Apify", "Niche Finder — esecuzione tramite Apify"),
985
- ("Imagem e Video", "Imagem e Video", "Image and Video", "图像和视频", "Bild und Video", "Hình ảnh và video", "Görüntü ve Video", "Изображение и видео", "Imagen y vídeo", "Gambar dan Video", "Immagine e Video"),
987
+ ("Imagem e Video IA", "Imagem e Video IA", "AI Image and Video", "图像和视频 AI", "KI-Bild und -Video", "Hình ảnh và video AI", "Yapay Zekâ Görüntü ve Video", "ИИ: изображения и видео", "Imagen y vídeo con IA", "Gambar dan Video AI", "Immagini e video IA"),
988
+ ("Imagem e Video Montagem/MoviePy", "Imagem e Video Montagem/MoviePy", "Image and Video Assembly/MoviePy", "图像和视频拼装/MoviePy", "Bild- und Videomontage/MoviePy", "Lắp ráp hình ảnh và video/MoviePy", "Görüntü ve Video Montajı/MoviePy", "Сборка изображения и видео/MoviePy", "Montaje de imagen y vídeo/MoviePy", "Perakitan Gambar dan Video/MoviePy", "Montaggio immagini e video/MoviePy"),
986
989
  ("A Nano Banana gera a imagem final das thumbnails a partir da variante escolhida. A chave é guardada apenas no storage local e é distinta da chave do Gemini usado como LLM textual.", "A Nano Banana gera a imagem final das thumbnails a partir da variante escolhida. A chave é guardada apenas no storage local e é distinta da chave do Gemini usado como LLM textual.", "Nano Banana generates the final thumbnail image from the selected variant. The key is stored locally and is separate from the Gemini key used as a text LLM.", "Nano Banana 根据所选变体生成最终缩略图。密钥仅保存在本地存储中,与用于文本 LLM 的 Gemini 密钥分开。", "Nano Banana erzeugt das endgültige Thumbnail aus der ausgewählten Variante. Der Schlüssel wird nur lokal gespeichert und ist vom Gemini-Schlüssel für das Text-LLM getrennt.", "Nano Banana tạo ảnh thumbnail cuối từ biến thể đã chọn. Key chỉ lưu cục bộ và tách biệt với key Gemini dùng cho LLM văn bản.", "Nano Banana, seçilen varyanttan son küçük resim görselini oluşturur. Anahtar yalnızca yerel depolamada tutulur ve metin LLM'i için kullanılan Gemini anahtarından ayrıdır.", "Nano Banana создаёт финальное изображение миниатюры из выбранного варианта. Ключ хранится локально отдельно от ключа Gemini для текстовой LLM.", "Nano Banana genera la miniatura final a partir de la variante elegida. La clave se guarda localmente y es distinta de la clave de Gemini del LLM de texto.", "Nano Banana menghasilkan gambar thumbnail akhir dari varian yang dipilih. Kunci disimpan secara lokal dan terpisah dari kunci Gemini untuk LLM teks.", "Nano Banana genera l'immagine finale della miniatura dalla variante scelta. La chiave è salvata localmente ed è separata da quella Gemini usata per l'LLM testuale."),
987
990
  ("LLM — providers e modelos", "LLM — providers e modelos", "LLM — providers and models", "LLM — 服务商和模型", "LLM — Anbieter und Modelle", "LLM — provider và mô hình", "LLM — sağlayıcılar ve modeller", "LLM — провайдеры и модели", "LLM — proveedores y modelos", "LLM — penyedia dan model", "LLM — provider e modelli"),
988
991
  ("OpenAI/ NVIDIA NIM — API key, Base URL e modelo", "OpenAI/ NVIDIA NIM — API key, Base URL e modelo", "OpenAI/NVIDIA NIM — API key, Base URL and model", "OpenAI/NVIDIA NIM — API 密钥、基础 URL 和模型", "OpenAI/NVIDIA NIM — API-Schlüssel, Basis-URL und Modell", "OpenAI/NVIDIA NIM — API key, Base URL và mô hình", "OpenAI/NVIDIA NIM — API anahtarı, Temel URL ve model", "OpenAI/NVIDIA NIM — API-ключ, базовый URL и модель", "OpenAI/NVIDIA NIM — clave API, URL base y modelo", "OpenAI/NVIDIA NIM — kunci API, URL Dasar, dan model", "OpenAI/NVIDIA NIM — API key, URL di base e modello"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.3.69",
3
+ "version": "0.3.70",
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",