diff --git a/src/transcript_extractor.py b/src/transcript_extractor.py index 0976310..c000667 100644 --- a/src/transcript_extractor.py +++ b/src/transcript_extractor.py @@ -220,13 +220,46 @@ class TranscriptExtractor: else: logger.warning(f"[FLARESOLVERR] ⚠️ Transcript URL'leri bulunamadı HTML'de") else: - # POST istekleri için JSON kontrolü - try: - import json - json.loads(response_content) - logger.debug(f"[FLARESOLVERR] ✅ POST response geçerli JSON") - except: - logger.warning(f"[FLARESOLVERR] ⚠️ POST response JSON değil, HTML olabilir") + # POST istekleri için JSON kontrolü ve HTML içindeki JSON extract + import re + import json + + # HTML içinde JSON var mı kontrol et (
 tag'i içinde olabilir)
+                        if response_content.strip().startswith(']*>(.*?)
', response_content, re.DOTALL) + if json_match: + json_content = json_match.group(1).strip() + try: + parsed_json = json.loads(json_content) + logger.debug(f"[FLARESOLVERR] ✅ POST response HTML içinde JSON bulundu ve parse edildi") + + # Hata kontrolü + if 'error' in parsed_json: + error_code = parsed_json.get('error', {}).get('code', 'unknown') + error_message = parsed_json.get('error', {}).get('message', 'unknown') + error_status = parsed_json.get('error', {}).get('status', '') + logger.warning(f"[FLARESOLVERR] ⚠️ POST response'unda hata: {error_code} - {error_message}") + + # FAILED_PRECONDITION hatası varsa, FlareSolverr POST istekleri için uygun değil + if error_code == 400 and 'FAILED_PRECONDITION' in error_status: + logger.warning(f"[FLARESOLVERR] ⚠️ YouTube API FlareSolverr POST isteklerini reddediyor (FAILED_PRECONDITION)") + logger.warning(f"[FLARESOLVERR] POST istekleri için FlareSolverr kullanılmayacak") + + # HTML yerine JSON kullan (response_content'i güncelle) + response_content = json_content + logger.debug(f"[FLARESOLVERR] ✅ POST response HTML'den JSON extract edildi") + except json.JSONDecodeError: + logger.warning(f"[FLARESOLVERR] ⚠️ POST response HTML içindeki JSON parse edilemedi") + else: + logger.warning(f"[FLARESOLVERR] ⚠️ POST response HTML ama içinde JSON bulunamadı") + else: + # Direkt JSON kontrolü + try: + json.loads(response_content) + logger.debug(f"[FLARESOLVERR] ✅ POST response geçerli JSON") + except: + logger.warning(f"[FLARESOLVERR] ⚠️ POST response JSON değil") # Response objesine transcript URL'lerini ve analiz sonuçlarını ekle (alternatif parse için) response_obj = FlareSolverrResponse(status_code, response_content, headers, url, is_post=is_post_request) @@ -369,58 +402,13 @@ class TranscriptExtractor: return original_get(session_self, url, **kwargs) def patched_post(session_self, url, **kwargs): - """requests.Session.post'i patch et - header'ları ekle ve FlareSolverr kullan""" - # YouTube API endpoint'leri için FlareSolverr kullan (youtubei/v1/player gibi) + """requests.Session.post'i patch et - header'ları ekle (FlareSolverr POST için kullanılmıyor)""" + # NOT: YouTube API POST istekleri FlareSolverr ile çalışmıyor (FAILED_PRECONDITION hatası) + # Bu yüzden POST istekleri için FlareSolverr kullanmayalım, sadece header'ları ekle is_youtube_api = ('youtube.com' in url or 'youtu.be' in url) and ('/youtubei/v1/' in url or '/api/' in url) - # FlareSolverr kullanılıyorsa ve YouTube API endpoint'i ise - if extractor_instance.use_flaresolverr and is_youtube_api and extractor_instance.flaresolverr_available: - logger.debug(f"[FLARESOLVERR] YouTube API POST isteği FlareSolverr üzerinden deneniyor: {url[:50]}...") - # POST için FlareSolverr'da request.post kullan - flaresolverr_response = extractor_instance._make_flaresolverr_request(url, 'POST', **kwargs) - if flaresolverr_response: - logger.debug(f"[FLARESOLVERR] ✅ FlareSolverr POST 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: - # Debug: JSON parse edilmeye çalışılan içeriği logla - logger.debug(f"[FLARESOLVERR] JSON parse deneniyor (POST), içerik tipi: {type(self.text)}, uzunluk: {len(self.text)}") - logger.debug(f"[FLARESOLVERR] İçerik önizleme (ilk 200 karakter): {self.text[:200]}") - - # Eğer HTML ise JSON parse etme - if self.text.strip().startswith('<') or 'html' in self.headers.get('content-type', '').lower(): - logger.warning(f"[FLARESOLVERR] ⚠️ HTML içerik JSON olarak parse edilmeye çalışılıyor, boş dict döndürülüyor") - return {} - - return json.loads(self.text) - except json.JSONDecodeError as e: - logger.error(f"[FLARESOLVERR] ❌ JSON parse hatası (POST): {e}") - logger.error(f"[FLARESOLVERR] İçerik (ilk 500 karakter): {self.text[:500]}") - return {} - except Exception as e: - logger.error(f"[FLARESOLVERR] ❌ JSON parse beklenmeyen hata (POST): {type(e).__name__} - {str(e)}") - 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 POST yanıt vermedi, normal istek deneniyor") - elif extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url): - logger.debug(f"[FLARESOLVERR] POST isteği tespit edildi, FlareSolverr kullanılmıyor (sadece header'lar): {url[:50]}...") + if is_youtube_api: + logger.debug(f"[FLARESOLVERR] POST isteği tespit edildi, FlareSolverr kullanılmıyor (FAILED_PRECONDITION önleme): {url[:50]}...") # Normal istek (header'ları ekle) headers = kwargs.get('headers', {}) @@ -638,16 +626,57 @@ class TranscriptExtractor: logger.error(f"[TRANSCRIPT] Hata detayları: {error_msg[:500]}") logger.error(f"[TRANSCRIPT] Bu video için transcript çıkarılamıyor (YouTube HTML yapısı değişmiş olabilir)") - # Debug: FlareSolverr response'unu analiz et (eğer kullanıldıysa) + # Debug: FlareSolverr response'unu analiz et ve transcript URL'lerini kullan (fallback) if self.use_flaresolverr and hasattr(self, '_last_flaresolverr_response'): logger.debug(f"[TRANSCRIPT] FlareSolverr response analizi yapılıyor...") last_response = self._last_flaresolverr_response - # Transcript URL'leri var mı kontrol et + # Transcript URL'leri var mı kontrol et ve kullan (fallback) transcript_urls = getattr(last_response, 'transcript_urls', []) if transcript_urls: - logger.warning(f"[TRANSCRIPT] ⚠️ FlareSolverr response'unda {len(transcript_urls)} transcript URL bulundu ama parse edilemedi") - logger.warning(f"[TRANSCRIPT] Bu URL'ler doğrudan kullanılabilir (fallback mekanizması henüz implement edilmedi)") + logger.warning(f"[TRANSCRIPT] ⚠️ FlareSolverr response'unda {len(transcript_urls)} transcript URL bulundu, fallback mekanizması deneniyor...") + + # İlk transcript URL'ini kullan (genellikle en uygun dil) + transcript_url = transcript_urls[0] + + # Unicode escape karakterlerini decode et (\u0026 -> &) + import codecs + transcript_url = codecs.decode(transcript_url, 'unicode_escape') + + logger.info(f"[TRANSCRIPT] Transcript URL'den transcript çekiliyor: {transcript_url[:100]}...") + + try: + import requests + import xml.etree.ElementTree as ET + + # Transcript URL'inden XML çek (browser header'ları ile) + headers = TranscriptExtractor._get_browser_headers() + response = requests.get(transcript_url, headers=headers, timeout=30) + response.raise_for_status() + + # XML'i parse et + root = ET.fromstring(response.text) + + # Transcript segment'lerini çıkar + transcript_list = [] + for text_elem in root.findall('.//text'): + start = float(text_elem.get('start', 0)) + duration = float(text_elem.get('dur', 0)) + text = text_elem.text or '' + + transcript_list.append({ + 'text': text, + 'start': start, + 'duration': duration + }) + + if transcript_list: + logger.info(f"[TRANSCRIPT] ✅ Transcript URL'den {len(transcript_list)} segment başarıyla çıkarıldı (fallback)") + return transcript_list + else: + logger.warning(f"[TRANSCRIPT] ⚠️ Transcript URL'den segment bulunamadı") + except Exception as fallback_err: + logger.error(f"[TRANSCRIPT] ❌ Transcript URL fallback hatası: {type(fallback_err).__name__} - {str(fallback_err)[:200]}") # ytInitialPlayerResponse var mı kontrol et yt_response_found = getattr(last_response, 'yt_initial_player_response_found', False)