From 327beae747ade83203fb5c87f98e6438a53bda89 Mon Sep 17 00:00:00 2001 From: salvacybersec Date: Thu, 13 Nov 2025 06:31:10 +0300 Subject: [PATCH] flaresolver fix --- src/transcript_extractor.py | 80 ++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/src/transcript_extractor.py b/src/transcript_extractor.py index 6759832..56641a8 100644 --- a/src/transcript_extractor.py +++ b/src/transcript_extractor.py @@ -169,9 +169,13 @@ class TranscriptExtractor: def patched_get(session_self, url, **kwargs): """requests.Session.get'i patch et - header'ları ekle ve FlareSolverr kullan""" - # FlareSolverr kullanılıyorsa ve YouTube URL'si ise - if extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url): - logger.debug(f"[FLARESOLVERR] YouTube isteği FlareSolverr üzerinden deneniyor: {url[:50]}...") + # FlareSolverr'ı sadece video sayfası için kullan (transcript API endpoint'leri için değil) + # Transcript API endpoint'leri genellikle /api/timedtext gibi path'ler içerir + is_video_page = ('youtube.com/watch' in url or 'youtu.be/' in url) and '/api/' not in url + + # FlareSolverr kullanılıyorsa ve video sayfası ise + if extractor_instance.use_flaresolverr and is_video_page: + logger.debug(f"[FLARESOLVERR] Video sayfası FlareSolverr üzerinden deneniyor: {url[:50]}...") flaresolverr_response = extractor_instance._make_flaresolverr_request(url, 'GET', **kwargs) if flaresolverr_response: logger.debug(f"[FLARESOLVERR] ✅ FlareSolverr başarılı, response döndürülüyor") @@ -201,6 +205,9 @@ class TranscriptExtractor: return PatchedResponse(flaresolverr_response) else: logger.debug(f"[FLARESOLVERR] FlareSolverr yanıt vermedi, normal istek deneniyor") + elif extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url): + # Transcript API endpoint'leri için FlareSolverr kullanma, sadece header'ları ekle + logger.debug(f"[FLARESOLVERR] Transcript API endpoint'i tespit edildi, FlareSolverr atlanıyor: {url[:50]}...") # Normal istek (header'ları ekle) headers = kwargs.get('headers', {}) @@ -211,37 +218,10 @@ class TranscriptExtractor: def patched_post(session_self, url, **kwargs): """requests.Session.post'i patch et - header'ları ekle ve FlareSolverr kullan""" - # FlareSolverr kullanılıyorsa ve YouTube URL'si ise + # POST istekleri için FlareSolverr kullanma (genellikle API endpoint'leri) + # Sadece header'ları ekle if extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url): - logger.debug(f"[FLARESOLVERR] YouTube POST isteği FlareSolverr üzerinden deneniyor: {url[:50]}...") - flaresolverr_response = extractor_instance._make_flaresolverr_request(url, 'POST', **kwargs) - if flaresolverr_response: - logger.debug(f"[FLARESOLVERR] ✅ FlareSolverr başarılı, response döndürülüyor") - class PatchedResponse: - def __init__(self, flaresolverr_response): - self.status_code = flaresolverr_response.status_code - self.text = flaresolverr_response.text - self.content = flaresolverr_response.content - self.headers = flaresolverr_response.headers - self.url = flaresolverr_response.url - self.ok = 200 <= self.status_code < 300 - - def json(self): - import json - try: - return json.loads(self.text) - except: - return {} - - def raise_for_status(self): - """requests.Response.raise_for_status() uyumluluğu""" - if not self.ok: - from requests.exceptions import HTTPError - raise HTTPError(f"{self.status_code} Client Error: {self.text[:100]}", response=self) - - return PatchedResponse(flaresolverr_response) - else: - logger.debug(f"[FLARESOLVERR] FlareSolverr yanıt vermedi, normal istek deneniyor") + logger.debug(f"[FLARESOLVERR] POST isteği tespit edildi, FlareSolverr atlanıyor (sadece header'lar): {url[:50]}...") # Normal istek (header'ları ekle) headers = kwargs.get('headers', {}) @@ -384,6 +364,40 @@ class TranscriptExtractor: error_msg = str(e) error_type = type(e).__name__ + # AttributeError: 'NoneType' object has no attribute 'get' hatası + # Bu genellikle FlareSolverr'dan dönen HTML'in parse edilememesinden kaynaklanır + if "AttributeError" in error_type and "'NoneType' object has no attribute 'get'" in error_msg: + logger.error(f"[TRANSCRIPT] ❌ Video {video_id} parse hatası: FlareSolverr HTML'i parse edilemedi") + logger.error(f"[TRANSCRIPT] Hata detayları: {error_type} - {error_msg[:300]}") + logger.error(f"[TRANSCRIPT] FlareSolverr HTML formatı youtube-transcript-api ile uyumsuz olabilir") + + # FlareSolverr'ı geçici olarak devre dışı bırak ve normal istek dene + if attempt < max_retries: + logger.warning(f"[TRANSCRIPT] ⚠️ FlareSolverr'ı atlayıp normal istek deneniyor (Deneme {attempt + 1}/{max_retries + 1})") + # FlareSolverr'ı geçici olarak devre dışı bırak + original_use_flaresolverr = self.use_flaresolverr + self.use_flaresolverr = False + try: + # Normal istek dene + api = YouTubeTranscriptApi() + fetched_transcript = api.fetch(video_id, languages=languages) + transcript = fetched_transcript.to_raw_data() + transcript_count = len(transcript) if transcript else 0 + logger.info(f"[TRANSCRIPT] ✅ Video {video_id} transcript'i normal istek ile başarıyla çıkarıldı ({transcript_count} segment)") + # FlareSolverr'ı tekrar etkinleştir + self.use_flaresolverr = original_use_flaresolverr + return transcript + except Exception as e2: + # FlareSolverr'ı tekrar etkinleştir + self.use_flaresolverr = original_use_flaresolverr + logger.error(f"[TRANSCRIPT] ❌ Normal istek de başarısız: {type(e2).__name__} - {str(e2)[:200]}") + # Retry yap + if attempt < max_retries: + continue + else: + logger.error(f"[TRANSCRIPT] ❌ Tüm denemeler başarısız (FlareSolverr ve normal istek)") + return None + # YouTubeDataUnparsable hatası için retry yap if "YouTubeDataUnparsable" in error_type or "Unparsable" in error_type: if attempt < max_retries: