@danhachuel/thunderbolt 0.5.86 → 0.5.87
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.
|
@@ -14,6 +14,8 @@ import os
|
|
|
14
14
|
import re
|
|
15
15
|
import shutil
|
|
16
16
|
import signal
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
17
19
|
import time
|
|
18
20
|
from contextlib import contextmanager
|
|
19
21
|
from dataclasses import dataclass
|
|
@@ -52,6 +54,70 @@ DESCRIPTION_SELECTORS = [
|
|
|
52
54
|
]
|
|
53
55
|
BLOCKED_TEXT = ("verify", "verificação", "verification", "captcha", "confirme que você é humano", "confirm it's you", "confirme que é você")
|
|
54
56
|
|
|
57
|
+
|
|
58
|
+
class _BrowserUnavailableError(RuntimeError):
|
|
59
|
+
"""Raised when Playwright cannot find or prepare a usable browser."""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _looks_like_missing_playwright_browser(error: BaseException) -> bool:
|
|
63
|
+
message = str(error).lower()
|
|
64
|
+
return "executable doesn't exist" in message or "executable does not exist" in message or "playwright was just installed" in message
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _install_playwright_chromium() -> bool:
|
|
68
|
+
"""Install the bundled Chromium once when Playwright reports it missing."""
|
|
69
|
+
try:
|
|
70
|
+
result = subprocess.run(
|
|
71
|
+
[os.fspath(sys.executable), "-m", "playwright", "install", "chromium"],
|
|
72
|
+
check=False,
|
|
73
|
+
capture_output=True,
|
|
74
|
+
text=True,
|
|
75
|
+
timeout=300,
|
|
76
|
+
)
|
|
77
|
+
except (OSError, subprocess.SubprocessError) as exc:
|
|
78
|
+
logger.warning("Não foi possível preparar o Chromium do Playwright: %s", _safe_error(exc))
|
|
79
|
+
return False
|
|
80
|
+
if result.returncode != 0:
|
|
81
|
+
detail = (result.stderr or result.stdout or "").strip().splitlines()
|
|
82
|
+
logger.warning("A instalação automática do Chromium falhou: %s", detail[-1] if detail else "erro desconhecido")
|
|
83
|
+
return False
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _launch_playwright_browser(playwright: Any) -> Any:
|
|
88
|
+
"""Launch the configured browser, recovering from a missing Playwright binary."""
|
|
89
|
+
configured_path = str(os.environ.get("THUNDERBOLT_CHROME_PATH") or "").strip()
|
|
90
|
+
launch_kwargs: dict[str, Any] = {"headless": True}
|
|
91
|
+
if configured_path:
|
|
92
|
+
if not Path(configured_path).is_file():
|
|
93
|
+
raise _BrowserUnavailableError(f"Executável Chrome/Chromium não encontrado: {configured_path}")
|
|
94
|
+
launch_kwargs["executable_path"] = configured_path
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
return playwright.chromium.launch(**launch_kwargs)
|
|
98
|
+
except Exception as initial_error:
|
|
99
|
+
if configured_path or not _looks_like_missing_playwright_browser(initial_error):
|
|
100
|
+
raise
|
|
101
|
+
|
|
102
|
+
# Prefer an already installed Google Chrome before downloading another
|
|
103
|
+
# browser. This is particularly useful on Windows machines where the
|
|
104
|
+
# Playwright cache was removed but Chrome itself is still available.
|
|
105
|
+
try:
|
|
106
|
+
return playwright.chromium.launch(headless=True, channel="chrome")
|
|
107
|
+
except Exception as chrome_error:
|
|
108
|
+
if not _looks_like_missing_playwright_browser(initial_error):
|
|
109
|
+
raise initial_error
|
|
110
|
+
if not _install_playwright_chromium():
|
|
111
|
+
raise _BrowserUnavailableError(
|
|
112
|
+
"O Chromium do Playwright não está instalado e não foi possível prepará-lo automaticamente."
|
|
113
|
+
) from chrome_error
|
|
114
|
+
try:
|
|
115
|
+
return playwright.chromium.launch(headless=True)
|
|
116
|
+
except Exception as retry_error:
|
|
117
|
+
raise _BrowserUnavailableError(
|
|
118
|
+
"O Chromium do Playwright continua indisponível depois da instalação automática."
|
|
119
|
+
) from retry_error
|
|
120
|
+
|
|
55
121
|
try:
|
|
56
122
|
from yt_cm import YouTubeCookieManager # type: ignore
|
|
57
123
|
YT_CM_AVAILABLE = True
|
|
@@ -203,7 +269,11 @@ def _capture_with_playwright(document: dict[str, Any], *, logs_dir: Path, video_
|
|
|
203
269
|
logs_dir.mkdir(parents=True, exist_ok=True)
|
|
204
270
|
cookies = [{"name": key, "value": str(document["cookies"][key]), "domain": ".youtube.com", "path": "/"} for key in COOKIE_KEYS if document.get("cookies", {}).get(key)]
|
|
205
271
|
with sync_playwright() as playwright:
|
|
206
|
-
|
|
272
|
+
try:
|
|
273
|
+
browser = _launch_playwright_browser(playwright)
|
|
274
|
+
except _BrowserUnavailableError as exc:
|
|
275
|
+
logger.error("Browser Playwright indisponível: %s", _safe_error(exc))
|
|
276
|
+
return "browser_unavailable", None
|
|
207
277
|
context = browser.new_context()
|
|
208
278
|
context.add_cookies(cookies)
|
|
209
279
|
page = context.new_page()
|
|
@@ -298,6 +368,14 @@ def renew_account_session(storage_root: Path, account: dict[str, Any], settings:
|
|
|
298
368
|
return RenewalResult(False, "cookie_renewal_failed", f"Falha na renovação de cookies: {_safe_error(exc)}", account_id)
|
|
299
369
|
for attempt in range(1, MAX_CAPTURE_ATTEMPTS + 1):
|
|
300
370
|
capture_status, token = _capture_with_playwright(document, logs_dir=logs_dir, video_id=str(account.get("video_id") or ""))
|
|
371
|
+
if capture_status == "browser_unavailable":
|
|
372
|
+
return RenewalResult(
|
|
373
|
+
False,
|
|
374
|
+
"browser_unavailable",
|
|
375
|
+
"Renovação falhou: o Chromium do Playwright não está disponível. Execute novamente a instalação do Thunderbolt e tente renovar outra vez.",
|
|
376
|
+
account_id,
|
|
377
|
+
attempt,
|
|
378
|
+
)
|
|
301
379
|
if capture_status == "blocked_by_google":
|
|
302
380
|
_write_renewal_state(directory, "blocked_by_google")
|
|
303
381
|
return RenewalResult(False, "blocked_by_google", "🔒 Sessão bloqueada pelo Google. Renovação manual necessária.", account_id, attempt)
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -135,7 +135,7 @@ function check() {
|
|
|
135
135
|
console.error(`Python não encontrado. Execute: npx.cmd --yes @danhachuel/thunderbolt install`);
|
|
136
136
|
process.exit(1);
|
|
137
137
|
}
|
|
138
|
-
const requiredModules = ["streamlit", "requests", "pandas", "toml", "imageio_ffmpeg", "edge_tts", "sklearn", "mlxtend", "plotly", "seaborn", "kagglehub", "composio"];
|
|
138
|
+
const requiredModules = ["streamlit", "requests", "pandas", "toml", "imageio_ffmpeg", "edge_tts", "sklearn", "mlxtend", "plotly", "seaborn", "kagglehub", "composio", "playwright"];
|
|
139
139
|
const missing = requiredModules.filter((moduleName) => !moduleAvailable(moduleName));
|
|
140
140
|
const ffmpeg = moduleAvailable("imageio_ffmpeg");
|
|
141
141
|
const mptPath = configuredMoneyPrinterPath();
|
package/scripts/install.mjs
CHANGED
|
@@ -452,6 +452,17 @@ function installRequirementIfNeeded(requirementsPath, stateKey, modules, label)
|
|
|
452
452
|
writeDependencyState({ [stateKey]: currentHash });
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
function installPlaywrightBrowsers() {
|
|
456
|
+
if (!existsSync(pythonBin)) return;
|
|
457
|
+
console.log("Playwright: a verificar o browser Chromium... ");
|
|
458
|
+
const result = spawnSync(pythonBin, ["-m", "playwright", "install", "chromium"], { stdio: "inherit", env: pythonEnvironment });
|
|
459
|
+
if (result.status !== 0) {
|
|
460
|
+
console.error("Não foi possível instalar o browser Chromium do Playwright.");
|
|
461
|
+
console.error("Execute novamente o instalador depois de confirmar o acesso à Internet.");
|
|
462
|
+
process.exit(result.status || 1);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
455
466
|
function writeSettings(moneyprinterPath) {
|
|
456
467
|
const stateDir = join(thunderboltHome, "storage", "state");
|
|
457
468
|
mkdirSync(stateDir, { recursive: true });
|
|
@@ -470,7 +481,8 @@ function writeSettings(moneyprinterPath) {
|
|
|
470
481
|
|
|
471
482
|
function installThunderboltDependencies(python) {
|
|
472
483
|
if (!existsSync(pythonBin)) run(python.command, [...python.args, "-m", "venv", venvPath]);
|
|
473
|
-
installRequirementIfNeeded(join(root, "requirements.txt"), "thunderbolt_requirements_sha256", ["streamlit", "requests", "pandas", "toml", "imageio_ffmpeg", "edge_tts", "google.auth", "google_auth_oauthlib", "googleapiclient", "yt_dlp", "deno", "youtube_transcript_api", "huggingface_hub"], "Thunderbolt");
|
|
484
|
+
installRequirementIfNeeded(join(root, "requirements.txt"), "thunderbolt_requirements_sha256", ["streamlit", "requests", "pandas", "toml", "imageio_ffmpeg", "edge_tts", "google.auth", "google_auth_oauthlib", "googleapiclient", "yt_dlp", "deno", "youtube_transcript_api", "huggingface_hub", "playwright"], "Thunderbolt");
|
|
485
|
+
installPlaywrightBrowsers();
|
|
474
486
|
}
|
|
475
487
|
|
|
476
488
|
function installMoneyPrinterDependencies(moneyprinterPath) {
|