@danhachuel/thunderbolt 0.3.89 → 0.3.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/main.py CHANGED
@@ -5066,6 +5066,22 @@ def render_google_accounts():
5066
5066
  key=f"batch_session_info_{account_id}",
5067
5067
  help="Token sessionInfo usado pelo Upload directo. É guardado por conta e sincronizado no credentials.json; os cookies e restantes valores continuam exclusivamente no documento.",
5068
5068
  )
5069
+ captured_at_value = str(
5070
+ session_health.captured_at
5071
+ or batch_account.get("sessionInfoCapturedAt")
5072
+ or batch_account.get("session_info_captured_at")
5073
+ or ""
5074
+ ).strip()
5075
+ try:
5076
+ account_session_info_captured_at = datetime.fromisoformat(captured_at_value.replace("Z", "+00:00")).date() if captured_at_value else None
5077
+ except ValueError:
5078
+ account_session_info_captured_at = None
5079
+ account_session_info_captured_at = st.date_input(
5080
+ "Data de Captura",
5081
+ value=account_session_info_captured_at,
5082
+ key=f"batch_session_info_captured_at_{account_id}",
5083
+ help="Data em que o sessionInfo token foi capturado. É usada para calcular o alerta de expiração e não revela o token.",
5084
+ )
5069
5085
  save_account = st.form_submit_button("Guardar dados da conta Google", type="primary", use_container_width=True)
5070
5086
 
5071
5087
  st.markdown("**Documento de cookies/credenciais desta conta Google**")
@@ -5119,8 +5135,14 @@ def render_google_accounts():
5119
5135
  credentials_changed = any(existing.get(field, "") != value for field, value in (("email", account_email.strip()), ("client_id", account_client_id.strip()), ("client_secret", account_client_secret.strip())))
5120
5136
  if credentials_changed:
5121
5137
  delete_youtube_batch_token(existing, STORAGE)
5122
- existing.update({"label": account_label.strip() or "Canais YouTube", "email": account_email.strip(), "client_id": account_client_id.strip(), "client_secret": account_client_secret.strip(), "sessionInfo": account_session_info.strip()})
5123
- update_credentials_document_session_info(STORAGE, existing, account_session_info.strip())
5138
+ captured_at_iso = account_session_info_captured_at.isoformat() if isinstance(account_session_info_captured_at, date) else ""
5139
+ existing.update({"label": account_label.strip() or "Canais YouTube", "email": account_email.strip(), "client_id": account_client_id.strip(), "client_secret": account_client_secret.strip(), "sessionInfo": account_session_info.strip(), "sessionInfoCapturedAt": captured_at_iso})
5140
+ update_credentials_document_session_info(
5141
+ STORAGE,
5142
+ existing,
5143
+ account_session_info.strip(),
5144
+ captured_at=captured_at_iso,
5145
+ )
5124
5146
  ensure_credentials_document(STORAGE, existing, settings, channel_state)
5125
5147
  settings["youtube_batch_accounts"] = batch_accounts
5126
5148
  write_json("settings.json", settings)
@@ -6403,6 +6425,22 @@ def render_supabase_tutorial():
6403
6425
  st.markdown(tutorial_content, unsafe_allow_html=True)
6404
6426
 
6405
6427
 
6428
+ def render_youtube_frontend_upload_tutorial():
6429
+ """Render the safe operational guide for the YouTube direct-upload workflow."""
6430
+ tutorial_path = ROOT / "seed" / "references" / "youtube-video-upload-frontend.md"
6431
+ st.title("Tutorial YouTube Video-Upload Frontend")
6432
+ st.caption("Guia prático e seguro para rever metadados, sessão e envio directo de vídeos concluídos.")
6433
+ st.markdown("[Abrir referência técnica no GitHub](https://github.com/Nojus10/YouTube-Video-Upload-Frontend-Api)")
6434
+ try:
6435
+ tutorial_content = tutorial_path.read_text(encoding="utf-8").strip()
6436
+ except OSError:
6437
+ tutorial_content = ""
6438
+ if not tutorial_content:
6439
+ st.error("O conteúdo local do tutorial não está disponível. Consulte a referência técnica no GitHub.")
6440
+ return
6441
+ st.markdown(tutorial_content, unsafe_allow_html=False)
6442
+
6443
+
6406
6444
  def render_mcp():
6407
6445
  st.title("MCP")
6408
6446
  st.caption("Clientes externos, servidor MCP do Thunderbolt e a skill local ficam separados para evitar confundir funções diferentes.")
@@ -6708,6 +6746,7 @@ def main():
6708
6746
  ("Tutorial Supabase", ":material/storage:", "Tutorial Supabase"),
6709
6747
  ("Tutorial Kaggle", ":material/menu_book:", "Tutorial Kaggle"),
6710
6748
  ("Tutorial Apify", ":material/menu_book:", "Tutorial Apify"),
6749
+ ("Tutorial YouTube Video-Upload Frontend", ":material/video_library:", "Tutorial YouTube Video-Upload Frontend"),
6711
6750
  ]
6712
6751
  settings_items = [
6713
6752
  ("MCP", ":material/hub:", "MCP"),
@@ -6846,6 +6885,7 @@ def main():
6846
6885
  "Documentação": lambda: render_edit_placeholder("Documentação", "Seleccione um tutorial no menu expansível."),
6847
6886
  "Tutorial Meta": render_models_ai_tutorial,
6848
6887
  "Tutorial Supabase": render_supabase_tutorial,
6888
+ "Tutorial YouTube Video-Upload Frontend": render_youtube_frontend_upload_tutorial,
6849
6889
  "Configurações": lambda: render_edit_placeholder("Configurações", "Seleccione uma opção no menu expansível."),
6850
6890
  "MCP": render_mcp,
6851
6891
  "Contas Google": render_google_accounts,
@@ -242,8 +242,14 @@ def save_credentials_document(storage_root: Path, account: dict[str, Any], docum
242
242
  return destination
243
243
 
244
244
 
245
- def update_credentials_document_session_info(storage_root: Path, account: dict[str, Any], session_info: str) -> Path | None:
246
- """Save sessionInfo and its UTC capture time without exposing the token."""
245
+ def update_credentials_document_session_info(
246
+ storage_root: Path,
247
+ account: dict[str, Any],
248
+ session_info: str,
249
+ *,
250
+ captured_at: str | None = None,
251
+ ) -> Path | None:
252
+ """Save sessionInfo and an optional user-confirmed capture date without exposing the token."""
247
253
  path = credentials_document_path(storage_root, account)
248
254
  if path.exists():
249
255
  raw = _read_json_document(path) or {}
@@ -251,7 +257,12 @@ def update_credentials_document_session_info(storage_root: Path, account: dict[s
251
257
  raw = load_credentials_document(storage_root, {**account, "sessionInfo": session_info}, create=True)
252
258
  document = _normalise_document(raw, {**account, "sessionInfo": session_info})
253
259
  document["sessionInfo"] = str(session_info or "").strip()
254
- document["sessionInfoCapturedAt"] = _session_info_now() if document["sessionInfo"] else ""
260
+ if not document["sessionInfo"]:
261
+ document["sessionInfoCapturedAt"] = ""
262
+ elif captured_at is None:
263
+ document["sessionInfoCapturedAt"] = _session_info_now()
264
+ else:
265
+ document["sessionInfoCapturedAt"] = str(captured_at).strip()
255
266
  return save_credentials_document(storage_root, account, document)
256
267
 
257
268
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.3.89",
3
+ "version": "0.3.91",
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,48 @@
1
+ # Tutorial YouTube Video-Upload Frontend
2
+
3
+ Este tutorial explica como utilizar, de forma responsável, o fluxo **Direct upload** do Thunderbolt para vídeos já concluídos. A rota é uma integração técnica baseada em comportamentos do frontend do YouTube e pode mudar sem aviso. Use-a somente em contas e canais que controla e respeite as políticas aplicáveis do YouTube.
4
+
5
+ > **Segurança primeiro:** dados de sessão, cookies de autenticação, identificadores de canal e material de credencial são confidenciais. Nunca os copie para conversas, capturas de ecrã, issues, ficheiros versionados ou campos de texto comuns. Configure-os exclusivamente nos campos protegidos da aplicação local.
6
+
7
+ ## 1. Antes de começar
8
+
9
+ Confirme que o vídeo foi concluído no **Video Backlog** e que o respectivo canal YouTube está seleccionado no Thunderbolt. No formulário de Upload, confirme título, descrição, tags, privacidade, categoria e idioma antes de autorizar qualquer envio.
10
+
11
+ O Thunderbolt oferece dois percursos distintos: o fluxo oficial, que utiliza a integração de API autorizada, e o fluxo directo, destinado a uma sessão válida e a uma conta controlada pelo utilizador. Estes percursos não devem ser usados para contornar políticas, limites da plataforma ou permissões de canal.
12
+
13
+ | Elemento | Finalidade | Boa prática |
14
+ | --- | --- | --- |
15
+ | Vídeo | Ficheiro final da tarefa concluída | Verifique duração, áudio, miniatura e legendas antes do envio. |
16
+ | Metadados | Título, descrição, tags, idioma e privacidade | Reveja manualmente antes de publicar. A descrição por IA pode ser editada. |
17
+ | Canal | Destino associado à conta autorizada | Confirme que é o canal correcto, sobretudo em contas com vários canais. |
18
+ | Sessão | Autorização temporária de upload directo | Mantenha-a apenas em armazenamento local protegido e actualize-a quando expirar. |
19
+
20
+ ## 2. Preparar a autenticação de forma segura
21
+
22
+ No Thunderbolt, abra **Upload > Direct upload** e utilize os controlos de autorização fornecidos pela interface. Complete o procedimento somente no seu navegador e conta YouTube. Não partilhe dados de sessão, cookies, tokens temporários, IDs de canal ou chaves de integração com terceiros.
23
+
24
+ Se a verificação de saúde indicar sessão expirada, interrompa o envio e renove a autorização pela interface. O sistema deve apresentar o diagnóstico sem mostrar os valores protegidos. Nunca use valores de exemplo ou chaves encontradas em documentos públicos.
25
+
26
+ ## 3. Envio em partes e memória
27
+
28
+ O envio directo pode transferir o ficheiro em partes, em vez de carregar todo o vídeo para a memória de uma só vez. Esta estratégia é adequada para ficheiros grandes e reduz a pressão de memória no computador. O Thunderbolt mantém o vídeo no armazenamento local e regista apenas o estado e o resultado do processo.
29
+
30
+ ## 4. Executar o envio
31
+
32
+ Abra **Upload > Conventional upload** para rever os metadados. Utilize **Gerar descrição com IA** apenas quando quiser uma sugestão editorial; o resultado permanece editável. O campo **Idioma** é detectado da tarefa ou canal e é mostrado numa lista suspensa para evitar códigos escritos incorrectamente.
33
+
34
+ Depois de confirmar o canal, a privacidade e os metadados, seleccione o fluxo de envio apropriado. Um envio publica ou cria conteúdo numa plataforma externa; por isso, o Thunderbolt deve exigir a confirmação explícita do utilizador antes da acção.
35
+
36
+ ## 5. Diagnóstico e manutenção
37
+
38
+ | Situação | Acção recomendada |
39
+ | --- | --- |
40
+ | Sessão expirada ou não autorizada | Renove a autorização na interface e volte a executar a verificação de saúde. |
41
+ | Canal incorrecto | Cancele antes do envio e seleccione o canal certo. |
42
+ | Falha de rede | Não repita automaticamente sem confirmar se o vídeo foi criado no YouTube Studio. |
43
+ | Metadados incompletos | Corrija título, descrição, tags, idioma ou privacidade no formulário antes de enviar. |
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.