740 lines
25 KiB
Markdown
740 lines
25 KiB
Markdown
|
|
# YouTube Transcript RSS Feed - Geliştirme Planı
|
|||
|
|
|
|||
|
|
## Proje Özeti
|
|||
|
|
|
|||
|
|
YouTube video transkriptlerini otomatik olarak çıkarıp, tam metin içeren RSS feed'ine dönüştüren otomatik bir pipeline geliştirilmesi. Sistem, Python tabanlı transcript çıkarımı ve RSS feed oluşturma ile RSS-Bridge entegrasyonu seçeneklerini içerir.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 1: Proje Altyapısı ve Ortam Kurulumu
|
|||
|
|
|
|||
|
|
### 1.1. Teknoloji Stack Seçimi
|
|||
|
|
|
|||
|
|
**Ana Yaklaşım: Python Tabanlı (Önerilen)**
|
|||
|
|
- **Transcript Çıkarımı**: `youtube-transcript-api`
|
|||
|
|
- **RSS Oluşturma**: `python-feedgen`
|
|||
|
|
- **Video Listesi**: RSS-Bridge (RSS feed parser)
|
|||
|
|
- **Dil**: Python 3.8+
|
|||
|
|
|
|||
|
|
### 1.2. Geliştirme Ortamı Kurulumu
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] Python 3.8+ kurulumu doğrulama
|
|||
|
|
- [ ] Virtual environment oluşturma (`python -m venv venv`)
|
|||
|
|
- [ ] Gerekli paketlerin kurulumu:
|
|||
|
|
```bash
|
|||
|
|
pip install youtube-transcript-api
|
|||
|
|
pip install feedgen
|
|||
|
|
pip install python-dateutil
|
|||
|
|
pip install feedparser # RSS-Bridge feed'lerini parse etmek için
|
|||
|
|
pip install requests # HTTP istekleri için
|
|||
|
|
pip install aiolimiter # Async rate limiting için
|
|||
|
|
pip install httpx # Async HTTP client
|
|||
|
|
pip install spacy # NLP ve Sentence Boundary Detection için
|
|||
|
|
```
|
|||
|
|
**Not**: SQLite Python'da built-in (`sqlite3` modülü), ekstra kurulum gerekmez.
|
|||
|
|
**SpaCy Model**: `python -m spacy download en_core_web_sm` (veya `tr_core_news_sm` Türkçe için)
|
|||
|
|
- [ ] Proje dizin yapısı oluşturma:
|
|||
|
|
```
|
|||
|
|
yttranscriptrss/
|
|||
|
|
├── src/
|
|||
|
|
│ ├── transcript_extractor.py
|
|||
|
|
│ ├── transcript_cleaner.py
|
|||
|
|
│ ├── rss_generator.py
|
|||
|
|
│ ├── video_fetcher.py
|
|||
|
|
│ └── database.py
|
|||
|
|
├── config/
|
|||
|
|
│ └── config.yaml
|
|||
|
|
├── data/
|
|||
|
|
│ └── videos.db
|
|||
|
|
├── output/
|
|||
|
|
│ └── transcript_feed.xml
|
|||
|
|
├── tests/
|
|||
|
|
├── requirements.txt
|
|||
|
|
└── main.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 1-2 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 2: Transcript Çıkarımı ve Temizleme Modülü
|
|||
|
|
|
|||
|
|
### 2.1. Transcript Çıkarımı (`transcript_extractor.py`)
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] `youtube-transcript-api` ile video ID'den transcript çıkarma
|
|||
|
|
- [ ] Çoklu dil desteği (fallback mekanizması)
|
|||
|
|
- Örnek: `languages=['tr', 'en']` - önce Türkçe, yoksa İngilizce
|
|||
|
|
- [ ] Hata yönetimi:
|
|||
|
|
- Transcript bulunamama durumları
|
|||
|
|
- API rate limiting
|
|||
|
|
- Geçersiz video ID'ler
|
|||
|
|
- [ ] Raw transcript formatını anlama:
|
|||
|
|
```python
|
|||
|
|
# Format: [{"text": "...", "start": 0.0, "duration": 2.5}, ...]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Kritik Gereksinimler:**
|
|||
|
|
- Headless browser kullanmama (API tabanlı yaklaşım)
|
|||
|
|
- Otomatik ve manuel transkriptleri destekleme
|
|||
|
|
- **Async Rate Limiting**: AIOLimiter ile eş zamanlı istek yönetimi
|
|||
|
|
- API limiti: 10 saniyede 5 istek
|
|||
|
|
- Async batching ile paralel işleme
|
|||
|
|
- **Retry-After Header**: 429 hatalarında `Retry-After` header'ını kullanma
|
|||
|
|
- Dinamik bekleme süresi (statik delay yerine)
|
|||
|
|
- Exponential backoff mekanizması
|
|||
|
|
- Timeout ve retry mekanizmaları
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 3-4 gün
|
|||
|
|
|
|||
|
|
### 2.2. Transcript Temizleme ve Dönüştürme (`transcript_cleaner.py`)
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] **Veri Temizleme Pipeline**:
|
|||
|
|
- **Artifact Removal**:
|
|||
|
|
- Zaman tabanlı işaretçileri kaldırma
|
|||
|
|
- Konuşma dışı etiketler: `[Music]`, `[Applause]`, vb.
|
|||
|
|
- Aşırı boşlukları temizleme
|
|||
|
|
- **Normalizasyon**:
|
|||
|
|
- Unicode karakter normalleştirme
|
|||
|
|
- Metin standardizasyonu
|
|||
|
|
- [ ] **Sentence Boundary Detection (SBD) - SpaCy Entegrasyonu**:
|
|||
|
|
- **SpaCy Model Kullanımı**: `en_core_web_sm` veya `tr_core_news_sm`
|
|||
|
|
- Noktalama işaretlerinin ötesine geçme:
|
|||
|
|
- Doğal duraklama noktalarını tespit
|
|||
|
|
- Anlamsal sınırları belirleme
|
|||
|
|
- Konuşmacı değişikliklerini algılama
|
|||
|
|
- Fragment'ları birleştirme ve cümle sınırlarını tespit etme
|
|||
|
|
- Paragraf yapısı oluşturma (cümle sayısı veya anlamsal değişikliklere göre)
|
|||
|
|
- **Özelleştirme**: Özel içerik için kural tabanlı SBD uzantıları
|
|||
|
|
- [ ] **HTML Wrapping**:
|
|||
|
|
- Temizlenmiş metni `<p>...</p>` tag'leri ile sarmalama
|
|||
|
|
- Paragraf yapısını koruma
|
|||
|
|
- Minimal HTML (sadece gerekli tag'ler)
|
|||
|
|
- [ ] **XML Entity Escaping** (Kritik!):
|
|||
|
|
- **Zorunlu karakter dönüşümleri**:
|
|||
|
|
- `&` → `&` (özellikle URL'lerde kritik!)
|
|||
|
|
- `<` → `<`
|
|||
|
|
- `>` → `>`
|
|||
|
|
- `"` → `"`
|
|||
|
|
- `'` → `'`
|
|||
|
|
- **CDATA kullanımı**: İsteğe bağlı ama entity escaping hala gerekli
|
|||
|
|
- URL'lerdeki ampersand'ların kaçışı özellikle önemli
|
|||
|
|
|
|||
|
|
**Algoritma Özeti:**
|
|||
|
|
1. Artifact'ları kaldır ve normalize et
|
|||
|
|
2. SpaCy ile fragment'ları birleştir ve cümle sınırlarını tespit et
|
|||
|
|
3. Paragraflara böl (anlamsal veya cümle sayısına göre)
|
|||
|
|
4. HTML tag'leri ekle (`<p>...</p>`)
|
|||
|
|
5. XML entity escaping uygula (özellikle `&` karakterleri)
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 4-5 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 3: Video Metadata Çıkarımı ve Yönetimi
|
|||
|
|
|
|||
|
|
### 3.1. Video Metadata Fetcher (`video_fetcher.py`) - RSS-Bridge Entegrasyonu
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] **RSS-Bridge Feed Parser**:
|
|||
|
|
- **Public RSS-Bridge instance kullanımı (Önerilen)**:
|
|||
|
|
- Base URL: `https://rss-bridge.org/bridge01/`
|
|||
|
|
- Ücretsiz ve hazır kullanılabilir
|
|||
|
|
- Rate limiting riski var ama başlangıç için yeterli
|
|||
|
|
- RSS-Bridge YouTube Bridge endpoint'ini kullanma
|
|||
|
|
- **Doğru URL formatı**:
|
|||
|
|
- Public (Channel ID): `https://rss-bridge.org/bridge01/?action=display&bridge=YoutubeBridge&context=By+channel+id&c=CHANNEL_ID&format=Atom`
|
|||
|
|
- Public (Channel Handle): `https://rss-bridge.org/bridge01/?action=display&bridge=YoutubeBridge&context=By+username&u=USERNAME&format=Atom`
|
|||
|
|
- Self-hosted (opsiyonel): `http://localhost:3000/?action=display&bridge=YoutubeBridge&context=By+channel+id&c=CHANNEL_ID&format=Atom`
|
|||
|
|
- **Önemli parametreler**:
|
|||
|
|
- `bridge=YoutubeBridge` (Youtube değil!)
|
|||
|
|
- `context=By+channel+id` veya `context=By+username`
|
|||
|
|
- `c=CHANNEL_ID` (channel ID için) veya `u=USERNAME` (handle için)
|
|||
|
|
- `format=Atom` veya `format=Rss` (feed için, Html değil)
|
|||
|
|
- `duration_min` ve `duration_max` (opsiyonel filtreleme)
|
|||
|
|
- [ ] **Feed Parsing** (`feedparser` kullanarak):
|
|||
|
|
- RSS/Atom feed'ini parse etme
|
|||
|
|
- Video entry'lerini çıkarma
|
|||
|
|
- Metadata çıkarımı:
|
|||
|
|
- Video ID (YouTube URL'den parse)
|
|||
|
|
- Video başlığı (`entry.title`)
|
|||
|
|
- Yayın tarihi (`entry.published` - timezone-aware)
|
|||
|
|
- Video URL (`entry.link`)
|
|||
|
|
- Video açıklaması (`entry.summary` - opsiyonel)
|
|||
|
|
- Thumbnail URL (opsiyonel)
|
|||
|
|
- [ ] **RSS-Bridge Avantajları**:
|
|||
|
|
- Native feed'den daha fazla video (10-15 yerine 100+)
|
|||
|
|
- Channel Handle (@username) desteği
|
|||
|
|
- Filtreleme seçenekleri (duration, vb.)
|
|||
|
|
- [ ] **Hata Yönetimi**:
|
|||
|
|
- RSS-Bridge instance erişilemezse fallback (native RSS)
|
|||
|
|
- Rate limiting handling
|
|||
|
|
- Feed format validation
|
|||
|
|
- [ ] **Channel ID Extraction (Handle'dan)**:
|
|||
|
|
- Channel handle (@username) verildiğinde Channel ID'yi bulma
|
|||
|
|
- Web scraping ile HTML source'dan Channel ID çıkarma
|
|||
|
|
- Regex pattern: `"externalId":"(UC[a-zA-Z0-9_-]{22})"` veya `"channelId":"(UC[a-zA-Z0-9_-]{22})"`
|
|||
|
|
- Channel ID formatı: `UC` ile başlar, 22 karakter
|
|||
|
|
- Fallback mekanizması: İlk pattern bulunamazsa alternatif pattern dene
|
|||
|
|
- Hata yönetimi: Request exception handling
|
|||
|
|
- [ ] **Video ID Extraction**:
|
|||
|
|
- YouTube URL'den video ID çıkarma
|
|||
|
|
- Regex pattern: `youtube.com/watch?v=([a-zA-Z0-9_-]+)`
|
|||
|
|
- Short URL desteği: `youtu.be/([a-zA-Z0-9_-]+)`
|
|||
|
|
- [ ] `yt-dlp` entegrasyonu (opsiyonel, gelişmiş metadata için)
|
|||
|
|
|
|||
|
|
**RSS-Bridge Kullanım Örneği:**
|
|||
|
|
```python
|
|||
|
|
import feedparser
|
|||
|
|
from urllib.parse import urlencode
|
|||
|
|
|
|||
|
|
# Public RSS-Bridge base URL
|
|||
|
|
RSS_BRIDGE_BASE = "https://rss-bridge.org/bridge01"
|
|||
|
|
|
|||
|
|
# Channel ID ile feed URL'i oluştur
|
|||
|
|
params = {
|
|||
|
|
'action': 'display',
|
|||
|
|
'bridge': 'YoutubeBridge',
|
|||
|
|
'context': 'By channel id',
|
|||
|
|
'c': 'UC9h8BDcXwkhZtnqoQJ7PggA', # Channel ID
|
|||
|
|
'format': 'Atom' # veya 'Rss'
|
|||
|
|
}
|
|||
|
|
rss_bridge_url = f"{RSS_BRIDGE_BASE}/?{urlencode(params)}"
|
|||
|
|
|
|||
|
|
# Feed'i parse et
|
|||
|
|
feed = feedparser.parse(rss_bridge_url)
|
|||
|
|
|
|||
|
|
# Video entry'lerini işle
|
|||
|
|
for entry in feed.entries:
|
|||
|
|
video_id = extract_video_id(entry.link)
|
|||
|
|
video_title = entry.title
|
|||
|
|
published_date = entry.published_parsed # timezone-aware
|
|||
|
|
video_url = entry.link
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Gerçek Örnek URL:**
|
|||
|
|
```
|
|||
|
|
https://rss-bridge.org/bridge01/?action=display&bridge=YoutubeBridge&context=By+channel+id&c=UC9h8BDcXwkhZtnqoQJ7PggA&format=Atom
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Channel ID Bulma Fonksiyonu (Handle'dan):**
|
|||
|
|
```python
|
|||
|
|
import requests
|
|||
|
|
import re
|
|||
|
|
|
|||
|
|
def get_channel_id_from_handle(handle_url):
|
|||
|
|
"""
|
|||
|
|
Channel handle URL'inden Channel ID'yi web scraping ile bulur.
|
|||
|
|
Örnek: https://www.youtube.com/@tavakfi -> UC...
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
# HTML içeriğini çek
|
|||
|
|
response = requests.get(handle_url)
|
|||
|
|
response.raise_for_status()
|
|||
|
|
|
|||
|
|
html_content = response.text
|
|||
|
|
|
|||
|
|
# İlk pattern: "externalId":"UC..."
|
|||
|
|
match = re.search(r'"externalId":"(UC[a-zA-Z0-9_-]{22})"', html_content)
|
|||
|
|
if match:
|
|||
|
|
return match.group(1)
|
|||
|
|
|
|||
|
|
# Alternatif pattern: "channelId":"UC..."
|
|||
|
|
match_alt = re.search(r'"channelId":"(UC[a-zA-Z0-9_-]{22})"', html_content)
|
|||
|
|
if match_alt:
|
|||
|
|
return match_alt.group(1)
|
|||
|
|
|
|||
|
|
return None # Channel ID bulunamadı
|
|||
|
|
|
|||
|
|
except requests.exceptions.RequestException as e:
|
|||
|
|
raise Exception(f"Error fetching channel page: {e}")
|
|||
|
|
|
|||
|
|
# Kullanım örneği
|
|||
|
|
handle_url = "https://www.youtube.com/@tavakfi"
|
|||
|
|
channel_id = get_channel_id_from_handle(handle_url)
|
|||
|
|
# Artık channel_id ile RSS-Bridge feed'ini çekebiliriz
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 3 gün
|
|||
|
|
|
|||
|
|
### 3.2. İşlenmiş Video Takibi (`database.py` - SQLite)
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] SQLite veritabanı modülü oluşturma (`database.py`)
|
|||
|
|
- [ ] Veritabanı şeması tasarımı:
|
|||
|
|
```sql
|
|||
|
|
-- Channels tablosu (kanal takibi için)
|
|||
|
|
CREATE TABLE IF NOT EXISTS channels (
|
|||
|
|
channel_id TEXT PRIMARY KEY, -- UC... formatında
|
|||
|
|
channel_name TEXT,
|
|||
|
|
channel_url TEXT,
|
|||
|
|
last_checked_utc TEXT, -- ISO 8601 UTC format
|
|||
|
|
created_at_utc TEXT DEFAULT (datetime('now'))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_channels_last_checked ON channels(last_checked_utc);
|
|||
|
|
|
|||
|
|
-- Videos tablosu (detaylı şema)
|
|||
|
|
CREATE TABLE IF NOT EXISTS videos (
|
|||
|
|
video_id TEXT PRIMARY KEY, -- 11 karakterli YouTube video ID
|
|||
|
|
channel_id TEXT, -- UC... formatında (FK)
|
|||
|
|
video_title TEXT,
|
|||
|
|
video_url TEXT,
|
|||
|
|
published_at_utc TEXT, -- ISO 8601 UTC format (YYYY-MM-DDTHH:MM:SSZ)
|
|||
|
|
processed_at_utc TEXT, -- ISO 8601 UTC format
|
|||
|
|
transcript_status INTEGER DEFAULT 0, -- 0: Beklemede, 1: Çıkarıldı, 2: Başarısız
|
|||
|
|
transcript_language TEXT,
|
|||
|
|
transcript_raw TEXT, -- Ham, bölümlenmemiş transcript
|
|||
|
|
transcript_clean TEXT, -- SBD ile işlenmiş, RSS için hazır HTML
|
|||
|
|
last_updated_utc TEXT DEFAULT (datetime('now'))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
-- Kritik index'ler (performans için)
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_videos_channel_id ON videos(channel_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_videos_published_at_utc ON videos(published_at_utc);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_videos_transcript_status ON videos(transcript_status);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_videos_processed_at_utc ON videos(processed_at_utc);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Önemli Notlar**:
|
|||
|
|
- **Zaman Formatı**: UTC ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) - SQLite'ın timezone desteği yok
|
|||
|
|
- **transcript_status**: INTEGER (0, 1, 2) - String değil, performans için
|
|||
|
|
- **Index Optimizasyonu**: `EXPLAIN QUERY PLAN` ile sorgu performansını doğrula
|
|||
|
|
- **DATE() fonksiyonu kullanma**: Index kullanımını engeller, direkt timestamp karşılaştırması yap
|
|||
|
|
- [ ] Database helper fonksiyonları:
|
|||
|
|
- `init_database()` - Veritabanı ve tablo oluşturma
|
|||
|
|
- `is_video_processed(video_id)` - Duplicate kontrolü
|
|||
|
|
- `get_pending_videos()` - `transcript_status = 0` olan videoları getir
|
|||
|
|
- `add_video(video_data)` - Yeni video kaydı (status=0 olarak)
|
|||
|
|
- `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)
|
|||
|
|
- **Query Performance**: `EXPLAIN QUERY PLAN` ile index kullanımını doğrula
|
|||
|
|
- [ ] 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
|
|||
|
|
- [ ] Transaction yönetimi (ACID compliance)
|
|||
|
|
- [ ] Connection pooling ve error handling
|
|||
|
|
|
|||
|
|
**Avantajlar:**
|
|||
|
|
- Daha hızlı sorgulama (index'li)
|
|||
|
|
- Transaction desteği
|
|||
|
|
- İlişkisel veri yapısı
|
|||
|
|
- Daha iyi veri bütünlüğü
|
|||
|
|
- İstatistik sorguları kolaylaşır
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 2 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 4: RSS Feed Oluşturma
|
|||
|
|
|
|||
|
|
### 4.1. RSS Generator (`rss_generator.py`)
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] `python-feedgen` ile FeedGenerator oluşturma
|
|||
|
|
- [ ] **Content Namespace Extension** ekleme:
|
|||
|
|
```xml
|
|||
|
|
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
|||
|
|
```
|
|||
|
|
- [ ] Channel metadata ayarlama:
|
|||
|
|
- `fg.id()` - Channel ID
|
|||
|
|
- `fg.title()` - Kanal başlığı
|
|||
|
|
- `fg.link()` - Kanal URL'i
|
|||
|
|
- `fg.language('tr')` - Dil
|
|||
|
|
- `fg.lastBuildDate()` - Son güncelleme tarihi
|
|||
|
|
- [ ] Item oluşturma:
|
|||
|
|
- `fe.id(video_id)` - GUID (YouTube Video ID)
|
|||
|
|
- `fe.title(video_title)` - Video başlığı
|
|||
|
|
- `fe.link(href=video_url)` - Video linki
|
|||
|
|
- `fe.published(datetime_with_timezone)` - Yayın tarihi (timezone-aware)
|
|||
|
|
- `fe.description(summary)` - Kısa özet
|
|||
|
|
- `fe.content(content=cleaned_transcript_html)` - Tam transcript (`<content:encoded>`)
|
|||
|
|
|
|||
|
|
**Kritik Gereksinimler:**
|
|||
|
|
- Timezone-aware tarih formatı (RFC 822 veya ISO 8601)
|
|||
|
|
- Video ID'nin GUID olarak kullanılması (immutable)
|
|||
|
|
- Full-text için `<content:encoded>` tag'i
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 2-3 gün
|
|||
|
|
|
|||
|
|
### 4.2. RSS Output ve Serialization
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] XML dosyası oluşturma:
|
|||
|
|
```python
|
|||
|
|
fg.rss_file('transcript_feed.xml', pretty=True, extensions=True)
|
|||
|
|
```
|
|||
|
|
- [ ] Pretty printing (okunabilirlik için)
|
|||
|
|
- [ ] Extensions desteği (Content Namespace dahil)
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 1 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 5: Ana Pipeline ve Otomasyon
|
|||
|
|
|
|||
|
|
### 5.1. Main Pipeline (`main.py`)
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] Tüm modüllerin entegrasyonu
|
|||
|
|
- [ ] İş akışı:
|
|||
|
|
1. SQLite veritabanını başlat (`init_database()`)
|
|||
|
|
2. Configuration'dan channel bilgisini oku:
|
|||
|
|
- Eğer handle (@username) verildiyse, `get_channel_id_from_handle()` ile Channel ID'ye çevir
|
|||
|
|
- Channel ID zaten varsa direkt kullan
|
|||
|
|
3. RSS-Bridge feed'den yeni videoları tespit et (`video_fetcher.py`)
|
|||
|
|
4. SQLite veritabanında `video_id` ile duplicate kontrolü yap
|
|||
|
|
5. Yeni videolar için:
|
|||
|
|
a. Transcript çıkar
|
|||
|
|
b. Transcript'i temizle
|
|||
|
|
c. RSS feed'e ekle
|
|||
|
|
d. SQLite'a kaydet (video metadata + işlenme durumu)
|
|||
|
|
6. RSS feed'i güncelle (veritabanından tüm işlenmiş videoları çek)
|
|||
|
|
7. XML dosyasını kaydet
|
|||
|
|
- [ ] Hata yönetimi ve logging
|
|||
|
|
- [ ] Configuration dosyası yükleme
|
|||
|
|
- [ ] Database transaction yönetimi (rollback on error)
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 2-3 gün
|
|||
|
|
|
|||
|
|
### 5.2. Configuration Management (`config.yaml`)
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] Yapılandırma dosyası oluşturma:
|
|||
|
|
```yaml
|
|||
|
|
channel:
|
|||
|
|
# Channel ID veya Handle (otomatik dönüştürülür)
|
|||
|
|
id: "UC9h8BDcXwkhZtnqoQJ7PggA" # Channel ID (UC ile başlar)
|
|||
|
|
# veya handle kullanılabilir:
|
|||
|
|
# handle: "@tavakfi" # Handle kullanılırsa otomatik olarak Channel ID'ye çevrilir
|
|||
|
|
# veya full URL:
|
|||
|
|
# handle_url: "https://www.youtube.com/@tavakfi"
|
|||
|
|
name: "Channel Name"
|
|||
|
|
url: "https://youtube.com/channel/..."
|
|||
|
|
language: "tr"
|
|||
|
|
|
|||
|
|
rss_bridge:
|
|||
|
|
# Public RSS-Bridge instance (varsayılan - önerilen)
|
|||
|
|
base_url: "https://rss-bridge.org/bridge01"
|
|||
|
|
# Self-hosted instance (opsiyonel - rate limiting sorunları için)
|
|||
|
|
# base_url: "http://localhost:3000"
|
|||
|
|
bridge_name: "YoutubeBridge" # Önemli: "YoutubeBridge" olmalı
|
|||
|
|
context: "By channel id" # veya "By username"
|
|||
|
|
format: "Atom" # veya "Rss" (feed için)
|
|||
|
|
max_items: 100 # RSS-Bridge'den çekilecek maksimum video sayısı
|
|||
|
|
use_fallback: true # RSS-Bridge erişilemezse native RSS kullan
|
|||
|
|
# Opsiyonel filtreleme
|
|||
|
|
duration_min: null # dakika cinsinden minimum süre
|
|||
|
|
duration_max: null # dakika cinsinden maksimum süre
|
|||
|
|
|
|||
|
|
transcript:
|
|||
|
|
languages: ["tr", "en"]
|
|||
|
|
enable_sbd: true
|
|||
|
|
paragraph_length: 3
|
|||
|
|
|
|||
|
|
rss:
|
|||
|
|
title: "Channel Transcript Feed"
|
|||
|
|
description: "Full-text transcript RSS feed"
|
|||
|
|
output_file: "transcript_feed.xml"
|
|||
|
|
|
|||
|
|
automation:
|
|||
|
|
check_interval_hours: 12
|
|||
|
|
max_items: 100
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 1 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 6: Test ve Validasyon
|
|||
|
|
|
|||
|
|
### 6.1. Unit Testler
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] Transcript extractor testleri
|
|||
|
|
- [ ] Transcript cleaner testleri (SBD, XML escaping)
|
|||
|
|
- [ ] RSS generator testleri
|
|||
|
|
- [ ] Video fetcher testleri:
|
|||
|
|
- RSS-Bridge feed parsing
|
|||
|
|
- Channel ID extraction (handle'dan):
|
|||
|
|
- Handle URL'den Channel ID çıkarma
|
|||
|
|
- Regex pattern matching testleri
|
|||
|
|
- Fallback pattern testleri
|
|||
|
|
- Hata durumları (geçersiz handle, network error)
|
|||
|
|
- Video ID extraction (URL'den)
|
|||
|
|
- Fallback mekanizması (RSS-Bridge erişilemezse)
|
|||
|
|
- Feed format validation (Atom/RSS)
|
|||
|
|
- [ ] Database modülü testleri:
|
|||
|
|
- Veritabanı oluşturma
|
|||
|
|
- Video ekleme/sorgulama
|
|||
|
|
- Duplicate kontrolü
|
|||
|
|
- Transaction rollback testleri
|
|||
|
|
- Test veritabanı kullanımı (in-memory SQLite)
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 2-3 gün
|
|||
|
|
|
|||
|
|
### 6.2. RSS Feed Validasyonu
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] W3C Feed Validation Service ile doğrulama
|
|||
|
|
- URL: https://validator.w3.org/feed/
|
|||
|
|
- [ ] Validasyon checklist:
|
|||
|
|
- [ ] XML entity escaping doğru mu?
|
|||
|
|
- [ ] Zorunlu RSS 2.0 tag'leri mevcut mu? (`<title>`, `<link>`, `<description>`)
|
|||
|
|
- [ ] Content Namespace doğru tanımlanmış mı?
|
|||
|
|
- [ ] Timezone-aware tarih formatı doğru mu?
|
|||
|
|
- [ ] GUID'ler unique ve immutable mi?
|
|||
|
|
- [ ] Farklı RSS reader'larda test (Feedly, Tiny Tiny RSS, vb.)
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 1-2 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 7: Deployment ve Hosting
|
|||
|
|
|
|||
|
|
### 7.1. Static Hosting Seçimi
|
|||
|
|
|
|||
|
|
**Seçenekler:**
|
|||
|
|
1. **GitHub Pages** (Önerilen)
|
|||
|
|
- Ücretsiz
|
|||
|
|
- CI/CD entegrasyonu
|
|||
|
|
- Version control
|
|||
|
|
2. **Static.app / StaticSave**
|
|||
|
|
- Ücretsiz tier mevcut
|
|||
|
|
- Dosya boyutu limitleri var
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] GitHub repository oluşturma
|
|||
|
|
- [ ] GitHub Actions workflow oluşturma (otomatik çalıştırma için)
|
|||
|
|
- [ ] MIME type ayarları (`application/rss+xml`)
|
|||
|
|
- [ ] Public URL oluşturma
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 1-2 gün
|
|||
|
|
|
|||
|
|
### 7.2. Otomasyon ve Scheduling
|
|||
|
|
|
|||
|
|
**Seçenekler:**
|
|||
|
|
1. **GitHub Actions** (Önerilen)
|
|||
|
|
- Cron job: Her 12-24 saatte bir
|
|||
|
|
- Ücretsiz tier yeterli
|
|||
|
|
2. **Cron Job** (VPS/Server)
|
|||
|
|
- Tam kontrol
|
|||
|
|
- Sunucu gereksinimi
|
|||
|
|
3. **Cloud Functions** (AWS Lambda, Google Cloud Functions)
|
|||
|
|
- Serverless
|
|||
|
|
- Kullanım bazlı maliyet
|
|||
|
|
|
|||
|
|
**GitHub Actions Workflow Örneği (Optimize Edilmiş):**
|
|||
|
|
```yaml
|
|||
|
|
name: Update RSS Feed
|
|||
|
|
on:
|
|||
|
|
schedule:
|
|||
|
|
- cron: '0 */12 * * *' # Her 12 saatte bir
|
|||
|
|
workflow_dispatch: # Manuel tetikleme
|
|||
|
|
|
|||
|
|
jobs:
|
|||
|
|
update-feed:
|
|||
|
|
runs-on: ubuntu-latest
|
|||
|
|
steps:
|
|||
|
|
# 1. Checkout (commit SHA ile sabitlenmiş - güvenlik)
|
|||
|
|
- uses: actions/checkout@8e5e7e5f366d5b8b75e3d67731b8b25a0a40a8a7 # v4 commit SHA
|
|||
|
|
|
|||
|
|
# 2. Python setup
|
|||
|
|
- uses: actions/setup-python@0a5d62f8d0679a54b4c1a51b3c9c0e0e8e8e8e8e # v5 commit SHA
|
|||
|
|
with:
|
|||
|
|
python-version: '3.10'
|
|||
|
|
|
|||
|
|
# 3. Cache veritabanı (Git commit yerine - performans)
|
|||
|
|
- name: Cache database
|
|||
|
|
uses: actions/cache@v3
|
|||
|
|
with:
|
|||
|
|
path: data/videos.db
|
|||
|
|
key: ${{ runner.os }}-videos-db-${{ hashFiles('data/videos.db') }}
|
|||
|
|
restore-keys: |
|
|||
|
|
${{ runner.os }}-videos-db-
|
|||
|
|
|
|||
|
|
# 4. Install dependencies
|
|||
|
|
- run: pip install -r requirements.txt
|
|||
|
|
- run: python -m spacy download en_core_web_sm
|
|||
|
|
|
|||
|
|
# 5. Run pipeline
|
|||
|
|
- run: python main.py
|
|||
|
|
|
|||
|
|
# 6. Save database to cache
|
|||
|
|
- name: Save database to cache
|
|||
|
|
uses: actions/cache@v3
|
|||
|
|
with:
|
|||
|
|
path: data/videos.db
|
|||
|
|
key: ${{ runner.os }}-videos-db-${{ hashFiles('data/videos.db') }}
|
|||
|
|
|
|||
|
|
# 7. Upload RSS feed as artifact
|
|||
|
|
- name: Upload RSS feed
|
|||
|
|
uses: actions/upload-artifact@v3
|
|||
|
|
with:
|
|||
|
|
name: transcript-feed
|
|||
|
|
path: output/transcript_feed.xml
|
|||
|
|
retention-days: 30
|
|||
|
|
|
|||
|
|
# 8. Deploy to GitHub Pages (opsiyonel)
|
|||
|
|
- name: Deploy to GitHub Pages
|
|||
|
|
uses: peaceiris/actions-gh-pages@v3
|
|||
|
|
with:
|
|||
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|||
|
|
publish_dir: ./output
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Önemli Notlar**:
|
|||
|
|
- **Cache Kullanımı**: Veritabanı için `actions/cache` kullan (Git commit yerine) - daha hızlı
|
|||
|
|
- **Action Pinning**: Tüm action'lar commit SHA ile sabitlenmiş (güvenlik)
|
|||
|
|
- **Artifact**: RSS feed'i artifact olarak sakla (GitHub Pages'e deploy edilebilir)
|
|||
|
|
- **SpaCy Model**: CI/CD'de model indirme adımı eklendi
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 1-2 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Faz 8: İleri Özellikler ve Optimizasyon
|
|||
|
|
|
|||
|
|
### 8.1. Performans Optimizasyonu
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] Paralel transcript çıkarımı (çoklu video için)
|
|||
|
|
- [ ] Caching mekanizması
|
|||
|
|
- [ ] Rate limiting yönetimi
|
|||
|
|
- [ ] Batch processing optimizasyonu
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 2-3 gün
|
|||
|
|
|
|||
|
|
### 8.2. Self-Hosted RSS-Bridge Deployment (Opsiyonel - Rate Limiting Sorunları İçin)
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] **Ne zaman self-hosting gerekir?**
|
|||
|
|
- Public instance rate limiting'e maruz kalırsa
|
|||
|
|
- Yüksek hacimli kullanım gerekiyorsa
|
|||
|
|
- Özelleştirilmiş bridge'ler (YoutubeEmbedBridge) gerekiyorsa
|
|||
|
|
- [ ] RSS-Bridge Docker deployment:
|
|||
|
|
```bash
|
|||
|
|
docker create --name=rss-bridge --publish 3000:80 \
|
|||
|
|
--volume $(pwd)/rss-bridge-config:/config \
|
|||
|
|
rssbridge/rss-bridge
|
|||
|
|
docker start rss-bridge
|
|||
|
|
```
|
|||
|
|
- [ ] RSS-Bridge konfigürasyonu:
|
|||
|
|
- `config.ini.php` ayarları
|
|||
|
|
- `CACHE_TIMEOUT` ayarlama (TTL kontrolü)
|
|||
|
|
- Custom bridge'ler ekleme (YoutubeEmbedBridge)
|
|||
|
|
- [ ] Self-hosting avantajları:
|
|||
|
|
- Rate limiting'den kaçınma (dedicated IP)
|
|||
|
|
- Daha fazla video çekebilme (100+)
|
|||
|
|
- Özelleştirilmiş bridge'ler
|
|||
|
|
- Gizlilik ve kontrol
|
|||
|
|
- [ ] Production deployment:
|
|||
|
|
- Reverse proxy (Nginx) kurulumu
|
|||
|
|
- SSL sertifikası (Let's Encrypt)
|
|||
|
|
- Monitoring ve health checks
|
|||
|
|
- [ ] YoutubeEmbedBridge entegrasyonu (ad-free playback):
|
|||
|
|
- `YoutubeEmbedBridge.php` dosyasını `/config/bridges/` klasörüne ekle
|
|||
|
|
- Container'ı restart et
|
|||
|
|
- Embed bridge'i test et
|
|||
|
|
|
|||
|
|
**Not**: Public RSS-Bridge (`https://rss-bridge.org/bridge01/`) varsayılan olarak kullanılır ve çoğu durumda yeterlidir. Self-hosting sadece rate limiting sorunları yaşandığında veya özel gereksinimler olduğunda önerilir.
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 2-3 gün (opsiyonel)
|
|||
|
|
|
|||
|
|
### 8.3. Monitoring ve Logging
|
|||
|
|
|
|||
|
|
**Görevler:**
|
|||
|
|
- [ ] Detaylı logging sistemi
|
|||
|
|
- [ ] Hata bildirimleri (email, webhook)
|
|||
|
|
- [ ] Feed health monitoring
|
|||
|
|
- [ ] İstatistikler (SQLite sorguları ile):
|
|||
|
|
- Toplam işlenen video sayısı
|
|||
|
|
- Başarı/başarısızlık oranları
|
|||
|
|
- Son işlenme tarihleri
|
|||
|
|
- Dil dağılımı
|
|||
|
|
- Günlük/haftalık istatistikler
|
|||
|
|
|
|||
|
|
**SQLite İstatistik Örnekleri:**
|
|||
|
|
```sql
|
|||
|
|
-- Toplam işlenen video sayısı
|
|||
|
|
SELECT COUNT(*) FROM processed_videos;
|
|||
|
|
|
|||
|
|
-- Başarı oranı
|
|||
|
|
SELECT
|
|||
|
|
transcript_status,
|
|||
|
|
COUNT(*) as count,
|
|||
|
|
ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM processed_videos), 2) as percentage
|
|||
|
|
FROM processed_videos
|
|||
|
|
GROUP BY transcript_status;
|
|||
|
|
|
|||
|
|
-- Son 7 günde işlenen videolar
|
|||
|
|
SELECT COUNT(*) FROM processed_videos
|
|||
|
|
WHERE processed_at >= datetime('now', '-7 days');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Süre Tahmini**: 2 gün
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Toplam Süre Tahmini
|
|||
|
|
|
|||
|
|
| Faz | Süre |
|
|||
|
|
|-----|------|
|
|||
|
|
| Faz 1: Altyapı | 1-2 gün |
|
|||
|
|
| Faz 2: Transcript Modülü | 7-9 gün |
|
|||
|
|
| Faz 3: Video Metadata | 6 gün |
|
|||
|
|
| Faz 4: RSS Generation | 3-4 gün |
|
|||
|
|
| Faz 5: Pipeline | 3-4 gün |
|
|||
|
|
| Faz 6: Test & Validasyon | 3-5 gün |
|
|||
|
|
| Faz 7: Deployment | 2-4 gün |
|
|||
|
|
| Faz 8: İleri Özellikler | 4-9 gün (opsiyonel) |
|
|||
|
|
| **TOPLAM (Temel)** | **25-35 gün** |
|
|||
|
|
| **TOPLAM (Tam)** | **29-44 gün** |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Kritik Başarı Faktörleri
|
|||
|
|
|
|||
|
|
1. **API Stabilitesi**: `youtube-transcript-api` kullanımı (scraping değil)
|
|||
|
|
2. **XML Compliance**: Content Namespace Extension ve entity escaping
|
|||
|
|
3. **Timezone Handling**: Tüm tarihler timezone-aware olmalı
|
|||
|
|
4. **Duplicate Prevention**: Video ID GUID olarak kullanılmalı
|
|||
|
|
5. **Efficient Processing**: Sadece yeni videolar işlenmeli
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Riskler ve Mitigasyon
|
|||
|
|
|
|||
|
|
| Risk | Etki | Mitigasyon |
|
|||
|
|
|------|------|------------|
|
|||
|
|
| YouTube API değişikliği | Yüksek | `youtube-transcript-api` güncellemelerini takip et |
|
|||
|
|
| Transcript bulunamama | Orta | Fallback diller, hata yönetimi |
|
|||
|
|
| Rate limiting | Orta | Exponential backoff, request throttling |
|
|||
|
|
| XML validation hataları | Yüksek | Comprehensive testing, W3C validation |
|
|||
|
|
| Hosting maliyeti | Düşük | GitHub Pages (ücretsiz) kullan |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Sonraki Adımlar
|
|||
|
|
|
|||
|
|
1. **Hemen Başla**: Faz 1 - Proje altyapısı kurulumu
|
|||
|
|
2. **MVP Hedefi**: Faz 1-6 tamamlanarak çalışan bir sistem
|
|||
|
|
3. **Production Ready**: Faz 7 deployment ile canlıya alma
|
|||
|
|
4. **Optimizasyon**: Faz 8 ile gelişmiş özellikler
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Referanslar ve Kaynaklar
|
|||
|
|
|
|||
|
|
- `youtube-transcript-api`: https://github.com/jdepoix/youtube-transcript-api
|
|||
|
|
- `python-feedgen`: https://github.com/lkiesow/python-feedgen
|
|||
|
|
- RSS 2.0 Specification: https://www.rssboard.org/rss-specification
|
|||
|
|
- Content Namespace: http://purl.org/rss/1.0/modules/content/
|
|||
|
|
- W3C Feed Validator: https://validator.w3.org/feed/
|
|||
|
|
- RSS-Bridge: https://github.com/RSS-Bridge/rss-bridge
|
|||
|
|
|