yapısal
This commit is contained in:
@@ -92,7 +92,13 @@ class TranscriptExtractor:
|
||||
|
||||
def _make_flaresolverr_request(self, url: str, method: str = 'GET', **kwargs) -> Optional:
|
||||
"""FlareSolverr üzerinden istek yap"""
|
||||
if not self.use_flaresolverr:
|
||||
# Availability kontrolü: Eğer FlareSolverr erişilemiyorsa hiç deneme
|
||||
if not self.use_flaresolverr or not self.flaresolverr_url:
|
||||
return None
|
||||
|
||||
# Eğer daha önce erişilemediği tespit edildiyse, tekrar deneme
|
||||
if hasattr(self, 'flaresolverr_available') and not self.flaresolverr_available:
|
||||
logger.debug(f"[FLARESOLVERR] FlareSolverr erişilemiyor (available=False), istek atlanıyor")
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -104,7 +110,7 @@ class TranscriptExtractor:
|
||||
flaresolverr_payload = {
|
||||
"cmd": "request.post",
|
||||
"url": url,
|
||||
"maxTimeout": 60000, # 60 saniye timeout
|
||||
"maxTimeout": 90000, # 90 saniye timeout (FlareSolverr'ın tarayıcı açması zaman alabilir)
|
||||
}
|
||||
|
||||
# POST data'sını ekle
|
||||
@@ -116,7 +122,7 @@ class TranscriptExtractor:
|
||||
flaresolverr_payload = {
|
||||
"cmd": "request.get",
|
||||
"url": url,
|
||||
"maxTimeout": 60000, # 60 saniye timeout
|
||||
"maxTimeout": 90000, # 90 saniye timeout (FlareSolverr'ın tarayıcı açması zaman alabilir)
|
||||
}
|
||||
|
||||
# Header'ları ekle
|
||||
@@ -126,11 +132,12 @@ class TranscriptExtractor:
|
||||
|
||||
logger.debug(f"[FLARESOLVERR] İstek gönderiliyor: {url[:50]}...")
|
||||
|
||||
# FlareSolverr timeout'unu kısalt (erişilemezse hemen normal isteğe geç)
|
||||
# FlareSolverr timeout'u (FlareSolverr'ın tarayıcı açması zaman alabilir)
|
||||
# maxTimeout 90 saniye olduğu için, HTTP timeout'u da biraz daha uzun tutuyoruz
|
||||
response = requests.post(
|
||||
self.flaresolverr_url,
|
||||
json=flaresolverr_payload,
|
||||
timeout=10 # 10 saniye timeout (erişilemezse hemen normal isteğe geç)
|
||||
timeout=95 # 95 saniye timeout (maxTimeout'dan biraz fazla)
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
@@ -147,11 +154,24 @@ class TranscriptExtractor:
|
||||
def __init__(self, status_code, text, headers, url, is_post=False):
|
||||
self.status_code = status_code
|
||||
self.text = text
|
||||
self.content = text.encode('utf-8') if isinstance(text, str) else text
|
||||
self.headers = headers if headers else {}
|
||||
# Encoding'i doğru ayarla (UTF-8)
|
||||
if isinstance(text, str):
|
||||
self.content = text.encode('utf-8')
|
||||
self.encoding = 'utf-8'
|
||||
else:
|
||||
self.content = text
|
||||
self.encoding = 'utf-8'
|
||||
# Header'ları dict olarak ayarla (requests.Response uyumluluğu için)
|
||||
if isinstance(headers, dict):
|
||||
self.headers = headers
|
||||
else:
|
||||
self.headers = dict(headers) if headers else {}
|
||||
self.url = url
|
||||
self.ok = 200 <= status_code < 300
|
||||
self.is_post = is_post # POST isteği mi?
|
||||
# requests.Response uyumluluğu için ek özellikler
|
||||
self.reason = 'OK' if self.ok else 'Error'
|
||||
self.apparent_encoding = 'utf-8'
|
||||
|
||||
def json(self):
|
||||
import json
|
||||
@@ -173,22 +193,30 @@ class TranscriptExtractor:
|
||||
content_size = len(response_content)
|
||||
logger.info(f"[FLARESOLVERR] ✅ İstek başarılı: HTTP {status_code}, {content_size} byte içerik ({'POST' if is_post_request else 'GET'})")
|
||||
|
||||
# Debug: İçeriğin ilk 500 karakterini logla
|
||||
logger.debug(f"[FLARESOLVERR] İçerik önizleme (ilk 500 karakter): {response_content[:500]}")
|
||||
# Debug: İçeriğin ilk 2000 karakterini logla (daha detaylı analiz için)
|
||||
logger.debug(f"[FLARESOLVERR] İçerik önizleme (ilk 2000 karakter): {response_content[:2000]}")
|
||||
|
||||
# GET istekleri için HTML analizi (POST istekleri genellikle JSON)
|
||||
transcript_urls = []
|
||||
yt_initial_player_response_found = False
|
||||
|
||||
if not is_post_request:
|
||||
import re
|
||||
if 'ytInitialPlayerResponse' in response_content or 'ytInitialData' in response_content:
|
||||
logger.debug(f"[FLARESOLVERR] ✅ YouTube player response bulundu HTML'de")
|
||||
# YouTube player response kontrolü
|
||||
if 'ytInitialPlayerResponse' in response_content:
|
||||
logger.debug(f"[FLARESOLVERR] ✅ ytInitialPlayerResponse bulundu HTML'de")
|
||||
yt_initial_player_response_found = True
|
||||
elif 'ytInitialData' in response_content:
|
||||
logger.debug(f"[FLARESOLVERR] ✅ ytInitialData bulundu HTML'de")
|
||||
yt_initial_player_response_found = True
|
||||
else:
|
||||
logger.warning(f"[FLARESOLVERR] ⚠️ YouTube player response bulunamadı HTML'de")
|
||||
|
||||
# Debug: Transcript endpoint URL'lerini ara
|
||||
transcript_urls = re.findall(r'https?://[^"\s]+timedtext[^"\s]*', response_content)
|
||||
# Transcript endpoint URL'lerini ara (daha kapsamlı pattern)
|
||||
transcript_urls = re.findall(r'https?://[^"\s<>]+timedtext[^"\s<>]*', response_content)
|
||||
if transcript_urls:
|
||||
logger.debug(f"[FLARESOLVERR] ✅ Transcript URL'leri bulundu: {len(transcript_urls)} adet")
|
||||
logger.debug(f"[FLARESOLVERR] İlk transcript URL: {transcript_urls[0][:100]}...")
|
||||
logger.debug(f"[FLARESOLVERR] İlk transcript URL: {transcript_urls[0][:150]}...")
|
||||
else:
|
||||
logger.warning(f"[FLARESOLVERR] ⚠️ Transcript URL'leri bulunamadı HTML'de")
|
||||
else:
|
||||
@@ -200,7 +228,36 @@ class TranscriptExtractor:
|
||||
except:
|
||||
logger.warning(f"[FLARESOLVERR] ⚠️ POST response JSON değil, HTML olabilir")
|
||||
|
||||
return FlareSolverrResponse(status_code, response_content, headers, url, is_post=is_post_request)
|
||||
# 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)
|
||||
response_obj.transcript_urls = transcript_urls # Alternatif parse için sakla
|
||||
response_obj.yt_initial_player_response_found = yt_initial_player_response_found
|
||||
|
||||
# Debug: FlareSolverr response'unu geçici dosyaya kaydet (YouTubeDataUnparsable hatası için)
|
||||
if not is_post_request and logger.isEnabledFor(logging.DEBUG):
|
||||
try:
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Video ID'yi URL'den çıkar
|
||||
video_id_match = None
|
||||
if 'watch?v=' in url:
|
||||
video_id_match = url.split('watch?v=')[1].split('&')[0]
|
||||
|
||||
if video_id_match:
|
||||
debug_dir = Path('output') / 'flaresolverr_debug'
|
||||
debug_dir.mkdir(parents=True, exist_ok=True)
|
||||
debug_file = debug_dir / f"flaresolverr_response_{video_id_match}_{int(time.time())}.html"
|
||||
|
||||
with open(debug_file, 'w', encoding='utf-8') as f:
|
||||
f.write(response_content)
|
||||
|
||||
logger.debug(f"[FLARESOLVERR] Debug: Response kaydedildi: {debug_file}")
|
||||
except Exception as debug_err:
|
||||
logger.debug(f"[FLARESOLVERR] Debug dosyası kaydedilemedi: {debug_err}")
|
||||
|
||||
return response_obj
|
||||
else:
|
||||
error = result.get('message', 'Unknown error')
|
||||
logger.error(f"[FLARESOLVERR] ❌ FlareSolverr hatası: {error}")
|
||||
@@ -209,6 +266,16 @@ class TranscriptExtractor:
|
||||
logger.error(f"[FLARESOLVERR] ❌ FlareSolverr HTTP hatası: {response.status_code}")
|
||||
return None
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
logger.warning(f"[FLARESOLVERR] ⚠️ FlareSolverr timeout (95 saniye), normal isteğe geçiliyor")
|
||||
return None
|
||||
except requests.exceptions.ReadTimeout:
|
||||
logger.warning(f"[FLARESOLVERR] ⚠️ FlareSolverr read timeout, normal isteğe geçiliyor")
|
||||
return None
|
||||
except requests.exceptions.ConnectTimeout:
|
||||
logger.warning(f"[FLARESOLVERR] ⚠️ FlareSolverr connect timeout (erişilemiyor), devre dışı bırakılıyor")
|
||||
self.flaresolverr_available = False # Erişilemediği için devre dışı bırak
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"[FLARESOLVERR] ❌ FlareSolverr istek hatası: {type(e).__name__} - {str(e)[:200]}")
|
||||
return None
|
||||
@@ -233,11 +300,14 @@ class TranscriptExtractor:
|
||||
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:
|
||||
if extractor_instance.use_flaresolverr and is_video_page and extractor_instance.flaresolverr_available:
|
||||
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")
|
||||
# FlareSolverr response'unu instance'a kaydet (YouTubeDataUnparsable hatası için analiz)
|
||||
extractor_instance._last_flaresolverr_response = flaresolverr_response
|
||||
|
||||
# FlareSolverr response'unu requests.Response'a benzet
|
||||
class PatchedResponse:
|
||||
def __init__(self, flaresolverr_response):
|
||||
@@ -247,6 +317,9 @@ class TranscriptExtractor:
|
||||
self.headers = flaresolverr_response.headers
|
||||
self.url = flaresolverr_response.url
|
||||
self.ok = 200 <= self.status_code < 300
|
||||
# FlareSolverr response metadata'sını sakla
|
||||
self.flaresolverr_transcript_urls = getattr(flaresolverr_response, 'transcript_urls', [])
|
||||
self.flaresolverr_yt_response_found = getattr(flaresolverr_response, 'yt_initial_player_response_found', False)
|
||||
|
||||
def json(self):
|
||||
import json
|
||||
@@ -278,9 +351,12 @@ class TranscriptExtractor:
|
||||
return PatchedResponse(flaresolverr_response)
|
||||
else:
|
||||
logger.debug(f"[FLARESOLVERR] FlareSolverr yanıt vermedi, normal istek deneniyor")
|
||||
elif extractor_instance.use_flaresolverr and is_video_page and not extractor_instance.flaresolverr_available:
|
||||
# FlareSolverr erişilemiyor, normal istek yap
|
||||
logger.debug(f"[FLARESOLVERR] FlareSolverr erişilemiyor, normal istek yapılıyor: {url[:50]}...")
|
||||
elif extractor_instance.use_flaresolverr and is_video_page:
|
||||
# FlareSolverr erişilemiyor veya available=False, normal istek yap
|
||||
if not extractor_instance.flaresolverr_available:
|
||||
logger.debug(f"[FLARESOLVERR] FlareSolverr erişilemiyor (available=False), normal istek yapılıyor: {url[:50]}...")
|
||||
else:
|
||||
logger.debug(f"[FLARESOLVERR] FlareSolverr kullanılamıyor, normal istek yapılıyor: {url[:50]}...")
|
||||
elif extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url) and '/api/' 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]}...")
|
||||
@@ -524,17 +600,65 @@ class TranscriptExtractor:
|
||||
logger.error(f"[TRANSCRIPT] ❌ Tüm denemeler başarısız (FlareSolverr ve normal istek)")
|
||||
return None
|
||||
|
||||
# YouTubeDataUnparsable hatası için retry yap
|
||||
# YouTubeDataUnparsable hatası için retry yap ve alternatif yöntemler dene
|
||||
if "YouTubeDataUnparsable" in error_type or "Unparsable" in error_type:
|
||||
logger.warning(f"[TRANSCRIPT] ⚠️ Video {video_id} parse hatası (Deneme {attempt + 1}/{max_retries + 1}): {error_type}")
|
||||
logger.warning(f"[TRANSCRIPT] Hata mesajı: {error_msg[:300]}")
|
||||
|
||||
# FlareSolverr kullanıldıysa, normal isteğe geç
|
||||
if self.use_flaresolverr and attempt == 0:
|
||||
logger.warning(f"[TRANSCRIPT] ⚠️ FlareSolverr ile parse edilemedi, normal isteğe geçiliyor...")
|
||||
# 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.warning(f"[TRANSCRIPT] ⚠️ Normal istek de başarısız: {type(e2).__name__} - {str(e2)[:200]}")
|
||||
# Retry yap
|
||||
if attempt < max_retries:
|
||||
continue
|
||||
|
||||
# Retry yap
|
||||
if attempt < max_retries:
|
||||
logger.warning(f"[TRANSCRIPT] ⚠️ Video {video_id} parse hatası (Deneme {attempt + 1}/{max_retries + 1}): {error_type}")
|
||||
logger.warning(f"[TRANSCRIPT] Hata mesajı: {error_msg[:300]}")
|
||||
logger.warning(f"[TRANSCRIPT] Retry yapılacak...")
|
||||
continue # Retry yap
|
||||
continue
|
||||
else:
|
||||
logger.error(f"[TRANSCRIPT] ❌ Video {video_id} parse hatası - Tüm denemeler başarısız: {error_type}")
|
||||
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)
|
||||
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_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)")
|
||||
|
||||
# ytInitialPlayerResponse var mı kontrol et
|
||||
yt_response_found = getattr(last_response, 'yt_initial_player_response_found', False)
|
||||
if yt_response_found:
|
||||
logger.warning(f"[TRANSCRIPT] ⚠️ FlareSolverr response'unda ytInitialPlayerResponse bulundu ama parse edilemedi")
|
||||
logger.warning(f"[TRANSCRIPT] YouTube HTML yapısı değişmiş olabilir veya kütüphane FlareSolverr response'unu tanımıyor")
|
||||
else:
|
||||
logger.warning(f"[TRANSCRIPT] ⚠️ FlareSolverr response'unda ytInitialPlayerResponse bulunamadı")
|
||||
|
||||
logger.debug(f"[TRANSCRIPT] FlareSolverr kullanıldı ama parse edilemedi - YouTube HTML yapısı değişmiş olabilir")
|
||||
|
||||
return None
|
||||
|
||||
# Diğer hatalar için normal işlem
|
||||
|
||||
Reference in New Issue
Block a user