YouTubeDataUnparsable
This commit is contained in:
@@ -50,10 +50,14 @@ class TranscriptExtractor:
|
|||||||
# FlareSolverr ayarları
|
# FlareSolverr ayarları
|
||||||
self.flaresolverr_url = flaresolverr_url or os.getenv('FLARESOLVERR_URL', 'http://192.168.1.27:8191/v1')
|
self.flaresolverr_url = flaresolverr_url or os.getenv('FLARESOLVERR_URL', 'http://192.168.1.27:8191/v1')
|
||||||
self.use_flaresolverr = bool(self.flaresolverr_url)
|
self.use_flaresolverr = bool(self.flaresolverr_url)
|
||||||
|
self.flaresolverr_available = False # Test sonucu
|
||||||
|
|
||||||
if self.use_flaresolverr:
|
if self.use_flaresolverr:
|
||||||
logger.info(f"[FLARESOLVERR] FlareSolverr etkin: {self.flaresolverr_url}")
|
logger.info(f"[FLARESOLVERR] FlareSolverr URL ayarlandı: {self.flaresolverr_url}")
|
||||||
|
# Test et ama başarısız olsa bile kullanmayı dene (test sırasında erişilemez olabilir)
|
||||||
self._test_flaresolverr()
|
self._test_flaresolverr()
|
||||||
|
if not self.flaresolverr_available:
|
||||||
|
logger.warning("[FLARESOLVERR] ⚠ Test başarısız, ancak gerçek isteklerde tekrar denenilecek")
|
||||||
else:
|
else:
|
||||||
logger.info("[FLARESOLVERR] FlareSolverr devre dışı")
|
logger.info("[FLARESOLVERR] FlareSolverr devre dışı")
|
||||||
|
|
||||||
@@ -68,14 +72,17 @@ class TranscriptExtractor:
|
|||||||
test_response = requests.get(f"{self.flaresolverr_url.replace('/v1', '')}/v1", timeout=5)
|
test_response = requests.get(f"{self.flaresolverr_url.replace('/v1', '')}/v1", timeout=5)
|
||||||
if test_response.status_code == 405: # Method Not Allowed normal (GET yerine POST bekliyor)
|
if test_response.status_code == 405: # Method Not Allowed normal (GET yerine POST bekliyor)
|
||||||
logger.info("[FLARESOLVERR] ✅ FlareSolverr erişilebilir")
|
logger.info("[FLARESOLVERR] ✅ FlareSolverr erişilebilir")
|
||||||
|
self.flaresolverr_available = True
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.warning(f"[FLARESOLVERR] ⚠️ FlareSolverr yanıtı beklenmedik: {test_response.status_code}")
|
logger.warning(f"[FLARESOLVERR] ⚠️ FlareSolverr yanıtı beklenmedik: {test_response.status_code}")
|
||||||
|
self.flaresolverr_available = False
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"[FLARESOLVERR] ⚠️ FlareSolverr test edilemedi: {e}")
|
logger.warning(f"[FLARESOLVERR] ⚠️ FlareSolverr test edilemedi: {e}")
|
||||||
logger.warning(f"[FLARESOLVERR] FlareSolverr devre dışı bırakılıyor")
|
logger.warning(f"[FLARESOLVERR] Test başarısız, ancak gerçek isteklerde tekrar denenilecek")
|
||||||
self.use_flaresolverr = False
|
self.flaresolverr_available = False
|
||||||
|
# use_flaresolverr'ı False yapma, gerçek isteklerde tekrar dene
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _make_flaresolverr_request(self, url: str, method: str = 'GET', **kwargs) -> Optional:
|
def _make_flaresolverr_request(self, url: str, method: str = 'GET', **kwargs) -> Optional:
|
||||||
@@ -164,9 +171,10 @@ class TranscriptExtractor:
|
|||||||
"""requests.Session.get'i patch et - header'ları ekle ve FlareSolverr kullan"""
|
"""requests.Session.get'i patch et - header'ları ekle ve FlareSolverr kullan"""
|
||||||
# FlareSolverr kullanılıyorsa ve YouTube URL'si ise
|
# FlareSolverr kullanılıyorsa ve YouTube URL'si ise
|
||||||
if extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url):
|
if extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url):
|
||||||
logger.info(f"[FLARESOLVERR] YouTube isteği FlareSolverr üzerinden: {url[:50]}...")
|
logger.debug(f"[FLARESOLVERR] YouTube isteği FlareSolverr üzerinden deneniyor: {url[:50]}...")
|
||||||
flaresolverr_response = extractor_instance._make_flaresolverr_request(url, 'GET', **kwargs)
|
flaresolverr_response = extractor_instance._make_flaresolverr_request(url, 'GET', **kwargs)
|
||||||
if flaresolverr_response:
|
if flaresolverr_response:
|
||||||
|
logger.debug(f"[FLARESOLVERR] ✅ FlareSolverr başarılı, response döndürülüyor")
|
||||||
# FlareSolverr response'unu requests.Response'a benzet
|
# FlareSolverr response'unu requests.Response'a benzet
|
||||||
class PatchedResponse:
|
class PatchedResponse:
|
||||||
def __init__(self, flaresolverr_response):
|
def __init__(self, flaresolverr_response):
|
||||||
@@ -192,7 +200,7 @@ class TranscriptExtractor:
|
|||||||
|
|
||||||
return PatchedResponse(flaresolverr_response)
|
return PatchedResponse(flaresolverr_response)
|
||||||
else:
|
else:
|
||||||
logger.warning(f"[FLARESOLVERR] FlareSolverr yanıt vermedi, normal istek deneniyor")
|
logger.debug(f"[FLARESOLVERR] FlareSolverr yanıt vermedi, normal istek deneniyor")
|
||||||
|
|
||||||
# Normal istek (header'ları ekle)
|
# Normal istek (header'ları ekle)
|
||||||
headers = kwargs.get('headers', {})
|
headers = kwargs.get('headers', {})
|
||||||
@@ -205,9 +213,10 @@ class TranscriptExtractor:
|
|||||||
"""requests.Session.post'i patch et - header'ları ekle ve FlareSolverr kullan"""
|
"""requests.Session.post'i patch et - header'ları ekle ve FlareSolverr kullan"""
|
||||||
# FlareSolverr kullanılıyorsa ve YouTube URL'si ise
|
# FlareSolverr kullanılıyorsa ve YouTube URL'si ise
|
||||||
if extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url):
|
if extractor_instance.use_flaresolverr and ('youtube.com' in url or 'youtu.be' in url):
|
||||||
logger.info(f"[FLARESOLVERR] YouTube POST isteği FlareSolverr üzerinden: {url[:50]}...")
|
logger.debug(f"[FLARESOLVERR] YouTube POST isteği FlareSolverr üzerinden deneniyor: {url[:50]}...")
|
||||||
flaresolverr_response = extractor_instance._make_flaresolverr_request(url, 'POST', **kwargs)
|
flaresolverr_response = extractor_instance._make_flaresolverr_request(url, 'POST', **kwargs)
|
||||||
if flaresolverr_response:
|
if flaresolverr_response:
|
||||||
|
logger.debug(f"[FLARESOLVERR] ✅ FlareSolverr başarılı, response döndürülüyor")
|
||||||
class PatchedResponse:
|
class PatchedResponse:
|
||||||
def __init__(self, flaresolverr_response):
|
def __init__(self, flaresolverr_response):
|
||||||
self.status_code = flaresolverr_response.status_code
|
self.status_code = flaresolverr_response.status_code
|
||||||
@@ -232,7 +241,7 @@ class TranscriptExtractor:
|
|||||||
|
|
||||||
return PatchedResponse(flaresolverr_response)
|
return PatchedResponse(flaresolverr_response)
|
||||||
else:
|
else:
|
||||||
logger.warning(f"[FLARESOLVERR] FlareSolverr yanıt vermedi, normal istek deneniyor")
|
logger.debug(f"[FLARESOLVERR] FlareSolverr yanıt vermedi, normal istek deneniyor")
|
||||||
|
|
||||||
# Normal istek (header'ları ekle)
|
# Normal istek (header'ları ekle)
|
||||||
headers = kwargs.get('headers', {})
|
headers = kwargs.get('headers', {})
|
||||||
@@ -326,13 +335,15 @@ class TranscriptExtractor:
|
|||||||
logger.debug(f"[RATE_LIMIT] İstek kaydedildi (Toplam aktif istek: {len(self.request_times)})")
|
logger.debug(f"[RATE_LIMIT] İstek kaydedildi (Toplam aktif istek: {len(self.request_times)})")
|
||||||
|
|
||||||
def fetch_transcript(self, video_id: str,
|
def fetch_transcript(self, video_id: str,
|
||||||
languages: List[str] = ['en']) -> Optional[List[Dict]]:
|
languages: List[str] = ['en'],
|
||||||
|
max_retries: int = 2) -> Optional[List[Dict]]:
|
||||||
"""
|
"""
|
||||||
Transcript çıkar (sync)
|
Transcript çıkar (sync) - Retry mekanizması ile
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
video_id: YouTube video ID
|
video_id: YouTube video ID
|
||||||
languages: Öncelik sırasına göre dil listesi
|
languages: Öncelik sırasına göre dil listesi
|
||||||
|
max_retries: Maksimum retry sayısı (YouTubeDataUnparsable için)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Transcript listesi veya None
|
Transcript listesi veya None
|
||||||
@@ -342,43 +353,72 @@ class TranscriptExtractor:
|
|||||||
# Rate limiting kontrolü
|
# Rate limiting kontrolü
|
||||||
self._check_rate_limit()
|
self._check_rate_limit()
|
||||||
|
|
||||||
try:
|
# Retry mekanizması
|
||||||
logger.debug(f"[TRANSCRIPT] YouTube Transcript API çağrısı yapılıyor: video_id={video_id}")
|
for attempt in range(max_retries + 1):
|
||||||
|
try:
|
||||||
# YouTube Transcript API kullanımı (yeni versiyon)
|
if attempt > 0:
|
||||||
# API instance oluştur ve fetch() metodunu kullan
|
# Retry için bekleme (YouTube'un HTML'i yenilemesi için)
|
||||||
api = YouTubeTranscriptApi()
|
wait_time = 5 + (attempt * 3) # 5, 8, 11 saniye
|
||||||
fetched_transcript = api.fetch(video_id, languages=languages)
|
logger.warning(f"[TRANSCRIPT] ⚠️ Retry {attempt}/{max_retries} - {wait_time} saniye bekleniyor...")
|
||||||
|
time.sleep(wait_time)
|
||||||
# Eski formatı döndürmek için to_raw_data() kullan
|
# Rate limit kontrolü tekrar yap
|
||||||
# Format: [{'text': '...', 'start': 1.36, 'duration': 1.68}, ...]
|
self._check_rate_limit()
|
||||||
transcript = fetched_transcript.to_raw_data()
|
|
||||||
|
|
||||||
transcript_count = len(transcript) if transcript else 0
|
|
||||||
logger.info(f"[TRANSCRIPT] ✅ Video {video_id} transcript'i başarıyla çıkarıldı ({transcript_count} segment)")
|
|
||||||
|
|
||||||
return transcript
|
|
||||||
except Exception as e:
|
|
||||||
error_msg = str(e)
|
|
||||||
error_type = type(e).__name__
|
|
||||||
|
|
||||||
logger.error(f"[TRANSCRIPT] ❌ Video {video_id} transcript çıkarımı başarısız: {error_type} - {error_msg[:200]}")
|
|
||||||
|
|
||||||
# IP blocking hatası tespit edilirse işaretle
|
|
||||||
if "blocking" in error_msg.lower() or "blocked" in error_msg.lower() or "IP" in error_msg or "IpBlocked" in error_type:
|
|
||||||
self.last_blocked_time = time.time()
|
|
||||||
self.block_count += 1
|
|
||||||
wait_time = 300 + (self.block_count * 60) # 5 dakika + (blocking sayısı * 1 dakika)
|
|
||||||
wait_time = min(wait_time, 1800) # Maksimum 30 dakika
|
|
||||||
|
|
||||||
logger.error(f"[TRANSCRIPT] 🚫 IP blocking tespit edildi! Video: {video_id}")
|
logger.debug(f"[TRANSCRIPT] YouTube Transcript API çağrısı yapılıyor: video_id={video_id} (Deneme: {attempt + 1}/{max_retries + 1})")
|
||||||
logger.error(f"[TRANSCRIPT] Toplam blocking sayısı: {self.block_count}, Sonraki isteklerde {wait_time} saniye ({wait_time/60:.1f} dakika) bekleme yapılacak")
|
|
||||||
logger.warning(f"[TRANSCRIPT] IP blocking detayları: {error_msg[:500]}")
|
|
||||||
|
|
||||||
# Çok fazla blocking varsa uyar
|
# YouTube Transcript API kullanımı (yeni versiyon)
|
||||||
if self.block_count >= 3:
|
# API instance oluştur ve fetch() metodunu kullan
|
||||||
logger.error(f"[TRANSCRIPT] ⚠️ UYARI: {self.block_count} kez IP blocking alındı! YouTube IP'nizi geçici olarak engellemiş olabilir.")
|
api = YouTubeTranscriptApi()
|
||||||
logger.error(f"[TRANSCRIPT] Öneriler: 1) Daha uzun bekleme (30+ dakika), 2) Farklı IP kullan, 3) Proxy/VPN kullan")
|
fetched_transcript = api.fetch(video_id, languages=languages)
|
||||||
|
|
||||||
return None
|
# Eski formatı döndürmek için to_raw_data() kullan
|
||||||
|
# Format: [{'text': '...', 'start': 1.36, 'duration': 1.68}, ...]
|
||||||
|
transcript = fetched_transcript.to_raw_data()
|
||||||
|
|
||||||
|
transcript_count = len(transcript) if transcript else 0
|
||||||
|
logger.info(f"[TRANSCRIPT] ✅ Video {video_id} transcript'i başarıyla çıkarıldı ({transcript_count} segment, Deneme: {attempt + 1})")
|
||||||
|
|
||||||
|
return transcript
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = str(e)
|
||||||
|
error_type = type(e).__name__
|
||||||
|
|
||||||
|
# YouTubeDataUnparsable hatası için retry yap
|
||||||
|
if "YouTubeDataUnparsable" in error_type or "Unparsable" in error_type:
|
||||||
|
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
|
||||||
|
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)")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Diğer hatalar için normal işlem
|
||||||
|
logger.error(f"[TRANSCRIPT] ❌ Video {video_id} transcript çıkarımı başarısız: {error_type} - {error_msg[:200]}")
|
||||||
|
|
||||||
|
# IP blocking hatası tespit edilirse işaretle
|
||||||
|
if "blocking" in error_msg.lower() or "blocked" in error_msg.lower() or "IP" in error_msg or "IpBlocked" in error_type:
|
||||||
|
self.last_blocked_time = time.time()
|
||||||
|
self.block_count += 1
|
||||||
|
wait_time = 300 + (self.block_count * 60) # 5 dakika + (blocking sayısı * 1 dakika)
|
||||||
|
wait_time = min(wait_time, 1800) # Maksimum 30 dakika
|
||||||
|
|
||||||
|
logger.error(f"[TRANSCRIPT] 🚫 IP blocking tespit edildi! Video: {video_id}")
|
||||||
|
logger.error(f"[TRANSCRIPT] Toplam blocking sayısı: {self.block_count}, Sonraki isteklerde {wait_time} saniye ({wait_time/60:.1f} dakika) bekleme yapılacak")
|
||||||
|
logger.warning(f"[TRANSCRIPT] IP blocking detayları: {error_msg[:500]}")
|
||||||
|
|
||||||
|
# Çok fazla blocking varsa uyar
|
||||||
|
if self.block_count >= 3:
|
||||||
|
logger.error(f"[TRANSCRIPT] ⚠️ UYARI: {self.block_count} kez IP blocking alındı! YouTube IP'nizi geçici olarak engellemiş olabilir.")
|
||||||
|
logger.error(f"[TRANSCRIPT] Öneriler: 1) Daha uzun bekleme (30+ dakika), 2) Farklı IP kullan, 3) Proxy/VPN kullan")
|
||||||
|
|
||||||
|
# Retry yapılmayacak hatalar için direkt dön
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Tüm retry'lar başarısız
|
||||||
|
return None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user