@danhachuel/thunderbolt 0.6.49 → 0.6.50
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.
|
@@ -216,8 +216,8 @@ def _render_instagram_posts(profile: Mapping[str, Any]) -> None:
|
|
|
216
216
|
if load_clicked or refresh_clicked:
|
|
217
217
|
target = max(10, len(posts)) if refresh_clicked else 10
|
|
218
218
|
posts_ok, posts_message, loaded_posts = _load_instagram_posts(profile, limit=target)
|
|
219
|
-
#
|
|
220
|
-
|
|
219
|
+
# DEBUG WINDOWS INSTAGRAM: manter até a validação final do carregamento público.
|
|
220
|
+
st.write({"instagram_posts_ok": posts_ok, "instagram_posts_message": posts_message, "instagram_posts_count": len(loaded_posts)})
|
|
221
221
|
if posts_ok:
|
|
222
222
|
posts = loaded_posts
|
|
223
223
|
_persist_instagram_posts(profile, posts)
|
|
@@ -132,18 +132,8 @@ class IntegrationResult:
|
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
def normalize_instagram_bio(value: Any) -> str:
|
|
135
|
-
"""Keep the
|
|
135
|
+
"""Keep the complete public biography exactly as returned by Instagram."""
|
|
136
136
|
bio = str(value or '').strip()
|
|
137
|
-
if not bio:
|
|
138
|
-
return ''
|
|
139
|
-
metric_words = r'followers|following|seguidores|seguindo|posts|publicações|publications'
|
|
140
|
-
metric_hits = re.findall(
|
|
141
|
-
rf'(?:\d[\d.,]*\s*(?:{metric_words})\b|\b(?:{metric_words})\s*\d[\d.,]*)',
|
|
142
|
-
bio,
|
|
143
|
-
flags=re.IGNORECASE,
|
|
144
|
-
)
|
|
145
|
-
if len(metric_hits) >= 2:
|
|
146
|
-
return ''
|
|
147
137
|
return bio
|
|
148
138
|
|
|
149
139
|
|
|
@@ -249,6 +239,7 @@ def _extract_profile_user(payload: Any) -> Mapping[str, Any] | None:
|
|
|
249
239
|
|
|
250
240
|
|
|
251
241
|
def _fetch_profile_with_playwright(username: str, headers: Mapping[str, str], cookies: Mapping[str, str]) -> dict[str, Any] | None:
|
|
242
|
+
# CORREÇÃO WINDOWS: este helper é o único caminho de rede usado pelo perfil no Windows.
|
|
252
243
|
endpoints = (
|
|
253
244
|
f'https://www.instagram.com/api/v1/users/web_profile_info/?username={username}',
|
|
254
245
|
f'https://i.instagram.com/api/v1/users/web_profile_info/?username={username}',
|
|
@@ -429,7 +420,9 @@ def _country_from_bio_fallback(bio: str) -> str:
|
|
|
429
420
|
|
|
430
421
|
def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]) -> dict[str, Any]:
|
|
431
422
|
followers = _profile_metric(user, 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
432
|
-
|
|
423
|
+
following_node = user.get('edge_follow')
|
|
424
|
+
following = normalize_instagram_metric(following_node) if isinstance(following_node, Mapping) else None
|
|
425
|
+
following = following if following is not None else _profile_metric(user, 'follows', 'following', 'following_count', 'followingCount')
|
|
433
426
|
followers = followers if followers is not None else _structured_metric_from_json(json.dumps(user), 'edge_followed_by', 'followers', 'follower_count', 'followerCount')
|
|
434
427
|
following = following if following is not None else _structured_metric_from_json(json.dumps(user), 'edge_follow', 'follows', 'following', 'following_count', 'followingCount')
|
|
435
428
|
media = ((user.get('edge_owner_to_timeline_media') or {}).get('count') if isinstance(user.get('edge_owner_to_timeline_media'), dict) else None)
|
|
@@ -437,7 +430,7 @@ def _profile_data_from_api(user: Mapping[str, Any], reference: Mapping[str, str]
|
|
|
437
430
|
biography = str(user.get('biography') or user.get('bio') or user.get('description') or '').strip()
|
|
438
431
|
country = extract_public_instagram_country(user) or _fetch_instagram_about_country(user.get('id') or user.get('pk'))
|
|
439
432
|
country = country or _country_from_bio_fallback(biography)
|
|
440
|
-
LOGGER.debug('Instagram profile extracted bio=%s followers=%s following=%s posts=%s country=%s', bool(biography), followers, following, media, country or '—')
|
|
433
|
+
LOGGER.debug('Instagram profile extracted keys=%s edge_follow=%r bio=%s followers=%s following=%s posts=%s country=%s', sorted(str(key) for key in user.keys()), following_node, bool(biography), followers, following, media, country or '—')
|
|
441
434
|
return {
|
|
442
435
|
'id': f"instagram_{reference['username']}",
|
|
443
436
|
**reference,
|
|
@@ -617,6 +610,7 @@ def _post_from_node(node: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
617
610
|
|
|
618
611
|
|
|
619
612
|
def _fetch_posts_with_playwright(reference: Mapping[str, str], limit: int, cookies: Mapping[str, str]) -> list[dict[str, Any]]:
|
|
613
|
+
# CORREÇÃO WINDOWS: posts são extraídos exclusivamente do JSON/HTML obtido pelo Playwright.
|
|
620
614
|
posts: list[dict[str, Any]] = []
|
|
621
615
|
seen: set[str] = set()
|
|
622
616
|
try:
|
|
@@ -635,6 +629,7 @@ def _fetch_posts_with_playwright(reference: Mapping[str, str], limit: int, cooki
|
|
|
635
629
|
if response is None or response.status >= 400:
|
|
636
630
|
return []
|
|
637
631
|
browser_document = page.content()
|
|
632
|
+
LOGGER.debug('Instagram Playwright posts document_bytes=%s', len(browser_document))
|
|
638
633
|
for document in _embedded_json_documents(browser_document):
|
|
639
634
|
for node in _walk_json(document):
|
|
640
635
|
post = _post_from_node(node)
|
|
@@ -642,6 +637,7 @@ def _fetch_posts_with_playwright(reference: Mapping[str, str], limit: int, cooki
|
|
|
642
637
|
continue
|
|
643
638
|
seen.add(post['id'])
|
|
644
639
|
posts.append(post)
|
|
640
|
+
LOGGER.debug('Instagram Playwright post extracted id=%s', post['id'])
|
|
645
641
|
if len(posts) >= max(1, int(limit)):
|
|
646
642
|
return posts
|
|
647
643
|
finally:
|
package/package.json
CHANGED