Transkript ip blocked

This commit is contained in:
salvacybersec
2025-11-13 03:52:26 +03:00
parent 372ed6401b
commit bb416e1f37
5 changed files with 135 additions and 8 deletions

View File

@@ -8,6 +8,7 @@ YouTube video transkriptlerini otomatik olarak çıkarıp, tam metin içeren RSS
-**Web Server Modu** - Flask ile RESTful API
-**API Key Authentication** - Tüm endpoint'ler API key gerektirir
-**Güvenlik Önlemleri** - SQL injection, XSS, rate limiting koruması
-**Transcript Cache** - 3 günlük cache ile YouTube IP blocking önleme
- ✅ RSS-Bridge entegrasyonu (100+ video desteği)
- ✅ Async rate limiting (AIOLimiter)
- ✅ SpaCy ile Sentence Boundary Detection
@@ -127,6 +128,17 @@ security:
**Detaylı güvenlik dokümantasyonu için:** [SECURITY.md](SECURITY.md)
### Transcript Cache
Sistem, işlenmiş transcript'leri **3 gün boyunca cache'de tutar**. Bu özellik:
- **YouTube IP blocking'i önler**: Aynı videoların transcript'ini tekrar çekmez
- **Performans artışı**: Cache'den hızlı yanıt
- **Rate limiting azaltır**: Gereksiz API isteklerini önler
- **Otomatik yenileme**: 3 gün sonra cache geçersiz olur, yeni transcript çekilir
Cache kontrolü otomatik yapılır ve kullanıcı müdahalesi gerektirmez.
## Proje Yapısı
```

View File

@@ -311,11 +311,26 @@ channel_id = get_channel_id_from_handle(handle_url)
- `update_video_transcript(video_id, raw, clean, status, language)` - Transcript güncelleme
- `get_processed_videos(limit=None, channel_id=None)` - İşlenmiş videoları getir
- `mark_video_failed(video_id, reason)` - Kalıcı hata işaretleme (status=2)
- `is_transcript_cached(video_id, cache_days=3)` - Transcript cache kontrolü (3 günlük)
- `get_cached_transcript(video_id)` - Cache'den transcript getirme
- **Query Performance**: `EXPLAIN QUERY PLAN` ile index kullanımını doğrula
- [ ] **Transcript Cache Mekanizması**:
- **3 Günlük Cache**: İşlenmiş transcript'ler 3 gün boyunca cache'de tutulur
- **Cache Kontrolü**: Transcript çıkarımından önce cache kontrolü yapılır
- **Avantajlar**:
- YouTube IP blocking riskini azaltır
- Performans artışı (tekrar isteklerde hızlı yanıt)
- API rate limiting'i azaltır
- Aynı videoların transcript'ini tekrar çekmez
- **Cache Süresi**: `processed_at_utc` tarihine göre 3 gün kontrolü
- **Otomatik Yenileme**: 3 gün sonra cache geçersiz olur, yeni transcript çekilir
- [ ] Yeni video tespiti algoritması:
1. RSS-Bridge feed'den son videoları çek
2. SQLite veritabanında `video_id` ile sorgula
3. Sadece yeni videoları (veritabanında olmayan) işle
4. **Cache Kontrolü**: İşlenmiş videolar için 3 günlük cache kontrolü yap
- Eğer 3 gün içinde işlenmişse, transcript çıkarma (cache'den kullan)
- 3 günden eskiyse, yeni transcript çek
- [ ] Transaction yönetimi (ACID compliance)
- [ ] Connection pooling ve error handling

View File

@@ -217,3 +217,72 @@ class Database:
""", (datetime.now(timezone.utc).isoformat(), video_id))
self.conn.commit()
def is_transcript_cached(self, video_id: str, cache_days: int = 3) -> bool:
"""
Video transcript'i cache'de mi ve hala geçerli mi kontrol et
Args:
video_id: Video ID
cache_days: Cache geçerlilik süresi (gün)
Returns:
True eğer cache'de ve geçerliyse, False değilse
"""
if not self._validate_video_id(video_id):
return False
cursor = self.conn.cursor()
# 3 gün içinde işlenmiş ve başarılı (status=1) transcript var mı?
cursor.execute("""
SELECT processed_at_utc, transcript_status, transcript_clean
FROM videos
WHERE video_id = ?
AND transcript_status = 1
AND processed_at_utc IS NOT NULL
""", (video_id,))
row = cursor.fetchone()
if not row:
return False
# processed_at_utc'yi parse et ve 3 gün kontrolü yap
try:
processed_at = datetime.fromisoformat(row['processed_at_utc'].replace('Z', '+00:00'))
now = datetime.now(timezone.utc)
days_diff = (now - processed_at).days
# 3 gün içindeyse ve transcript_clean varsa cache geçerli
if days_diff < cache_days and row['transcript_clean']:
return True
except (ValueError, AttributeError):
return False
return False
def get_cached_transcript(self, video_id: str) -> Optional[Dict]:
"""
Cache'den transcript'i getir (eğer varsa)
Args:
video_id: Video ID
Returns:
Video dict (transcript_clean ile) veya None
"""
if not self._validate_video_id(video_id):
return None
cursor = self.conn.cursor()
cursor.execute("""
SELECT * FROM videos
WHERE video_id = ?
AND transcript_status = 1
AND transcript_clean IS NOT NULL
""", (video_id,))
row = cursor.fetchone()
if row:
return dict(row)
return None

View File

@@ -9,19 +9,28 @@ import time
class TranscriptExtractor:
"""YouTube transcript çıkarıcı sınıfı"""
def __init__(self, rate_limit: int = 5, time_window: int = 10):
def __init__(self, rate_limit: int = 2, time_window: int = 30):
"""
Args:
rate_limit: Zaman penceresi başına maksimum istek sayısı
rate_limit: Zaman penceresi başına maksimum istek sayısı (YouTube IP blocking'i önlemek için düşük)
time_window: Zaman penceresi (saniye)
"""
self.rate_limit = rate_limit
self.time_window = time_window
self.request_times = []
self.last_blocked_time = 0
def _check_rate_limit(self):
"""Rate limiting kontrolü (basit implementasyon)"""
"""Rate limiting kontrolü (YouTube IP blocking'i önlemek için)"""
now = time.time()
# Eğer son 5 dakikada IP blocking hatası aldıysak, daha uzun bekle
if self.last_blocked_time > 0 and (now - self.last_blocked_time) < 300:
wait_time = 60 # 1 dakika bekle
print(f"IP blocking sonrası bekleme: {wait_time} saniye")
time.sleep(wait_time)
self.last_blocked_time = 0 # Reset
# Son time_window saniyesindeki istekleri filtrele
self.request_times = [t for t in self.request_times if now - t < self.time_window]
@@ -29,11 +38,20 @@ class TranscriptExtractor:
if len(self.request_times) >= self.rate_limit:
sleep_time = self.time_window - (now - self.request_times[0])
if sleep_time > 0:
print(f"Rate limit: {sleep_time:.1f} saniye bekleniyor...")
time.sleep(sleep_time)
# Tekrar filtrele
now = time.time()
self.request_times = [t for t in self.request_times if now - t < self.time_window]
# İstekler arasında minimum bekleme (YouTube blocking'i önlemek için)
if self.request_times:
time_since_last = now - self.request_times[-1]
min_interval = 3 # Minimum 3 saniye
if time_since_last < min_interval:
sleep_time = min_interval - time_since_last
time.sleep(sleep_time)
# İstek zamanını kaydet
self.request_times.append(time.time())
@@ -64,6 +82,13 @@ class TranscriptExtractor:
return transcript
except Exception as e:
print(f"Error fetching transcript for {video_id}: {e}")
error_msg = str(e)
print(f"Error fetching transcript for {video_id}: {error_msg}")
# IP blocking hatası tespit edilirse işaretle
if "blocking" in error_msg.lower() or "blocked" in error_msg.lower():
self.last_blocked_time = time.time()
print(f"IP blocking tespit edildi, sonraki isteklerde daha uzun bekleme yapılacak")
return None

View File

@@ -1,7 +1,7 @@
"""
Flask web server - RSS-Bridge benzeri URL template sistemi
"""
from flask import Flask, request, Response, jsonify, g, after_request
from flask import Flask, request, Response, jsonify, g
from typing import Optional
import sys
import os
@@ -193,13 +193,18 @@ def process_channel(channel_id: str, max_items: int = 50) -> dict:
if not db.is_video_processed(video['video_id']):
db.add_video(video)
# Bekleyen videoları işle (ilk 20)
pending_videos = db.get_pending_videos()[:20]
# Bekleyen videoları işle (YouTube IP blocking'i önlemek için sadece 5 video)
pending_videos = db.get_pending_videos()[:5]
for video in pending_videos:
if video['channel_id'] != channel_id:
continue
# Cache kontrolü: 3 gün içinde işlenmiş transcript varsa atla
if db.is_transcript_cached(video['video_id'], cache_days=3):
print(f"Video {video['video_id']} transcript'i cache'de (3 gün içinde işlenmiş), atlanıyor")
continue
try:
# Transcript çıkar
transcript = extractor.fetch_transcript(
@@ -285,7 +290,8 @@ def generate_feed():
return jsonify({
'error': 'Henüz işlenmiş video yok',
'channel_id': normalized_channel_id,
'message': 'Lütfen birkaç dakika sonra tekrar deneyin'
'message': 'Transcript\'ler arka planda işleniyor. Lütfen birkaç dakika sonra tekrar deneyin.',
'note': 'YouTube IP blocking nedeniyle transcript çıkarımı yavaş olabilir. İlk istekte birkaç dakika bekleyin.'
}), 404
# RSS feed oluştur