@danhachuel/thunderbolt 0.3.20 → 0.3.21

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
@@ -3437,103 +3437,116 @@ def render_google_accounts():
3437
3437
  missing_document_parts = list(direct_status.get("missing_cookies", []))
3438
3438
  if not direct_status.get("has_session_info"):
3439
3439
  missing_document_parts.append("sessionInfo")
3440
+ account_ready = bool(
3441
+ account_email_snapshot != "sem e-mail"
3442
+ and str(batch_account.get("client_id") or "").strip()
3443
+ and str(batch_account.get("client_secret") or "").strip()
3444
+ and bool(direct_status.get("document_exists"))
3445
+ and not missing_document_parts
3446
+ )
3440
3447
  if missing_document_parts:
3441
3448
  youtube_accounts_missing_document.append(account_email_snapshot)
3442
3449
 
3443
- with st.expander(f"{account_label_snapshot} — {account_email_snapshot}", expanded=False):
3444
- with st.form(f"batch_account_form_{account_id}"):
3445
- account_cols = st.columns(2)
3446
- with account_cols[0]:
3447
- account_label = st.text_input("Nome da conta", value=account_label_snapshot, key=f"batch_label_{account_id}")
3448
- account_email = st.text_input("E-mail/Gmail da conta", value=account_email_snapshot if account_email_snapshot != "sem e-mail" else "", key=f"batch_email_{account_id}")
3449
- account_client_id = st.text_input("OAuth Client ID", value=str(batch_account.get("client_id", "")), key=f"batch_client_id_{account_id}")
3450
- with account_cols[1]:
3451
- account_client_secret = st.text_input("OAuth Client Secret", value=str(batch_account.get("client_secret", "")), type="password", key=f"batch_client_secret_{account_id}")
3452
- account_session_info = st.text_input(
3453
- "sessionInfo token desta conta Google",
3454
- value=str(batch_account.get("sessionInfo") or batch_account.get("session_info") or batch_account.get("direct_session_info", "")),
3455
- type="password",
3456
- key=f"batch_session_info_{account_id}",
3457
- help="Token sessionInfo usado pelo Upload directo. É guardado por conta e sincronizado no credentials.json; os cookies e restantes valores continuam exclusivamente no documento.",
3458
- )
3459
- save_account = st.form_submit_button("Guardar dados da conta Google", type="primary", use_container_width=True)
3460
-
3461
- st.markdown("**Documento de cookies/credenciais desta conta Google**")
3462
- st.caption("O documento padrão é criado automaticamente. Suba um JSON completo ou apenas o documento de cookies; os valores preenchidos são incorporados e mantidos em credentials.json.")
3463
- document_upload = st.file_uploader(
3464
- "Subir documento de cookies/credenciais",
3465
- type=["json"],
3466
- key=f"direct_credentials_document_{account_id}",
3467
- help="Aceita o JSON do YouTube-Video-Upload-Frontend-Api. Um documento parcial de cookies também é incorporado sem apagar os restantes campos.",
3468
- )
3469
- if missing_document_parts:
3470
- st.warning(f"Documento incompleto: {', '.join(missing_document_parts)}")
3471
- else:
3472
- st.success("Documento completo para a conta Google")
3473
- st.caption(f"Documento guardado em: {direct_status['document_file']}")
3474
- document_save = st.button("Guardar documento nesta conta", key=f"save_direct_account_{account_id}", use_container_width=True)
3475
-
3476
- account_status = youtube_batch_account_status(batch_account, STORAGE)
3477
- status_cols = st.columns(3)
3478
- with status_cols[0]:
3479
- (st.success if account_status.ok else st.warning)(account_status.message)
3480
- with status_cols[1]:
3481
- if st.button("Autorizar/Reautorizar", key=f"batch_authorize_settings_{account_id}", use_container_width=True):
3482
- result = authorize_youtube_batch_account(batch_account, STORAGE)
3483
- (st.success if result.ok else st.error)(result.message)
3484
- if result.ok:
3485
- st.rerun()
3486
- with status_cols[2]:
3487
- if st.button("Apagar conta", icon=":material/delete:", key=f"batch_remove_settings_{account_id}", use_container_width=True):
3488
- delete_youtube_batch_token(batch_account, STORAGE)
3489
- delete_credentials_document(STORAGE, batch_account)
3490
- remaining_accounts = [account for account in batch_accounts if str(account.get("id")) != account_id]
3491
- settings["youtube_batch_accounts"] = remaining_accounts
3492
- if settings.get("youtube_batch_selected_account_id") == account_id:
3493
- settings["youtube_batch_selected_account_id"] = str(remaining_accounts[0].get("id")) if remaining_accounts else ""
3494
- for channel in channel_state:
3495
- if str(channel.get("google_account_id") or "") == account_id:
3496
- channel.update({"google_account_id": "", "google_account_email": ""})
3497
- write_json("channels.json", channel_state)
3498
- write_json("settings.json", settings)
3499
- st.rerun()
3500
-
3501
- if save_account:
3502
- if "@" not in account_email.strip():
3503
- st.error("Informe um e-mail Google válido.")
3504
- elif not account_client_id.strip() or not account_client_secret.strip():
3505
- st.error("Informe o Client ID e o Client Secret desta conta.")
3450
+ with st.container(border=True):
3451
+ account_header_cols = st.columns([3.2, 1.2])
3452
+ with account_header_cols[0]:
3453
+ st.subheader(f"{account_label_snapshot} — {account_email_snapshot}")
3454
+ with account_header_cols[1]:
3455
+ _api_status_badge("Configured" if account_ready else "Missing configuration", "ready" if account_ready else "missing")
3456
+ with st.expander("Detalhes da conta Google", expanded=False):
3457
+ with st.form(f"batch_account_form_{account_id}"):
3458
+ account_cols = st.columns(2)
3459
+ with account_cols[0]:
3460
+ account_label = st.text_input("Nome da conta", value=account_label_snapshot, key=f"batch_label_{account_id}")
3461
+ account_email = st.text_input("E-mail/Gmail da conta", value=account_email_snapshot if account_email_snapshot != "sem e-mail" else "", key=f"batch_email_{account_id}")
3462
+ account_client_id = st.text_input("OAuth Client ID", value=str(batch_account.get("client_id", "")), key=f"batch_client_id_{account_id}")
3463
+ with account_cols[1]:
3464
+ account_client_secret = st.text_input("OAuth Client Secret", value=str(batch_account.get("client_secret", "")), type="password", key=f"batch_client_secret_{account_id}")
3465
+ account_session_info = st.text_input(
3466
+ "sessionInfo token desta conta Google",
3467
+ value=str(batch_account.get("sessionInfo") or batch_account.get("session_info") or batch_account.get("direct_session_info", "")),
3468
+ type="password",
3469
+ key=f"batch_session_info_{account_id}",
3470
+ help="Token sessionInfo usado pelo Upload directo. É guardado por conta e sincronizado no credentials.json; os cookies e restantes valores continuam exclusivamente no documento.",
3471
+ )
3472
+ save_account = st.form_submit_button("Guardar dados da conta Google", type="primary", use_container_width=True)
3473
+
3474
+ st.markdown("**Documento de cookies/credenciais desta conta Google**")
3475
+ st.caption("O documento padrão é criado automaticamente. Suba um JSON completo ou apenas o documento de cookies; os valores preenchidos são incorporados e mantidos em credentials.json.")
3476
+ document_upload = st.file_uploader(
3477
+ "Subir documento de cookies/credenciais",
3478
+ type=["json"],
3479
+ key=f"direct_credentials_document_{account_id}",
3480
+ help="Aceita o JSON do YouTube-Video-Upload-Frontend-Api. Um documento parcial de cookies também é incorporado sem apagar os restantes campos.",
3481
+ )
3482
+ if missing_document_parts:
3483
+ st.warning(f"Documento incompleto: {', '.join(missing_document_parts)}")
3506
3484
  else:
3507
- for existing in batch_accounts:
3508
- if str(existing.get("id")) == account_id:
3509
- 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())))
3510
- if credentials_changed:
3511
- delete_youtube_batch_token(existing, STORAGE)
3512
- 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()})
3513
- update_credentials_document_session_info(STORAGE, existing, account_session_info.strip())
3514
- ensure_credentials_document(STORAGE, existing, settings, channel_state)
3515
- settings["youtube_batch_accounts"] = batch_accounts
3516
- write_json("settings.json", settings)
3517
- st.success("Conta Google/YouTube guardada.")
3518
- st.rerun()
3485
+ st.success("Documento completo para a conta Google")
3486
+ st.caption(f"Documento guardado em: {direct_status['document_file']}")
3487
+ document_save = st.button("Guardar documento nesta conta", key=f"save_direct_account_{account_id}", use_container_width=True)
3488
+
3489
+ account_status = youtube_batch_account_status(batch_account, STORAGE)
3490
+ status_cols = st.columns(3)
3491
+ with status_cols[0]:
3492
+ (st.success if account_status.ok else st.warning)(account_status.message)
3493
+ with status_cols[1]:
3494
+ if st.button("Autorizar/Reautorizar", key=f"batch_authorize_settings_{account_id}", use_container_width=True):
3495
+ result = authorize_youtube_batch_account(batch_account, STORAGE)
3496
+ (st.success if result.ok else st.error)(result.message)
3497
+ if result.ok:
3498
+ st.rerun()
3499
+ with status_cols[2]:
3500
+ if st.button("Apagar conta", icon=":material/delete:", key=f"batch_remove_settings_{account_id}", use_container_width=True):
3501
+ delete_youtube_batch_token(batch_account, STORAGE)
3502
+ delete_credentials_document(STORAGE, batch_account)
3503
+ remaining_accounts = [account for account in batch_accounts if str(account.get("id")) != account_id]
3504
+ settings["youtube_batch_accounts"] = remaining_accounts
3505
+ if settings.get("youtube_batch_selected_account_id") == account_id:
3506
+ settings["youtube_batch_selected_account_id"] = str(remaining_accounts[0].get("id")) if remaining_accounts else ""
3507
+ for channel in channel_state:
3508
+ if str(channel.get("google_account_id") or "") == account_id:
3509
+ channel.update({"google_account_id": "", "google_account_email": ""})
3510
+ write_json("channels.json", channel_state)
3511
+ write_json("settings.json", settings)
3512
+ st.rerun()
3519
3513
 
3520
- if document_save:
3521
- if document_upload is None:
3522
- st.error("Seleccione um documento JSON de cookies/credenciais antes de guardar.")
3523
- else:
3524
- try:
3525
- merge_credentials_document(
3526
- STORAGE,
3527
- batch_account,
3528
- document_upload.getvalue(),
3529
- document_upload.name,
3530
- session_info_override=str(batch_account.get("sessionInfo") or ""),
3531
- channels=channel_state,
3532
- )
3533
- st.success("Documento incorporado e guardado nesta conta Google.")
3514
+ if save_account:
3515
+ if "@" not in account_email.strip():
3516
+ st.error("Informe um e-mail Google válido.")
3517
+ elif not account_client_id.strip() or not account_client_secret.strip():
3518
+ st.error("Informe o Client ID e o Client Secret desta conta.")
3519
+ else:
3520
+ for existing in batch_accounts:
3521
+ if str(existing.get("id")) == account_id:
3522
+ 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())))
3523
+ if credentials_changed:
3524
+ delete_youtube_batch_token(existing, STORAGE)
3525
+ 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()})
3526
+ update_credentials_document_session_info(STORAGE, existing, account_session_info.strip())
3527
+ ensure_credentials_document(STORAGE, existing, settings, channel_state)
3528
+ settings["youtube_batch_accounts"] = batch_accounts
3529
+ write_json("settings.json", settings)
3530
+ st.success("Conta Google/YouTube guardada.")
3534
3531
  st.rerun()
3535
- except ValueError as exc:
3536
- st.error(str(exc))
3532
+
3533
+ if document_save:
3534
+ if document_upload is None:
3535
+ st.error("Seleccione um documento JSON de cookies/credenciais antes de guardar.")
3536
+ else:
3537
+ try:
3538
+ merge_credentials_document(
3539
+ STORAGE,
3540
+ batch_account,
3541
+ document_upload.getvalue(),
3542
+ document_upload.name,
3543
+ session_info_override=str(batch_account.get("sessionInfo") or ""),
3544
+ channels=channel_state,
3545
+ )
3546
+ st.success("Documento incorporado e guardado nesta conta Google.")
3547
+ st.rerun()
3548
+ except ValueError as exc:
3549
+ st.error(str(exc))
3537
3550
 
3538
3551
  if youtube_accounts_missing_document:
3539
3552
  st.info("Contas que ainda precisam de dados no documento: " + ", ".join(youtube_accounts_missing_document))
@@ -3559,6 +3572,11 @@ def render_google_accounts():
3559
3572
  legacy_account.pop("INNERTUBE_API_KEY", None)
3560
3573
  settings["youtube_batch_accounts"] = batch_accounts
3561
3574
  write_json("settings.json", settings)
3575
+ innertube_status_cols = st.columns([3.2, 1.2])
3576
+ with innertube_status_cols[0]:
3577
+ st.caption("Estado da chave global")
3578
+ with innertube_status_cols[1]:
3579
+ _render_credential_status(current_innertube_api_key)
3562
3580
  with st.form("innertube_api_key_form"):
3563
3581
  innertube_api_key_value = st.text_input(
3564
3582
  "INNERTUBE_API_KEY",
@@ -781,6 +781,7 @@ _CONTENT_TRANSLATION_ROWS = (
781
781
  ("O Thunderbolt é o cliente. A API key é enviada exclusivamente ao servidor Postiz configurado; não é colocada em URLs, logs ou repositório.", "O Thunderbolt é o cliente. A API key é enviada exclusivamente ao servidor Postiz configurado; não é colocada em URLs, logs ou repositório.", "Thunderbolt is the client. The API key is sent only to the configured Postiz server; it is not placed in URLs, logs or the repository.", "Thunderbolt 是客户端。API 密钥仅发送到配置的 Postiz 服务器,不会放入 URL、日志或代码库。", "Thunderbolt ist der Client. Der API-Schlüssel wird nur an den konfigurierten Postiz-Server gesendet und nicht in URLs, Logs oder dem Repository abgelegt.", "Thunderbolt là client. API key chỉ gửi đến máy chủ Postiz đã cấu hình; không đặt trong URL, log hoặc repository.", "Thunderbolt istemcidir. API anahtarı yalnızca yapılandırılmış Postiz sunucusuna gönderilir; URL'lere, günlüklerine veya depoya yazılmaz.", "Thunderbolt — клиент. API-ключ отправляется только настроенному серверу Postiz и не помещается в URL, логи или репозиторий.", "Thunderbolt es el cliente. La clave API solo se envía al servidor Postiz configurado; no se incluye en URL, registros ni repositorio.", "Thunderbolt adalah klien. Kunci API hanya dikirim ke server Postiz yang dikonfigurasi; tidak dimasukkan ke URL, log, atau repositori.", "Thunderbolt è il client. La chiave API viene inviata solo al server Postiz configurato; non viene inserita in URL, log o repository."),
782
782
  ("Modo de ligação", "Modo de ligação", "Connection mode", "连接模式", "Verbindungsmodus", "Chế độ kết nối", "Bağlantı modu", "Режим подключения", "Modo de conexión", "Mode koneksi", "Modalità di connessione"),
783
783
  ("Metadados removidos e nova cópia criada. O original continua preservado.", "Metadados removidos e nova cópia criada. O original continua preservado.", "Metadata removed and a new copy created. The original remains preserved.", "元数据已移除并创建新副本。原文件仍然保留。", "Metadaten entfernt und eine neue Kopie erstellt. Das Original bleibt erhalten.", "Đã xóa siêu dữ liệu và tạo bản sao mới. Bản gốc vẫn được giữ nguyên.", "Üst veriler kaldırıldı ve yeni bir kopya oluşturuldu. Orijinal korunur.", "Метаданные удалены и создана новая копия. Оригинал сохранён.", "Metadatos eliminados y nueva copia creada. El original se conserva.", "Metadata dihapus dan salinan baru dibuat. File asli tetap dipertahankan.", "Metadati rimossi e nuova copia creata. L'originale è preservato."),
784
+ ("Detalhes da conta Google", "Detalhes da conta Google", "Google account details", "Google 账户详情", "Details des Google-Kontos", "Chi tiết tài khoản Google", "Google hesap ayrıntıları", "Сведения об аккаунте Google", "Detalles de la cuenta de Google", "Detail akun Google", "Dettagli dell'account Google"),
784
785
  )
785
786
  UI_CONTENT_TRANSLATIONS: dict[str, dict[str, str]] = {code: {} for code in _CONTENT_TRANSLATION_CODES}
786
787
  for _row in _CONTENT_TRANSLATION_ROWS:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danhachuel/thunderbolt",
3
- "version": "0.3.20",
3
+ "version": "0.3.21",
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",