@danhachuel/thunderbolt 0.6.44 → 0.6.45
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.
|
@@ -24,7 +24,6 @@ except ImportError: # pragma: no cover - Streamlit is a runtime dependency
|
|
|
24
24
|
|
|
25
25
|
LOGGER = logging.getLogger(__name__)
|
|
26
26
|
_WINDOWS_PLAYWRIGHT_CHECKED = False
|
|
27
|
-
_WINDOWS_COOKIE_WARNING_SHOWN = False
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
ABOUT_ACCOUNT_URL = 'https://i.instagram.com/api/v1/bloks/apps/com.instagram.interactions.about_this_account/'
|
|
@@ -70,19 +69,11 @@ def _streamlit_message(level: str, message: str, key: str) -> None:
|
|
|
70
69
|
|
|
71
70
|
def _ensure_windows_playwright() -> None:
|
|
72
71
|
"""Ensure Chromium exists on Windows before using the Playwright fallback."""
|
|
73
|
-
global _WINDOWS_PLAYWRIGHT_CHECKED
|
|
72
|
+
global _WINDOWS_PLAYWRIGHT_CHECKED
|
|
74
73
|
if os.name != 'nt' or _WINDOWS_PLAYWRIGHT_CHECKED:
|
|
75
74
|
return
|
|
76
75
|
_WINDOWS_PLAYWRIGHT_CHECKED = True
|
|
77
76
|
_load_dotenv_compat()
|
|
78
|
-
cookie_names = ('INSTAGRAM_SESSIONID', 'IG_SESSIONID')
|
|
79
|
-
if not any(os.getenv(name, '').strip() for name in cookie_names) and not _WINDOWS_COOKIE_WARNING_SHOWN:
|
|
80
|
-
_WINDOWS_COOKIE_WARNING_SHOWN = True
|
|
81
|
-
message = ('Windows: cookies Instagram não configurados. O país da conta pode ficar indisponível; '
|
|
82
|
-
'configure INSTAGRAM_SESSIONID (e, se necessário, INSTAGRAM_CSRFTOKEN, '
|
|
83
|
-
'INSTAGRAM_DS_USER_ID, INSTAGRAM_MID e INSTAGRAM_IG_DID) no ficheiro .env.')
|
|
84
|
-
LOGGER.warning(message)
|
|
85
|
-
_streamlit_message('warning', message, 'windows_cookie_warning')
|
|
86
77
|
try:
|
|
87
78
|
completed = subprocess.run(
|
|
88
79
|
[sys.executable, '-m', 'playwright', 'install', 'chromium'],
|
|
@@ -224,6 +215,7 @@ def _structured_metric_from_json(document: str, *keys: str) -> int | None:
|
|
|
224
215
|
|
|
225
216
|
def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
226
217
|
_ensure_windows_playwright()
|
|
218
|
+
cookies = _instagram_cookies()
|
|
227
219
|
headers = _instagram_headers()
|
|
228
220
|
headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0 Safari/537.36'
|
|
229
221
|
# The www host is the current public web endpoint. The legacy i host is
|
|
@@ -273,7 +265,7 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
|
273
265
|
for request_header in request_headers:
|
|
274
266
|
for endpoint in endpoints:
|
|
275
267
|
try:
|
|
276
|
-
response = requests.get(endpoint, headers=request_header, timeout=15)
|
|
268
|
+
response = requests.get(endpoint, headers=request_header, cookies=cookies or None, timeout=15)
|
|
277
269
|
LOGGER.debug('Instagram profile URL=%s status=%s content_type=%s', endpoint, response.status_code, response.headers.get('content-type', ''))
|
|
278
270
|
if response.status_code >= 400:
|
|
279
271
|
continue
|
|
@@ -330,6 +322,8 @@ def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
|
|
330
322
|
user_agent=headers['User-Agent'],
|
|
331
323
|
extra_http_headers={'x-ig-app-id': headers['x-ig-app-id'], 'Accept-Language': 'en-US,en;q=0.9'},
|
|
332
324
|
)
|
|
325
|
+
if cookies:
|
|
326
|
+
page.context.add_cookies([{'name': name, 'value': value, 'domain': '.instagram.com', 'path': '/'} for name, value in cookies.items()])
|
|
333
327
|
for endpoint in endpoints:
|
|
334
328
|
try:
|
|
335
329
|
response = page.goto(endpoint, wait_until='domcontentloaded', timeout=30000)
|
|
@@ -632,7 +626,7 @@ def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationRes
|
|
|
632
626
|
if api_user.get('is_private'):
|
|
633
627
|
return IntegrationResult(False, 'Esta conta é privada. O Instagram não disponibiliza posts sem uma sessão autenticada.', reference | {'posts': [], 'is_private': True})
|
|
634
628
|
try:
|
|
635
|
-
response = requests.get(reference['url'], headers={'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'pt-BR,pt;q=0.9,en;q=0.8'}, timeout=15)
|
|
629
|
+
response = requests.get(reference['url'], headers={'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'pt-BR,pt;q=0.9,en;q=0.8'}, cookies=_instagram_cookies() or None, timeout=15)
|
|
636
630
|
except requests.RequestException as exc:
|
|
637
631
|
LOGGER.warning('Falha HTTP nos posts Instagram URL=%s: %s', reference['url'], exc, exc_info=True)
|
|
638
632
|
return IntegrationResult(False, f'Não foi possível consultar os posts públicos do Instagram: {exc}', reference)
|
|
@@ -662,6 +656,9 @@ def fetch_public_instagram_posts(source: str, limit: int = 10) -> IntegrationRes
|
|
|
662
656
|
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/131.0 Safari/537.36',
|
|
663
657
|
extra_http_headers={'x-ig-app-id': '936619743392459', 'Accept-Language': 'en-US,en;q=0.9'},
|
|
664
658
|
)
|
|
659
|
+
cookies = _instagram_cookies()
|
|
660
|
+
if cookies:
|
|
661
|
+
page.context.add_cookies([{'name': name, 'value': value, 'domain': '.instagram.com', 'path': '/'} for name, value in cookies.items()])
|
|
665
662
|
page.goto(reference['url'], wait_until='domcontentloaded', timeout=30000)
|
|
666
663
|
browser_document = page.content()
|
|
667
664
|
for document in _embedded_json_documents(browser_document):
|
package/package.json
CHANGED