@danhachuel/thunderbolt 0.5.92 → 0.5.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/main.py +16 -27
- package/integrations/youtube_growth_api.py +2 -2
- package/package.json +1 -1
- package/seed/references/guide-instagram.md +0 -2
- package/seed/references/guide-supabase.md +0 -2
- package/seed/references/tutorial-youtube-data-api-key.md +0 -5
- package/seed/references/youtube-video-upload-frontend.md +0 -4
package/app/main.py
CHANGED
|
@@ -124,6 +124,7 @@ from integrations.youtube_direct_credentials import delete_credentials_document,
|
|
|
124
124
|
from integrations.session_info_health import check_account_session_info_health
|
|
125
125
|
from integrations.youtube_session_manager import renew_account_session
|
|
126
126
|
from integrations.youtube_batch import account_key as youtube_batch_account_key, account_status as youtube_batch_account_status, authorize_account as authorize_youtube_batch_account, delete_account_token as delete_youtube_batch_token, list_my_channels as list_youtube_batch_channels, loopback_redirect_uri
|
|
127
|
+
from integrations.youtube_growth_api import find_account_for_channel as find_youtube_growth_account
|
|
127
128
|
from integrations.local_runtime import MoneyPrinterRuntime
|
|
128
129
|
from integrations.moneyprinter_config import sync_moneyprinter_config
|
|
129
130
|
from integrations.openai_model_discovery import DEFAULT_NVIDIA_NIM_BASE_URL
|
|
@@ -1496,21 +1497,6 @@ def render_growth_youtube():
|
|
|
1496
1497
|
st.caption("Auditoria pública baseada nos 3 pilares críticos e nas métricas secundárias do agente Growth.")
|
|
1497
1498
|
growth_settings = read_json("settings.json", {})
|
|
1498
1499
|
youtube_data_api_ready = bool(str(growth_settings.get("youtube_api_key") or os.getenv("YOUTUBE_API_KEY") or "").strip())
|
|
1499
|
-
growth_accounts = growth_settings.get("youtube_batch_accounts", [])
|
|
1500
|
-
analytics_accounts_ready = False
|
|
1501
|
-
if isinstance(growth_accounts, list):
|
|
1502
|
-
for growth_account in growth_accounts:
|
|
1503
|
-
if isinstance(growth_account, dict) and youtube_batch_account_status(growth_account, STORAGE).ok:
|
|
1504
|
-
analytics_accounts_ready = True
|
|
1505
|
-
break
|
|
1506
|
-
st.markdown("**Estado das APIs de Growth**")
|
|
1507
|
-
api_status_cols = st.columns(2, gap="small")
|
|
1508
|
-
with api_status_cols[0]:
|
|
1509
|
-
st.caption("YouTube Data API v3: Metadados públicos")
|
|
1510
|
-
_api_status_badge("Configured" if youtube_data_api_ready else "Missing configuration", "ready" if youtube_data_api_ready else "missing")
|
|
1511
|
-
with api_status_cols[1]:
|
|
1512
|
-
st.caption("YouTube Analytics API: Métricas internas")
|
|
1513
|
-
_api_status_badge("Configured" if analytics_accounts_ready else "Missing configuration", "ready" if analytics_accounts_ready else "missing")
|
|
1514
1500
|
channels = [item for item in read_json("channels.json", []) if isinstance(item, dict) and item.get("active", True) and is_youtube_channel_record(item)]
|
|
1515
1501
|
if not channels:
|
|
1516
1502
|
st.info("Cadastre pelo menos um canal YouTube em Canais YouTube para iniciar a análise.")
|
|
@@ -1526,6 +1512,16 @@ def render_growth_youtube():
|
|
|
1526
1512
|
with action_status_col:
|
|
1527
1513
|
if st.session_state.get("growth_analysis_status") == "complete":
|
|
1528
1514
|
st.markdown('<div style="padding-top:9px;color:#22c55e;font-weight:700;white-space:nowrap;">✓ Análise concluída</div>', unsafe_allow_html=True)
|
|
1515
|
+
matched_growth_account = find_youtube_growth_account(selected, growth_settings)
|
|
1516
|
+
analytics_accounts_ready = bool(matched_growth_account and youtube_batch_account_status(matched_growth_account, STORAGE).ok)
|
|
1517
|
+
st.markdown("**Estado das APIs de Growth**")
|
|
1518
|
+
api_status_cols = st.columns(2, gap="small")
|
|
1519
|
+
with api_status_cols[0]:
|
|
1520
|
+
st.caption("YouTube Data API v3: Metadados públicos")
|
|
1521
|
+
_api_status_badge("Configured" if youtube_data_api_ready else "Missing configuration", "ready" if youtube_data_api_ready else "missing")
|
|
1522
|
+
with api_status_cols[1]:
|
|
1523
|
+
st.caption("YouTube Analytics API: Métricas internas")
|
|
1524
|
+
_api_status_badge("Configured" if analytics_accounts_ready else "Missing configuration", "ready" if analytics_accounts_ready else "missing")
|
|
1529
1525
|
if analyse_clicked:
|
|
1530
1526
|
settings = read_json("settings.json", {})
|
|
1531
1527
|
llm_settings, _ = ensure_llm_provider_cards(settings)
|
|
@@ -8341,17 +8337,15 @@ def render_notifications():
|
|
|
8341
8337
|
|
|
8342
8338
|
|
|
8343
8339
|
def render_models_ai_tutorial():
|
|
8344
|
-
tutorial_url = "https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-instagram.md"
|
|
8345
8340
|
tutorial_path = ROOT / "seed" / "references" / "guide-instagram.md"
|
|
8346
8341
|
st.title("Tutorial Meta")
|
|
8347
8342
|
st.caption("Guia de configuração de uma conta Instagram profissional e das credenciais Meta para automações com n8n.")
|
|
8348
|
-
st.markdown(f"[Abrir fonte original no GitHub]({tutorial_url})")
|
|
8349
8343
|
try:
|
|
8350
8344
|
tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
|
|
8351
8345
|
except OSError:
|
|
8352
8346
|
tutorial_content = ""
|
|
8353
8347
|
if not tutorial_content:
|
|
8354
|
-
st.error("O conteúdo local do tutorial não está disponível.
|
|
8348
|
+
st.error("O conteúdo local do tutorial não está disponível.")
|
|
8355
8349
|
return
|
|
8356
8350
|
st.markdown(tutorial_content, unsafe_allow_html=True)
|
|
8357
8351
|
|
|
@@ -8363,18 +8357,16 @@ def render_niche_tutorial(tutorial_kind: str):
|
|
|
8363
8357
|
|
|
8364
8358
|
|
|
8365
8359
|
def render_supabase_tutorial():
|
|
8366
|
-
tutorial_url = "https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-supabase.md"
|
|
8367
8360
|
tutorial_path = ROOT / "seed" / "references" / "guide-supabase.md"
|
|
8368
8361
|
ui_language = current_ui_language()
|
|
8369
8362
|
st.title(ui_text("Tutorial Supabase", ui_language))
|
|
8370
8363
|
st.caption(ui_text("Guia de configuração do Supabase para automações com n8n.", ui_language))
|
|
8371
|
-
st.markdown(f"[Abrir fonte original no GitHub]({tutorial_url})")
|
|
8372
8364
|
try:
|
|
8373
8365
|
tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
|
|
8374
8366
|
except OSError:
|
|
8375
8367
|
tutorial_content = ""
|
|
8376
8368
|
if not tutorial_content:
|
|
8377
|
-
st.error("O conteúdo local do tutorial não está disponível.
|
|
8369
|
+
st.error("O conteúdo local do tutorial não está disponível.")
|
|
8378
8370
|
return
|
|
8379
8371
|
st.markdown(tutorial_content, unsafe_allow_html=True)
|
|
8380
8372
|
|
|
@@ -8384,13 +8376,12 @@ def render_google_oauth_tutorial():
|
|
|
8384
8376
|
tutorial_path = ROOT / "seed" / "references" / "tutorial-oauth-google.md"
|
|
8385
8377
|
st.title("Tutorial OAuth do Google")
|
|
8386
8378
|
st.caption("Guia completo para configurar a autenticação OAuth do Google e o acesso à YouTube Data API v3.")
|
|
8387
|
-
st.markdown("[Abrir Google Cloud Console](https://console.cloud.google.com/)")
|
|
8388
8379
|
try:
|
|
8389
8380
|
tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
|
|
8390
8381
|
except OSError:
|
|
8391
8382
|
tutorial_content = ""
|
|
8392
8383
|
if not tutorial_content:
|
|
8393
|
-
st.error("O conteúdo local do tutorial não está disponível.
|
|
8384
|
+
st.error("O conteúdo local do tutorial não está disponível.")
|
|
8394
8385
|
return
|
|
8395
8386
|
st.markdown(tutorial_content, unsafe_allow_html=False)
|
|
8396
8387
|
|
|
@@ -8400,13 +8391,12 @@ def render_youtube_frontend_upload_tutorial():
|
|
|
8400
8391
|
tutorial_path = ROOT / "seed" / "references" / "youtube-video-upload-frontend.md"
|
|
8401
8392
|
st.title("Tutorial YouTube Video-Upload Frontend")
|
|
8402
8393
|
st.caption("Guia prático e seguro para rever metadados, sessão e envio directo de vídeos concluídos.")
|
|
8403
|
-
st.markdown("[Abrir referência técnica no GitHub](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api)")
|
|
8404
8394
|
try:
|
|
8405
8395
|
tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
|
|
8406
8396
|
except OSError:
|
|
8407
8397
|
tutorial_content = ""
|
|
8408
8398
|
if not tutorial_content:
|
|
8409
|
-
st.error("O conteúdo local do tutorial não está disponível.
|
|
8399
|
+
st.error("O conteúdo local do tutorial não está disponível.")
|
|
8410
8400
|
return
|
|
8411
8401
|
st.markdown(tutorial_content, unsafe_allow_html=False)
|
|
8412
8402
|
|
|
@@ -8416,13 +8406,12 @@ def render_youtube_data_api_key_tutorial():
|
|
|
8416
8406
|
tutorial_path = ROOT / "seed" / "references" / "tutorial-youtube-data-api-key.md"
|
|
8417
8407
|
st.title("Tutorial YouTube Data API Key (Public Data)")
|
|
8418
8408
|
st.caption("Guia para criar e configurar uma YouTube Data API Key para consultas de dados públicos.")
|
|
8419
|
-
st.markdown("[Abrir documento fonte no Google Drive](https://drive.google.com/file/d/1ypzaR1v8khBxyUQmZ12ZAY0pGTD49mqt/view)")
|
|
8420
8409
|
try:
|
|
8421
8410
|
tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
|
|
8422
8411
|
except OSError:
|
|
8423
8412
|
tutorial_content = ""
|
|
8424
8413
|
if not tutorial_content:
|
|
8425
|
-
st.error("O conteúdo local do tutorial não está disponível.
|
|
8414
|
+
st.error("O conteúdo local do tutorial não está disponível.")
|
|
8426
8415
|
return
|
|
8427
8416
|
st.markdown(tutorial_content, unsafe_allow_html=False)
|
|
8428
8417
|
|
|
@@ -23,9 +23,9 @@ def find_account_for_channel(channel: Mapping[str, Any], settings: Mapping[str,
|
|
|
23
23
|
channel_id = _text(channel.get("youtube_channel_id") or channel.get("id"))
|
|
24
24
|
account_id = _text(channel.get("google_account_id"))
|
|
25
25
|
accounts = _accounts(settings)
|
|
26
|
+
if account_id:
|
|
27
|
+
return next((account for account in accounts if _text(account.get("id")) == account_id), None)
|
|
26
28
|
for account in accounts:
|
|
27
|
-
if account_id and _text(account.get("id")) == account_id:
|
|
28
|
-
return account
|
|
29
29
|
known_channels = account.get("channels", [])
|
|
30
30
|
if isinstance(known_channels, list) and any(_text(item.get("youtube_channel_id") or item.get("id")) == channel_id for item in known_channels if isinstance(item, Mapping)):
|
|
31
31
|
return account
|
package/package.json
CHANGED
|
@@ -171,5 +171,3 @@ You'll see this interface below.
|
|
|
171
171
|
2. The Instagram account id which you'll need to set in the `Configure` node in the n8n workflow
|
|
172
172
|
|
|
173
173
|
---
|
|
174
|
-
|
|
175
|
-
> **Source:** [ai_agents_az/episode_8/guide-instagram.md](https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-instagram.md)
|
|
@@ -65,5 +65,3 @@ No Thunderbolt, abra **Configurações > Configuração API > AI Influencers > B
|
|
|
65
65
|
O Thunderbolt usa o Supabase como backend seleccionado, não como executor de n8n: os assets são enviados ao Storage, os metadados ficam nas tabelas e os estados de geração ficam em `influencer_content`. Para uma execução totalmente local, seleccione **SQLite** no mesmo painel; a alternativa cria `storage/state/ai_influencers.db` e não utiliza a conta Supabase.
|
|
66
66
|
|
|
67
67
|
A conta Supabase está pronta para ser utilizada pela automação.
|
|
68
|
-
|
|
69
|
-
Fonte original: [guide-supabase.md no GitHub](https://github.com/gyoridavid/ai_agents_az/blob/main/episode_8/guide-supabase.md)
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
# Tutorial YouTube Data API Key (Public Data)
|
|
2
|
-
|
|
3
1
|
Este tutorial explica como criar uma **YouTube Data API Key** no Google Cloud e configurá-la no Thunderbolt para consultar dados públicos do YouTube, como vídeos, canais e estatísticas públicas.
|
|
4
2
|
|
|
5
|
-
> **Fonte:** [Tutorial YouTube Data API Key (Public Data) no Google Drive](https://drive.google.com/file/d/1ypzaR1v8khBxyUQmZ12ZAY0pGTD49mqt/view)
|
|
6
|
-
|
|
7
3
|
## Quando usar uma API Key
|
|
8
4
|
|
|
9
5
|
Uma API Key é suficiente para operações com dados públicos, como pesquisar vídeos, consultar canais públicos e obter estatísticas públicas. Ela **não concede acesso a dados privados** da conta nem substitui o OAuth 2.0 para playlists privadas, inscrições privadas ou uploads.
|
|
@@ -93,4 +89,3 @@ Uma API Key só identifica o projecto e permite chamadas autorizadas para dados
|
|
|
93
89
|
- [Google Cloud Console](https://console.cloud.google.com/)
|
|
94
90
|
- [YouTube Data API v3](https://developers.google.com/youtube/v3)
|
|
95
91
|
- [Documentação de quotas da YouTube Data API](https://developers.google.com/youtube/v3/determine_quota_cost)
|
|
96
|
-
- [Documento fonte no Google Drive](https://drive.google.com/file/d/1ypzaR1v8khBxyUQmZ12ZAY0pGTD49mqt/view)
|
|
@@ -42,7 +42,3 @@ Depois de confirmar o canal, a privacidade e os metadados, seleccione o fluxo de
|
|
|
42
42
|
| Falha de rede | Não repita automaticamente sem confirmar se o vídeo foi criado no YouTube Studio. |
|
|
43
43
|
| Metadados incompletos | Corrija título, descrição, tags, idioma ou privacidade no formulário antes de enviar. |
|
|
44
44
|
| Limite ou política da plataforma | Pare o processo e siga as políticas e limites actualmente aplicáveis do YouTube. |
|
|
45
|
-
|
|
46
|
-
## Referência técnica
|
|
47
|
-
|
|
48
|
-
O material fornecido pelo utilizador cita o repositório [Nojus10/YouTube-Video-Upload-Frontend-Api](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api). O Thunderbolt apresenta este tutorial como orientação de integração segura; não replica segredos, valores de autenticação nem instruções de extracção de credenciais.
|