@danhachuel/thunderbolt 0.3.9 → 0.3.11
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/MANUAL-INSTALACAO.md +6 -6
- package/README.md +6 -6
- package/app/main.py +52 -25
- package/integrations/youtube_direct_credentials.py +22 -2
- package/package.json +1 -1
package/MANUAL-INSTALACAO.md
CHANGED
|
@@ -357,7 +357,7 @@ Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
|
|
|
357
357
|
|
|
358
358
|
## 8. Configuração inicial da UI
|
|
359
359
|
|
|
360
|
-
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
|
|
360
|
+
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` por conta, a `INNERTUBE_API_KEY` global, 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.
|
|
361
361
|
|
|
362
362
|
| Configuração | Finalidade |
|
|
363
363
|
|---|---|
|
|
@@ -411,7 +411,7 @@ Após iniciar a aplicação, valide o seguinte percurso:
|
|
|
411
411
|
14. **Canais Youtube:** abra **Configurações > Canais Youtube**, clique em **Editar** num cartão, altere o nicho, Blueprint/Prompts do Canal, Narrador/Voz padrão e horário, guarde e confirme que o cartão é actualizado sem duplicar o canal. Clique em **Actualizar últimos 10 vídeos**, valide a vista Lista, mude para Kanban e abra **Editar vídeo** para testar título, estado, data, URL e notas.
|
|
412
412
|
15. **Automação Youtube:** no cartão de cada canal, escolha o **Blueprint padrão** e a **Voz padrão**, clique em `Guardar` e confirme que o resumo do cartão se actualiza. Configure também **Automação ON** e um horário `HH:MM`; confirme a lista de vídeos cadastrados. Os mesmos defaults aparecem em **Canais Youtube** e são usados em novas tarefas. Inicie o Thunderbolt pelo launcher, confirme o aviso verde **Worker activo** e verifique que o relógio apresentado corresponde ao computador. O worker cria no máximo um lote por canal por dia quando o horário local coincide.
|
|
413
413
|
16. **Configurações > Configuração API > Teste de vozes:** teste Edge/Azure ou provider configurado e confirme reprodução/download sem criação de tarefa.
|
|
414
|
-
17. **Upload directo:** em **Configurações > Contas Google**, cada cartão Gmail apresenta o uploader **Documento de credenciais desta conta Google** e o campo **sessionInfo token desta conta Google**. O sessionInfo é guardado por conta e sincronizado no documento JSON, que reúne `SID`, `SSID`, `HSID`, `APISID`, `
|
|
414
|
+
17. **Upload directo:** em **Configurações > Contas Google**, cada cartão Gmail apresenta o uploader **Documento de credenciais desta conta Google** e o campo **sessionInfo token desta conta Google**. O sessionInfo é guardado por conta e sincronizado no documento JSON, que reúne `SID`, `SSID`, `HSID`, `APISID`, `chunk_size` e `delegated_session_ids` por canal; a `INNERTUBE_API_KEY` é guardada separadamente como configuração global para todas as contas e para todo o sistema. Use **Repetir campos para nova conta** para preencher o formulário seguinte e o ícone **Apagar conta** para remover a conta e os dados privados associados. Em **Canais Youtube > Canais cadastrados**, associe cada canal à conta Google do documento; a UI não mostra nem edita o `DELEGATED_SESSION_ID`. O documento é guardado em `storage/youtube_direct_accounts/<id-da-conta>/credentials.json`, e o uploader lê os valores directos exclusivamente desse documento. A `INNERTUBE_API_KEY` não é um campo técnico por conta nem pertence ao documento; existe apenas no bloco global da página. Não existem campos separados de cookies, `chunk_size` ou `DELEGATED_SESSION_ID` na parte inferior da UI e o método não extrai cookies automaticamente do navegador.
|
|
415
415
|
18. **Configurações > MCP > Client MCP:** confirme que Short Video Maker, AutoVio, OpenMontage e OpenCut aparecem com as portas padrão editáveis. O estado **Activo** é uma preferência local; a detecção deve indicar **Não detectado** quando os serviços externos não estiverem instalados ou iniciados.
|
|
416
416
|
19. **Configurações > MCP > Servidor MCP:** abra a subaba, mantenha o host `127.0.0.1`, active **Servidor MCP ON** e clique em **Guardar e iniciar Servidor MCP**. Confirme o endpoint `/mcp` e o health endpoint `/health`. Mantenha **Permitir ferramentas de escrita** desactivado até precisar que um agente crie lotes.
|
|
417
417
|
20. **Configurações > MCP > Skill:** clique em **Guardar skill localmente** e confirme o ficheiro em `storage/skills/moneyprinterturbo-video.md`; opcionalmente use **Descarregar skill .md** para obter a cópia através do navegador.
|
|
@@ -422,7 +422,7 @@ A área **Configurações > Configuração API > API Keys** separa as credenciai
|
|
|
422
422
|
|
|
423
423
|
Clique em **Guardar fonte e chaves** para persistir a fonte activa e as respectivas chaves no storage local. O Thunderbolt remove linhas vazias e duplicadas, mantém compatibilidade com instalações antigas que usavam campos separados e escreve arrays no `config.toml` do MoneyPrinterTurbo para permitir rotação interna. Ao seleccionar **Ficheiros locais**, nenhuma API key é necessária e os materiais são obtidos do storage local.
|
|
424
424
|
|
|
425
|
-
A subaba de fontes não é um painel de tuning: endpoints, proxy, qualidade, correspondência ao roteiro, directório, FFmpeg, codec, Whisper e outros parâmetros técnicos não são expostos ali. A subaba **Serviços e modelos** continua disponível para credenciais de LLM, TTS, Nano Banana, TikTok, Postiz e integrações que não são fontes de materiais. `INNERTUBE_API_KEY` não pertence a esta página; permanece no
|
|
425
|
+
A subaba de fontes não é um painel de tuning: endpoints, proxy, qualidade, correspondência ao roteiro, directório, FFmpeg, codec, Whisper e outros parâmetros técnicos não são expostos ali. A subaba **Serviços e modelos** continua disponível para credenciais de LLM, TTS, Nano Banana, TikTok, Postiz e integrações que não são fontes de materiais. `INNERTUBE_API_KEY` não pertence a esta página; permanece no bloco de configuração global em **Configurações > Contas Google** e aplica-se a todas as contas.
|
|
426
426
|
|
|
427
427
|
## Gestão de Canais, edição e vídeos recentes
|
|
428
428
|
|
|
@@ -496,7 +496,7 @@ A subaba **Postiz**, dentro de **Upload**, permite carregar as integrações Pos
|
|
|
496
496
|
|
|
497
497
|
O botão de envio YouTube segue a ordem fixa **1. API Oficial, 2. Upload directo, 3. Postiz**. O **Upload-Post** é uma quarta subaba independente e não altera essa rota automática do YouTube. A API Oficial regista no storage local até cinco envios bem-sucedidos por dia por conta Gmail. Quando a quota é atingida ou o método falha, o Thunderbolt valida o `credentials.json` e tenta o Upload directo. Postiz só é tentado como último recurso e apenas quando está activo, com API key e integração válida.
|
|
498
498
|
|
|
499
|
-
Em **Configurações > Contas Google**, cada conta aparece como um expander identificado por nome e e-mail. Dentro dele existe o uploader **Subir documento de cookies/credenciais** e o input **sessionInfo token desta conta Google**. O documento JSON único contém cookies,
|
|
499
|
+
Em **Configurações > Contas Google**, cada conta aparece como um expander identificado por nome e e-mail. Dentro dele existe o uploader **Subir documento de cookies/credenciais** e o input **sessionInfo token desta conta Google**. O documento JSON único contém cookies, `chunk_size` e o mapa de IDs delegados por canal; o sessionInfo preenchido na conta é sincronizado no mesmo ficheiro, guardado em `storage/youtube_direct_accounts/<id-da-conta>/credentials.json`. A `INNERTUBE_API_KEY` fica no bloco global da página, fora dos cartões, e é partilhada por todas as contas.
|
|
500
500
|
|
|
501
501
|
Em **Canais Youtube > Canais cadastrados**, a secção **Upload directo — documento da conta deste canal** mostra apenas a conta Google associada e confirma se o documento contém o ID delegado desse canal. O `DELEGATED_SESSION_ID` permanece exclusivamente no mapa do documento; o upload bloqueia a operação se faltar qualquer elemento técnico.
|
|
502
502
|
|
|
@@ -508,9 +508,9 @@ A credencial não é duplicada na página de Upload. A integração envia o fich
|
|
|
508
508
|
|
|
509
509
|
### Upload directo
|
|
510
510
|
|
|
511
|
-
A subaba **Upload directo** adapta o [YouTube-Video-Upload-Frontend-Api](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api). Cada conta Google tem um único `credentials.json`, criado automaticamente ao adicionar a conta, com cookies, sessionInfo,
|
|
511
|
+
A subaba **Upload directo** adapta o [YouTube-Video-Upload-Frontend-Api](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api). Cada conta Google tem um único `credentials.json`, criado automaticamente ao adicionar a conta, com cookies, sessionInfo, `chunk_size` e `delegated_session_ids` por canal. A `INNERTUBE_API_KEY` é uma configuração global separada, usada por todas as contas. O uploader aceita um documento JSON completo ou parcial; o merge actualiza somente os valores presentes e preserva os restantes. O documento é guardado fora de `storage/state/`, com permissões locais restritas.
|
|
512
512
|
|
|
513
|
-
Associe cada canal à conta Google correcta em **Canais Youtube**; não introduza o ID delegado na UI. A associação do canal é permitida mesmo que o documento esteja incompleto. No momento do upload, o Thunderbolt lê o documento da conta, encontra o `DELEGATED_SESSION_ID` pela chave do canal e bloqueia apenas a operação se faltar conta, documento válido, cookies, sessionInfo,
|
|
513
|
+
Associe cada canal à conta Google correcta em **Canais Youtube**; não introduza o ID delegado na UI. A associação do canal é permitida mesmo que o documento esteja incompleto. No momento do upload, o Thunderbolt lê a `INNERTUBE_API_KEY` global nas configurações, lê o documento da conta, encontra o `DELEGATED_SESSION_ID` pela chave do canal e bloqueia apenas a operação se faltar conta, documento válido, cookies, sessionInfo, chave global, ID delegado ou vídeo elegível. O tamanho de chunk é normalizado para múltiplos de 262144 bytes. O método é uma integração não oficial de sessão do YouTube; não extraia cookies automaticamente, não os coloque no repositório e não os partilhe.
|
|
514
514
|
|
|
515
515
|
## MCP e integrações externas
|
|
516
516
|
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ A primeira versão implementa a camada UI independente com:
|
|
|
22
22
|
| Edição | Menu expansível abaixo de Automação com Limpador de Metadados, Clip Generator local em Cortes e Editor Python inspirado no PYEdit para vídeos e scripts locais |
|
|
23
23
|
| Upload | YouTube via `youtube-automation-agent` adaptado internamente, fallback ordenado API Oficial → Upload directo → Postiz, upload de MP4 para Postiz via API key/MCP configurável, TikTok, Instagram e Facebook Pages no front end |
|
|
24
24
|
| MCP | Catálogo local opcional de Short Video Maker, AutoVio, OpenMontage e OpenCut, com portas editáveis e activação |
|
|
25
|
-
| Configurações | **Contas Google**, **Configuração API** e **Notificações**; API Keys divididas entre **Serviços e modelos** e **Fontes de materiais**, com várias chaves independentes por fonte, além de contas Google/YouTube por cartão, `INNERTUBE_API_KEY`
|
|
25
|
+
| Configurações | **Contas Google**, **Configuração API** e **Notificações**; API Keys divididas entre **Serviços e modelos** e **Fontes de materiais**, com várias chaves independentes por fonte, além de contas Google/YouTube por cartão, `INNERTUBE_API_KEY` global para todas as contas, providers, TTS, Nano Banana, Postiz, TikTok e Upload-Post |
|
|
26
26
|
| Launcher | Execução via `npx`, instalação assistida, diagnóstico e preparação para distribuição |
|
|
27
27
|
|
|
28
28
|
## Upload-Post — publicação para múltiplas plataformas
|
|
@@ -35,11 +35,11 @@ O Upload-Post é independente do Postiz: Postiz continua a usar o seu fluxo pró
|
|
|
35
35
|
|
|
36
36
|
## Upload directo — credenciais por conta e por canal
|
|
37
37
|
|
|
38
|
-
O Upload directo baseado no [YouTube-Video-Upload-Frontend-Api](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api) usa um único documento JSON por conta Google. Em **Configurações > Contas Google > Contas Google/YouTube — canais em lote**, cada conta aparece como um cartão expansível identificado por **nome — e-mail**. Dentro do cartão existe o uploader **Subir documento de cookies/credenciais** e o único campo técnico permitido na UI, **sessionInfo token desta conta Google**. Cookies, `
|
|
38
|
+
O Upload directo baseado no [YouTube-Video-Upload-Frontend-Api](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api) usa um único documento JSON por conta Google. Em **Configurações > Contas Google > Contas Google/YouTube — canais em lote**, cada conta aparece como um cartão expansível identificado por **nome — e-mail**. Dentro do cartão existe o uploader **Subir documento de cookies/credenciais** e o único campo técnico permitido na UI, **sessionInfo token desta conta Google**. Cookies, `chunk_size` e `delegated_session_ids` continuam no documento de cada conta; `INNERTUBE_API_KEY` é configurada uma única vez como chave global para todas as contas e para todo o sistema.
|
|
39
39
|
|
|
40
|
-
O botão **Adicionar outra conta Gmail** fica abaixo de uma divisória, fora dos cartões existentes. Ao criar uma conta, o Thunderbolt cria imediatamente `credentials.json` com placeholders vazios. Um upload completo ou parcial é incorporado por merge: os cookies enviados actualizam apenas os cookies fornecidos e valores como `sessionInfo`, `
|
|
40
|
+
O botão **Adicionar outra conta Gmail** fica abaixo de uma divisória, fora dos cartões existentes. Ao criar uma conta, o Thunderbolt cria imediatamente `credentials.json` com placeholders vazios. Um upload completo ou parcial é incorporado por merge: os cookies enviados actualizam apenas os cookies fornecidos e valores como `sessionInfo`, `chunk_size` e `delegated_session_ids` existentes não são apagados. O alerta de documento incompleto permanece visível por conta quando necessário; a `INNERTUBE_API_KEY` global é validada separadamente.
|
|
41
41
|
|
|
42
|
-
O documento é guardado por Gmail em `storage/youtube_direct_accounts/<id-da-conta>/credentials.json`, com permissões locais restritas. Em **Canais > Canais cadastrados**, a UI apenas associa o canal à conta Google; essa associação continua permitida mesmo quando o documento está incompleto. O uploader procura o ID do canal dentro do mapa `delegated_session_ids` do documento
|
|
42
|
+
O documento é guardado por Gmail em `storage/youtube_direct_accounts/<id-da-conta>/credentials.json`, com permissões locais restritas. Em **Canais > Canais cadastrados**, a UI apenas associa o canal à conta Google; essa associação continua permitida mesmo quando o documento está incompleto. O uploader resolve a `INNERTUBE_API_KEY` global nas configurações e procura o ID do canal dentro do mapa `delegated_session_ids` do documento, usando-o como `pageId`/`onBehalfOfUser`; só a operação de Upload directo é bloqueada quando faltarem os dados técnicos. Não existem campos de `INNERTUBE_API_KEY` por conta, nem campos separados de cookies, `chunk_size` ou `DELEGATED_SESSION_ID` na UI.
|
|
43
43
|
|
|
44
44
|
Os dados são segredos de sessão. Os valores não aparecem em tabelas ou logs, o ficheiro é escrito com permissões locais restritas e não é incluído no Git. O Thunderbolt não extrai cookies automaticamente do browser e não envia as credenciais para o GitHub. O método é não oficial e pode deixar de funcionar se o YouTube alterar os endpoints internos.
|
|
45
45
|
|
|
@@ -66,11 +66,11 @@ O conteúdo interno das páginas também é localizado, não apenas a navegaçã
|
|
|
66
66
|
|
|
67
67
|
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.
|
|
68
68
|
|
|
69
|
-
Dentro de **Configurações > Contas Google**, a UI contém os cartões expansíveis de contas Google/YouTube, `sessionInfo
|
|
69
|
+
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, Nano Banana, TikTok e Postiz.
|
|
70
70
|
|
|
71
71
|
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.
|
|
72
72
|
|
|
73
|
-
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**,
|
|
73
|
+
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.
|
|
74
74
|
|
|
75
75
|
A página **AI Influencers > Tutorial Meta** apresenta o guia de configuração de uma conta Instagram profissional e das credenciais Meta para automações com n8n, distribuído localmente em `seed/references/guide-instagram.md` e com ligação para a [fonte original no GitHub](https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-instagram.md). A página **Configurações > Notificações** mantém um histórico persistente de conclusões e falhas, reconcilia estados escritos por componentes locais e disponibiliza um checkbox independente para cada operação mapeada.
|
|
76
76
|
|
package/app/main.py
CHANGED
|
@@ -159,6 +159,7 @@ st.markdown("""
|
|
|
159
159
|
*/
|
|
160
160
|
:root { --tb-accent:#35a7ff; --tb-gold:#c59b55; }
|
|
161
161
|
[data-testid="stAppViewContainer"] { background:transparent; color:inherit; }
|
|
162
|
+
[data-testid="stSidebar"] { background:rgba(128,128,128,.04); border-right:1px solid rgba(128,128,128,.20); }
|
|
162
163
|
[data-testid="stSidebar"] { background:color-mix(in srgb, currentColor 4%, transparent); border-right:1px solid color-mix(in srgb, currentColor 16%, transparent); }
|
|
163
164
|
[data-testid="stSidebar"] .block-container { padding-top:0.28rem; padding-bottom:0.45rem; }
|
|
164
165
|
[data-testid="stSidebar"] > div:first-child { padding-top:0.28rem; }
|
|
@@ -171,31 +172,41 @@ st.markdown("""
|
|
|
171
172
|
[data-testid="stSidebar"] [data-testid="stButton"] button [data-testid="stMarkdownContainer"] { flex:1 1 auto !important; width:100% !important; text-align:left !important; }
|
|
172
173
|
[data-testid="stSidebar"] [data-testid="stButton"] p { margin:0; line-height:1; width:100%; text-align:left !important; }
|
|
173
174
|
[data-testid="stSidebar"] [data-testid="stBaseButton-secondary"] { background:transparent; color:inherit; }
|
|
175
|
+
[data-testid="stSidebar"] [data-testid="stBaseButton-secondary"]:hover { background:rgba(128,128,128,.08); border-color:rgba(128,128,128,.25); color:inherit; }
|
|
174
176
|
[data-testid="stSidebar"] [data-testid="stBaseButton-secondary"]:hover { background:color-mix(in srgb, currentColor 8%, transparent); border-color:color-mix(in srgb, currentColor 18%, transparent); color:inherit; }
|
|
177
|
+
[data-testid="stSidebar"] [data-testid="stBaseButton-primary"] { background:rgba(128,128,128,.14); color:inherit; border-color:rgba(128,128,128,.30); }
|
|
175
178
|
[data-testid="stSidebar"] [data-testid="stBaseButton-primary"] { background:color-mix(in srgb, currentColor 12%, transparent); color:inherit; border-color:color-mix(in srgb, currentColor 22%, transparent); }
|
|
179
|
+
[data-testid="stSidebar"] [data-testid="stBaseButton-primary"]:hover { background:rgba(128,128,128,.20); color:inherit; }
|
|
176
180
|
[data-testid="stSidebar"] [data-testid="stBaseButton-primary"]:hover { background:color-mix(in srgb, currentColor 18%, transparent); color:inherit; }
|
|
177
181
|
[data-testid="stSidebar"] [data-testid="stBaseButton-primary"] span,
|
|
178
182
|
[data-testid="stSidebar"] [data-testid="stBaseButton-secondary"] span { color:inherit; }
|
|
179
183
|
[data-testid="stSidebar"] [data-testid="stExpander"] { border:0 !important; background:transparent !important; margin:0.02rem 0 !important; }
|
|
180
184
|
[data-testid="stSidebar"] [data-testid="stExpander"] details { border:0 !important; background:transparent !important; }
|
|
181
185
|
[data-testid="stSidebar"] [data-testid="stExpander"] summary { min-height:1.72rem; padding:0.10rem 0.52rem !important; border-radius:7px; color:inherit; font-size:0.86rem; font-weight:650; }
|
|
186
|
+
[data-testid="stSidebar"] [data-testid="stExpander"] summary:hover { background:rgba(128,128,128,.08); }
|
|
182
187
|
[data-testid="stSidebar"] [data-testid="stExpander"] summary:hover { background:color-mix(in srgb, currentColor 8%, transparent); }
|
|
183
188
|
[data-testid="stSidebar"] [data-testid="stExpander"] summary p { margin:0; line-height:1; }
|
|
184
189
|
[data-testid="stSidebar"] [data-testid="stExpander"] > div { padding:0 0 0 0.42rem !important; }
|
|
185
190
|
[data-testid="stSidebar"] [data-testid="stExpander"] > div [data-testid="stButton"] button { padding-left:0.92rem; font-size:0.83rem; min-height:1.62rem; height:1.62rem; }
|
|
186
|
-
.content-card { box-sizing:border-box; padding:1rem 1.1rem; border:1px solid
|
|
191
|
+
.content-card { box-sizing:border-box; padding:1rem 1.1rem; border:1px solid rgba(128,128,128,.28); border-radius:14px; background:rgba(128,128,128,.10); color:inherit; min-height:110px; box-shadow:0 4px 14px rgba(0,0,0,.12); }
|
|
192
|
+
.content-card { border:1px solid color-mix(in srgb, currentColor 18%, transparent); background:color-mix(in srgb, currentColor 5%, transparent); box-shadow:0 4px 14px color-mix(in srgb, currentColor 12%, transparent); }
|
|
187
193
|
.content-label { color:inherit; opacity:.72; font-size:.8rem; text-transform:uppercase; letter-spacing:.07em; }
|
|
188
194
|
.content-value { color:inherit; font-size:1.8rem; font-weight:700; margin-top:.3rem; }
|
|
189
195
|
.content-card .small-muted { color:inherit; opacity:.72; }
|
|
190
|
-
.stage { border-left:3px solid var(--tb-accent); padding:.65rem .8rem; margin:.4rem 0; background:
|
|
196
|
+
.stage { border-left:3px solid var(--tb-accent); padding:.65rem .8rem; margin:.4rem 0; background:rgba(128,128,128,.06); border-radius:8px; }
|
|
197
|
+
.stage { background:color-mix(in srgb, currentColor 4%, transparent); }
|
|
191
198
|
.small-muted { color:inherit; opacity:.65; font-size:.85rem; }
|
|
192
|
-
.tb-cuts-hero { max-width:860px; margin:0 auto 1.1rem; padding:1.6rem 1.4rem 1.35rem; text-align:center; border:1px solid
|
|
199
|
+
.tb-cuts-hero { max-width:860px; margin:0 auto 1.1rem; padding:1.6rem 1.4rem 1.35rem; text-align:center; border:1px solid rgba(128,128,128,.28); border-radius:18px; background:rgba(128,128,128,.06); box-shadow:0 18px 48px rgba(0,0,0,.12); }
|
|
200
|
+
.tb-cuts-hero { border:1px solid color-mix(in srgb, currentColor 18%, transparent); background:radial-gradient(circle at 50% 0, color-mix(in srgb, var(--tb-gold) 16%, transparent), transparent 58%), color-mix(in srgb, currentColor 4%, transparent); box-shadow:0 18px 48px color-mix(in srgb, currentColor 12%, transparent); }
|
|
193
201
|
.tb-cuts-hero .tb-cuts-kicker { color:var(--tb-gold); font-size:.68rem; letter-spacing:.18em; text-transform:uppercase; font-weight:700; }
|
|
194
202
|
.tb-cuts-hero h2 { color:inherit; font-family:Georgia,serif; font-size:2rem; font-weight:500; margin:.42rem 0 .25rem; text-transform:lowercase; }
|
|
195
203
|
.tb-cuts-hero p { color:inherit; opacity:.68; margin:0 auto; max-width:620px; font-size:.9rem; }
|
|
196
204
|
[data-testid="stRadio"] [role="radiogroup"] { gap:.6rem; }
|
|
197
|
-
[data-testid="stRadio"] label { border:1px solid
|
|
198
|
-
[data-testid="stRadio"] label
|
|
205
|
+
[data-testid="stRadio"] label { border:1px solid rgba(128,128,128,.28); border-radius:12px; padding:.65rem .8rem; background:rgba(128,128,128,.06); min-height:4.3rem; }
|
|
206
|
+
[data-testid="stRadio"] label { border:1px solid color-mix(in srgb, currentColor 18%, transparent); background:color-mix(in srgb, currentColor 4%, transparent); }
|
|
207
|
+
[data-testid="stRadio"] label:has(input:checked) { border-color:var(--tb-gold); background:rgba(197,155,85,.24); }
|
|
208
|
+
[data-testid="stRadio"] label:has(input:checked) { background:linear-gradient(145deg, color-mix(in srgb, var(--tb-gold) 24%, transparent), color-mix(in srgb, currentColor 6%, transparent)); }
|
|
209
|
+
[data-testid="stStatusWidget"] { border-color:rgba(128,128,128,.28) !important; background:rgba(128,128,128,.06) !important; }
|
|
199
210
|
[data-testid="stStatusWidget"] { border-color:color-mix(in srgb, currentColor 18%, transparent) !important; background:color-mix(in srgb, currentColor 4%, transparent) !important; }
|
|
200
211
|
/* O menu de idiomas usa layout nativo da aplicação; o toolbar do Streamlit não é alterado. */
|
|
201
212
|
|
|
@@ -3518,28 +3529,44 @@ def render_google_accounts():
|
|
|
3518
3529
|
|
|
3519
3530
|
st.divider()
|
|
3520
3531
|
st.markdown("### INNERTUBE_API_KEY")
|
|
3521
|
-
st.caption("Esta chave
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
if
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
settings.pop("
|
|
3532
|
+
st.caption("Esta é uma chave API global do YouTube: aplica-se a todas as contas Google/YouTube e a todo o sistema. Não é associada a uma conta específica, não faz parte dos documentos de cookies/credenciais e não é editada no separador API Keys.")
|
|
3533
|
+
configured_global_innertube_api_key = str(settings.get("direct_innertube_api_key") or settings.get("INNERTUBE_API_KEY") or "").strip()
|
|
3534
|
+
current_innertube_api_key = configured_global_innertube_api_key
|
|
3535
|
+
if not current_innertube_api_key:
|
|
3536
|
+
# Migrate values created by older releases, which incorrectly stored
|
|
3537
|
+
# the key inside a selected Google account. The first legacy value is
|
|
3538
|
+
# retained as the single global value and all account copies are removed.
|
|
3539
|
+
for legacy_account in batch_accounts:
|
|
3540
|
+
current_innertube_api_key = str(legacy_account.get("innertube_api_key") or legacy_account.get("INNERTUBE_API_KEY") or "").strip()
|
|
3541
|
+
if current_innertube_api_key:
|
|
3542
|
+
break
|
|
3543
|
+
if current_innertube_api_key:
|
|
3544
|
+
settings["direct_innertube_api_key"] = current_innertube_api_key
|
|
3545
|
+
settings.pop("INNERTUBE_API_KEY", None)
|
|
3546
|
+
for legacy_account in batch_accounts:
|
|
3547
|
+
legacy_account.pop("innertube_api_key", None)
|
|
3548
|
+
legacy_account.pop("INNERTUBE_API_KEY", None)
|
|
3535
3549
|
settings["youtube_batch_accounts"] = batch_accounts
|
|
3536
3550
|
write_json("settings.json", settings)
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3551
|
+
with st.form("innertube_api_key_form"):
|
|
3552
|
+
innertube_api_key_value = st.text_input(
|
|
3553
|
+
"INNERTUBE_API_KEY",
|
|
3554
|
+
value=current_innertube_api_key,
|
|
3555
|
+
type="password",
|
|
3556
|
+
key="global_innertube_api_key",
|
|
3557
|
+
help="Chave global usada pelo Upload directo para todas as contas Google/YouTube. Guarde-a na configuração global, separada dos documentos de cookies.",
|
|
3558
|
+
)
|
|
3559
|
+
save_innertube_api_key = st.form_submit_button("Guardar INNERTUBE_API_KEY global", type="primary", use_container_width=True)
|
|
3560
|
+
if save_innertube_api_key:
|
|
3561
|
+
settings["direct_innertube_api_key"] = innertube_api_key_value.strip()
|
|
3562
|
+
settings.pop("INNERTUBE_API_KEY", None)
|
|
3563
|
+
for legacy_account in batch_accounts:
|
|
3564
|
+
legacy_account.pop("innertube_api_key", None)
|
|
3565
|
+
legacy_account.pop("INNERTUBE_API_KEY", None)
|
|
3566
|
+
settings["youtube_batch_accounts"] = batch_accounts
|
|
3567
|
+
write_json("settings.json", settings)
|
|
3568
|
+
st.success("INNERTUBE_API_KEY global guardada para todas as contas Google/YouTube e para todo o sistema.")
|
|
3569
|
+
st.rerun()
|
|
3543
3570
|
|
|
3544
3571
|
st.divider()
|
|
3545
3572
|
st.markdown("### Adicionar outra conta Gmail")
|
|
@@ -128,14 +128,34 @@ def _account_session_info(account: dict[str, Any]) -> str:
|
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
def account_innertube_api_key(account: dict[str, Any] | None, document: dict[str, Any] | None = None, settings: dict[str, Any] | None = None) -> str:
|
|
131
|
-
"""Return the
|
|
131
|
+
"""Return the global YouTube API key for every account.
|
|
132
|
+
|
|
133
|
+
The account and document values are retained only as read-only migration
|
|
134
|
+
fallbacks for installations created by older releases. A configured
|
|
135
|
+
``direct_innertube_api_key`` always wins and is shared by the whole system.
|
|
136
|
+
"""
|
|
132
137
|
account = account or {}
|
|
133
138
|
document = document or {}
|
|
134
139
|
settings = settings or {}
|
|
140
|
+
global_key = str(settings.get("direct_innertube_api_key") or settings.get("INNERTUBE_API_KEY") or "").strip()
|
|
141
|
+
if global_key:
|
|
142
|
+
return global_key
|
|
143
|
+
|
|
144
|
+
# Before the global field existed, older releases could store different
|
|
145
|
+
# copies inside accounts. Use the first non-empty legacy value for every
|
|
146
|
+
# account until the settings page can migrate and remove those copies.
|
|
147
|
+
legacy_accounts = settings.get("youtube_batch_accounts", [])
|
|
148
|
+
if isinstance(legacy_accounts, list):
|
|
149
|
+
for legacy_account in legacy_accounts:
|
|
150
|
+
if not isinstance(legacy_account, dict):
|
|
151
|
+
continue
|
|
152
|
+
legacy_key = str(legacy_account.get("innertube_api_key") or legacy_account.get("INNERTUBE_API_KEY") or "").strip()
|
|
153
|
+
if legacy_key:
|
|
154
|
+
return legacy_key
|
|
155
|
+
|
|
135
156
|
return str(
|
|
136
157
|
account.get("innertube_api_key")
|
|
137
158
|
or account.get("INNERTUBE_API_KEY")
|
|
138
|
-
or settings.get("direct_innertube_api_key")
|
|
139
159
|
or document.get("INNERTUBE_API_KEY")
|
|
140
160
|
or ""
|
|
141
161
|
).strip()
|
package/package.json
CHANGED