Proxy IP'lerini havuza dağıttığımız aynı sorun cookie/hesap seviyesinde de
vardı: tek bir Google hesabı 7-8 kanalı taramaya çalışınca YouTube hesabın
kendisini şüpheli bulup çoğu/tüm kanalda "Sign in to confirm you're not a
bot" veriyordu — proxy IP'den bağımsız bir sinyal, IP dağıtımı bunu
çözemez.
Yeni CookieProfile tablosu: her kanal, DB id'sinden aynı deterministik hash
ile (proxyUrlForChannel'daki gibi) havuzdaki hesaplardan birine sabitlenir.
Her profil kendi ayrı geçici dosyasına yazılıyor (/tmp/yt-cookies-<id>.txt)
— tek paylaşılan dosya, farklı profil kullanan kanallar eşzamanlı
çalışınca birbirinin cookie'sini ezerdi. Hiç profil eklenmemişse eski tekli
ytdlp_cookies ayarına geri düşüyor (geriye dönük uyumlu, hemen kırılmaz).
Ayarlar sayfasında yeni "Cookie Hesapları" kartı: hesap ekle/sil/yenile,
her biri için ayrı bayatlık rozeti. Eski tekli cookie kartı "tekli/eski"
etiketiyle fallback olarak duruyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- auto_render_enabled (varsayılan açık): kapatılınca transkript sonrası
otomatik render tetiklenmez, aday TRANSCRIBED'de bekler — panelin zaten
var olan "9:16 Render Et" butonuyla elle tetiklenebilir.
- delete_after_telegram_send (varsayılan KAPALI — geri alınamaz bir
davranış, bilinçli açılmalı): açıksa render biten video Telegram'a
gerçek dosya olarak gönderilir (yeni send_telegram_video, sendVideo
multipart upload, ~50MB bot-upload limiti önceden kontrol ediliyor);
gönderim başarılıysa dosya diskten silinip ShortVideo.file_path NULL'a
çekiliyor (DB kaydı/transkript kalıcı kalıyor), başarısızsa dosya
sunucuda kalıp düz metin bildirimi gidiyor. Kanal sayısı arttıkça VPS
disk doluluğunu önlemek için.
- Panel: READY + file_path=null durumunda video/indir yerine "Telegram'a
gönderildi, sunucuda saklanmıyor" notu (channels/[id], segments).
Şema değişikliği yok — ShortVideo.filePath zaten nullable.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@liberotv'de segment indirirken sürekli "403 Forbidden / Failed to open
segment" hatası vardı — kendi ayrılmış proxy IP'sinde bile. yt-dlp'nin
GitHub issue'larında android client'ının videoplayback isteklerinde 403'e
sebep olduğu birden fazla kez raporlanmış. Tespit adımı (--simulate, format
çözümlemesi yok) risksiz olduğu için web,mweb,android'i koruyor; asıl
capture artık sadece web,mweb kullanıyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dashboard'dan doğrulandı: isp.oxylabs.io:8001-8010 her biri farklı, statik
(rotasyon yapmayan) bir IP'ye karşılık geliyor. Ama PROXY_URL'in portu hep
sabitti — 7-8 kanalın tüm poll+capture trafiği tek bir IP'den (8001)
geçiyordu. Artık her kanal, DB id'sinden deterministik bir hash ile
havuzdaki 10 porttan birine sabitleniyor (aynı kanal hep aynı IP'yi
kullanıyor, farklı kanallar havuza yayılıyor) — hem YouTube'un tek IP
üzerindeki yük algısını azaltıyor hem de bant genişliğini paylaştırıyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Çöküp yeniden başlayan bir kanal (bkz. streamIngest'teki crash-loop notu)
dakikalar içinde art arda "başladı" + "hata" + "düzeldi" mesajı atıyordu —
birkaç kanal aynı anda flap edince gerçek bir spam'e dönüştü (kullanıcı
raporu: "sürekli bildirim geliyor"). Üç bildirim türü de artık kanal
başına ortak 10dk'lık bir soğuma süresini paylaşıyor — bir kanal ilk
olayını bildirir, sonra durum tekrar tekrar sıçrasa bile bir süre sessiz
kalır.
Not: bu sadece bildirim gürültüsünü kesiyor, alttaki crash-loop'un kendisi
(muhtemelen kaynak/proxy kapasitesi) hâlâ duruyor — o konu ayrı, kullanıcı
ile "önce test edelim" üzerinde anlaşıldı.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pollOnce her döngüde AKTİF kanalların hepsini (zaten kayıtta olanlar dahil)
yt-dlp'nin bot-check riskli /live sorgusuna sokuyordu — gereksiz. Birden
fazla kanal aynı anda canlıyken bu, aynı paylaşılan cookie+proxy üzerinden
gereksiz istek hacmi katlıyordu (muhtemelen 4-5 kanal eklenince birkaç
kanalın aynı anda tespit hatası vermeye başlamasının sebeplerinden biri).
Artık zaten açık bir StreamSession'ı olan kanal tamamen atlanıyor, kalan
kanallar arasına da 2sn gecikme eklendi (art arda patlama yerine seyrek).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dockerfile'ın CMD'si next dev kullanıyordu — Coolify'de canlı sistemde
dev-mode overlay/bildirimleri (Next.js sürüm uyarısı, hata detayları) ve
optimize edilmemiş, JIT-compile edilen bir build çalışıyordu. Artık image
build sırasında next build çalışıyor, CMD sadece next start.
DATABASE_URL build adımında gerçek bir DB'ye ihtiyaç duymuyor (tüm
sayfalar force-dynamic, build sırasında sorgu atılmıyor) ama Prisma
client import'ta connection string formatını doğruluyor — yer tutucu bir
değer verildi, Coolify'nin gerçek DATABASE_URL'i runtime'da onun üzerine
yazıyor zaten.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3 seviyeli border-left girinti (oturum > segment > aday klip) yerine
gerçek kutu (soft-card) blokları — her segment kendi arka planıyla
ayrışıyor, adaylar onun içinde daha da net bir alt kutuda. Segment
başlığı artık ham dosya yolu değil ("15sn segment · 02.09.2026 20:14"
gibi) insan-okur bir etiket — dosya yolu küçük/soluk referans olarak
altta kaldı. "Canlı Durum" kartındaki karışık bölümler de (hata/canlı
önizleme/log/segment ayarı/manuel başlat) Separator ile ayrıştırıldı.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Global SEGMENT_TIME_SEC varsayılanı 900->300 (docker-compose, .env.example,
env.ts, signal_detection.py fallback'i) — kanal bazlı override zaten
panelden ayarlanabiliyordu, bu sadece varsayılanı değiştiriyor.
Dashboard'daki kanal listesi tablo yerine kart-grid: her kanal kendi
kartında, durum rozetleri üstte belirgin, aksiyonlar altta — önceki
tablo düzeni (özellikle aksiyon sütunundaki sıkışık butonlar) okunması
zor bulunuyordu.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
UI yenilemesinde segments/page.tsx'teki "aday klip bulunamadı" boş-durum
mesajı kanal detay sayfasına taşınmamıştı — sinyal analizi bir segmentte
ilginç an bulamayınca panelde sessizce hiçbir şey görünmüyordu, bozukmuş
gibi duruyordu.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Poll hatası şu ana kadar sadece panele girip bakınca görülüyordu — proaktif
haberdar olma yolu yoktu. Şimdi bir kanal hata vermeye BAŞLADIĞINDA (her
60sn'de tekrar değil, sadece durum değişince) ve düzeldiğinde Telegram
bildirimi gidiyor. Ayarlar'dan kapatılabilir (notify_poll_error).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.toLocaleString("tr-TR") tarih formatını Türkçe yapıyor ama saat değerini
hâlâ sunucunun sistem saat dilimiyle (Coolify/Hetzner container'ı UTC)
hesaplıyordu. Tüm tarih gösterimleri artık ortak formatTr() helper'ı
üzerinden timeZone: "Europe/Istanbul" ile sabitleniyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Channel.segmentTimeSec (nullable, null = global SEGMENT_TIME_SEC env
var'ı kullan) eklendi. Kanal detay sayfasından dakika cinsinden
girilebiliyor, StreamIngestJob'a taşınıp streamIngest.ts'te ffmpeg'in
-segment_time argümanına yansıyor. Sadece o kanal için bundan sonra
başlayacak yeni kayıt oturumlarını etkiler — halihazırda çalışan bir
ffmpeg process'inin segment süresi zaten sabitlenmiş durumda.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Radix Switch'in kendi gizli-input form entegrasyonu bu ortamda Server
Action'ın FormData'sına güvenilir şekilde ulaşmıyordu — switch'i "off"
yapıp Kaydet'e basınca eski değer kalıcı oluyordu (STT hep otomatik
çalışmaya devam ediyordu). Artık her switch kendi değerini doğrudan
kontrol eden bir hidden input'la (SettingSwitch) submit ediliyor,
Radix'in iç mekanizmasına bağımlı değil.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- lastPollErrors artık {message, at} tutuyor — panelde hangi hatanın ne
zaman oluştuğu görünmüyordu, eski/güncel ayrımı yapılamıyordu.
- "This live event will begin in N minutes" gibi zararsız mesajlar da
hata rozetine düşüyordu, "is not currently live" ile aynı gruba alındı.
- Yeni stt_enabled ayarı (Ayarlar sayfası): kapatılınca stt_scoring.py
OpenAI Whisper çağrısı yapmadan adayı PENDING_STT'de bırakıp çıkıyor —
konuşmasız/ambiyans yayınlarda (bkz. NASA testi) boşa API maliyeti
önleniyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Elle yazılmış tek-dosya CSS (sabit koyu tema, ham table/button, window.confirm
ile silme onayı) yerine tutarlı bir bileşen sistemi: Card/Table/Badge/Button/
Input/Switch/AlertDialog primitifleri, Hanken Grotesk + JetBrains Mono
tipografi çifti, "broadcast/monitör" temalı özel bir renk paleti (camgöbeği
accent, ok/warn/err/live durum rengi sözlüğü). Tüm sayfalar (dashboard, kanal
detay, segmentler, ayarlar, istatistik, kullanıcılar, login) aynı oturumda
taşındı — silme onayları artık gerçek AlertDialog, native confirm() değil.
Veri akışı/server action'lar değişmedi, tamamen görsel katman.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Faz 2'de ShortVideo eklenirken deleteRawSegment/deleteCandidateSegment/
deleteChannel güncellenmemiş — candidate_id FK'sı yüzünden bir short'u
olan candidate silinmeye çalışılınca Postgres reddediyordu. Şimdi her
üç yol da candidate'leri silmeden önce ilişkili ShortVideo satırlarını
(+ render edilmiş dosyalarını) temizliyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Yayın durdurma, kanal duraklat/devam (tekli+toplu), şimdi kontrol et,
manuel URL ile zorla kayıt başlatma, canlı player+log önizleme, oturum
süre sayacı, başarısız/takılı render için tekrar dene ve iptal, poll
aralığı + ham segment TTL'yi panelden canlı değiştirme, cookie bayatlık
uyarısı, Telegram bildirim aç/kapa, ve kullanım/maliyet özeti (/stats).
api-daemon'ın yeni state-değiştiren endpoint'leri (stop/force-start/
render/cancel) INTERNAL_API_TOKEN ile korunuyor — bu daemon'ın portu
Coolify'de public'e açık olduğu için korumasız bırakmak güvenlik açığı
olurdu.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Her aday klip transkribe olduktan hemen sonra otomatik olarak
video-render kuyruğuna giriyor. Yeni Python worker (video_render.py):
- Klibi ffmpeg ile keser
- OpenCV + MediaPipe Face Detector (Tasks API — yeni mediapipe
sürümlerinde eski solutions API'si kalktı, .tflite model dosyası
Docker build'de indiriliyor) ile örneklenmiş karelerde yüz merkezi
tespit eder, yumuşatır (moving average); hiç yüz bulunamazsa sabit
merkez crop'a düşer, hata saymaz
- Kırpılmış kareleri OpenCV'den ffmpeg'e pipe'layıp tek geçişte orijinal
sesi map'ler ve transcript_json'dan ürettiği .ass altyazıyı yakar
- Sonucu /shared-media/shorts/<candidate_id>.mp4'e yazar
Yeni ShortVideo modeli (PENDING/RENDERING/READY/FAILED). Panelde
(segments + kanal detay sayfaları) render durumu + READY olunca video
önizleme/indirme. VIDEO_RENDER_CONCURRENCY ayrı ve düşük (varsayılan 2)
— bu iş diğerlerinden çok daha ağır.
Kapsam: sadece tekil konuşmacı şablonu (çoklu konuşmacı/split-screen
sonraki bir iterasyona bırakıldı — aktif-konuşmacı tespiti gerektiriyor).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/users: kullanıcı listesi, ekleme formu, silme (kendi hesabını veya
son kalan tek kullanıcıyı silmeye izin verilmiyor). Nav'a link eklendi.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PRD'nin auto-purge lifecycle'ı: RawSegment analiz tamamlanmış
(PROCESSED/DISCARDED) ve hiçbir candidate'i PENDING_STT'de değilse
(yani hâlâ transkribe edilmeyi bekleyen yoksa) 24 saatten (yapılandırı-
labilir: RAW_SEGMENT_TTL_HOURS) eskiyse DB kaydı + dosyası siliniyor.
Saatlik kontrol ediliyor, api-daemon başlarken de bir kere çalışıyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NODE_ENV=production'da cookie hep secure:true oluyordu, ama panel
şu an SSL'siz sslip.io preview domain'inde (http://) çalışıyor —
tarayıcı Secure cookie'yi HTTP origin'de hiç saklamıyor. Giriş
"başarılı" gibi görünüp anında /login'e geri dönülüyordu. Artık
gerçek istek protokolüne (Traefik'in x-forwarded-proto header'ı)
bakılıyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Panel şu ana kadar herkese açıktı — URL'yi bilen herkes kanal
ekleyip/silebilir, cookie güncelleyebilir, kayıt indirebilirdi.
- users tablosu (bcrypt şifre hash'i)
- /login: hiç kullanıcı yoksa "ilk admin hesabı oluştur" formu, varsa
normal giriş formu
- jose ile imzalanmış, httpOnly çerezde tutulan 30 günlük oturum
- middleware.ts: /login hariç tüm rotaları korur (video stream route'u
dahil — aynı origin istekleri çerezi otomatik taşır)
- layout: oturum yoksa nav hiç gösterilmiyor, varsa kullanıcı adı +
çıkış butonu ekleniyor
SESSION_SECRET production'da zorunlu (docker-compose derleme zamanında
kontrol ediyor); Coolify'a rastgele bir değer eklendi.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Kanal detay sayfasına "Kanalı Sil" eklendi — kanal + tüm session/
segment/candidate geçmişini (dosyalar dahil) siler, ana sayfaya
yönlendirir. Yanlışlıkla/test amaçlı eklenen kanalları temizlemek için.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
yt-dlp bunu da non-zero exit code ile bildiriyor, bu yüzden panelde
her kontrolde (kanal gerçekten sadece canlı değilken bile) kırmızı
"hata" rozeti görünüyordu — yanıltıcıydı. "is not currently live"
mesajı artık gerçek bir hata olarak işlenmiyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Regex tabanlı HTML çıkarımı IP'den bağımsız olarak tutarsız çıktı:
YouTube bazen player verisini (videoDetails) sunucu tarafında hiç
göndermiyor. yt-dlp'nin kendi extractor'ı engellenmediği sürece her
zaman doğru çözüyor, o yüzden kontrol tekrar yt-dlp'ye taşındı — bu
sefer üç katmanı birlikte kullanarak: cookie (oturum), pot-provider
(PO-token) ve PROXY_URL (temiz residential/ISP IP — Coolify
sunucusunun kendi IP'si bir günlük yoğun testten sonra işaretlendi).
Oxylabs ISP Proxies ile doğrulandı (gerçek ISP: CenturyLink), datacenter
proxy'lerin aksine.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
videoDetails yok ama sayfa consent duvarına da takılmıyor (büyük,
tam HTML dönüyor) — YouTube oynatıcı verisini bilerek vermiyor gibi
görünüyor. playabilityStatus.status/.reason genelde tam olarak
nedenini açıklıyor (LOGIN_REQUIRED, UNPLAYABLE, ERROR vb.).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cookie header'ı eklemek beklenen sonucu vermedi — @ertemsener kanalı
gerçekten canlıydı ama sistem hâlâ "değil" diyordu. Kör tahminle
devam etmek yerine, her kontrolde (sonuç ne olursa olsun) final URL,
HTML uzunluğu, cookie gönderilip gönderilmediği, videoDetails bulunup
bulunmadığı ve consent formu varlığı GET /status'ta lastPollDebug
alanında görünür kılındı.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Coolify sunucusu AB'de; oturumsuz istek YouTube'un interaktif consent
sayfasına düşüyor (o sayfada videoDetails yok), bu da gerçekten canlı
bir kanalı "değil" gibi gösteriyordu (doğrulandı: @ertemsener canlıydı,
sistem "değil" dedi). yt-dlp için zaten sakladığımız oturum açık
cookie'leri aynı fetch'e Cookie header'ı olarak eklendi — giriş yapılmış
hesaplar consent duvarını hiç görmüyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Proxy'ye gerek kalmadan bir çözüm: bugün boyunca düz HTTP fetch hiç
engellenmedi, sadece yt-dlp çağrıları bot-check/PO-token duvarına
takıldı. Önceki naive regex sorunuysa "videoId" alanının sayfada
onlarca kez (kanal video listesi, öneriler) geçmesiydi. Çözüm:
YouTube'un videoDetails objesi — sayfa gerçekten canlıysa tek ve
biricik olarak videoId/isLive/channelId'yi birlikte içeriyor (canlı
değilse obje hiç yok, doğrulandı). channelId çapraz kontrolüyle
birlikte hem doğru video hem yt-dlp'ye hiç dokunmadan (dolayısıyla
bot-check'e hiç maruz kalmadan) tespit yapılıyor. yt-dlp + cookie +
pot-provider artık sadece gerçek capture adımında kullanılıyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cookie'ler saatler içinde eskiyordu, her seferinde manuel Coolify env
güncellemesi + redeploy gerekiyordu. Artık DB'de (app_settings tablosu)
tutuluyor; /settings sayfasından yeni cookies.txt yapıştırılıp
kaydedilebiliyor, bir sonraki yt-dlp çağrısında redeploy gerekmeden
devreye giriyor. YTDLP_COOKIES_B64 env var mekanizması kaldırıldı.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bir redeploy, devam eden capture'ı temizlik kodu hiç çalışmadan
öldürüyordu — StreamSession.endedAt sonsuza dek null kalıyordu. Bu
sadece panelde yanlış "kayıtta" göstermekle kalmıyor, startSession()
açık bir session gördüğünde o kanal için yeni session başlatmayı
tamamen atlıyor — yani o kanal bir daha asla kayda alınamıyordu.
api-daemon başlarken artık tüm açık session'ları kapatıyor (taze bir
process başlangıcı = gerçekte devam eden capture yok demektir).
BullMQ lockDuration da 24 saatten 10 dakikaya indirildi — worker
canlıyken kilit otomatik yenileniyor, düşük değer sadece ölü bir
worker'ın job'ının ne kadar hızlı kurtarılacağını etkiliyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Kullanıcı panelindeki kayıtların kendi yayınıyla alakasız olduğunu
bildirdi. Sebep: channel /live sayfası onlarca alakasız videoId
içeriyor (kanal video listesi, öneriler); düz regex sayfadaki İLK
videoId'yi alıyordu, bu da rastgele başka bir videoydu — isLive:true
kontrolü de aynı bağlamda değildi. yt-dlp'nin extractor'ı kanalın asıl
o anki canlı videosunu doğru çözüyor; -f (format) verilmediği için
PO-token duvarını da tetiklemiyor (format çözümleme sadece gerçek
capture adımında, -f ile, gerekiyor).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- streamIngest worker concurrency 3'ten sabit değildi, artık
MAX_CONCURRENT_CAPTURES (varsayılan 10) ile ayarlanabiliyor. Eskiden
3'ten fazla kanal aynı anda canlıya geçerse fazlası, ilk 3'ten biri
bitene kadar (saatlerce) hiç kayda alınmıyordu.
- Python worker'larda (signal-detection, stt-scoring) bullmq varsayılan
concurrency'si 1'di — WORKER_CONCURRENCY (varsayılan 5) ile
paralelleştirildi, birden fazla kanal aynı anda segment/transkript
üretirse sıraya takılmasın diye.
- Frontend artık shared-media volume'üne bağlı: /api/segments/[id]/video
route'u Range destekli video stream/indirme sağlıyor. Segment ve
kanal detay sayfalarına <video> oynatıcı, indirme linki ve onaylı
silme (RawSegment + CandidateSegment, dosya dahil) eklendi.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Gerçek capture denemesinde debug endpoint'i asıl nedeni gösterdi:
"n challenge solving failed" — YouTube'un imza çözme JS challenge'ı
için yt-dlp'nin bir JS runtime'ı yoktu, bu da formatların eksik
kalıp "page needs to be reloaded" ile sonuçlanmasına yol açıyordu.
node:22-slim zaten Node 22 içeriyor (minimum gereksinim); yt-dlp'yi
"yt-dlp[default]" (EJS solver dahil) olarak kurup capture çağrısına
--js-runtimes node eklendi. Lokal olarak doğrulandı: aynı video artık
formatları hatasız çözüyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
İlk gerçek capture denemesi 2 saniyede sessionı bitirdi (0 segment) —
Coolify /logs endpoint'i api-daemon'un stdout'unu güvenilir şekilde
izole edemediği için sebebi göremedik. Artık her session'ın son
yt-dlp/ffmpeg stderr satırları bellekte tutulup HTTP'den okunabiliyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/channels/[id] rotası eklendi: dashboard'daki kanal adı artık oraya
link veriyor. Sayfa api-daemon'un /status'undan canlı durum ve son
poll hatasını (lastPollError, DB'de değil sadece bellekte tutuluyordu)
çekiyor, Prisma'dan da o kanala ait tüm StreamSession/RawSegment/
CandidateSegment geçmişini transkript önizlemesiyle listeliyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PO-token duvarı polling'de hiç gerekli değildi — sadece stream byte'ı
indirirken (capture) lazım. yt-dlp --simulate bile format çözümlemeye
çalıştığı için PO-token'a takılıyordu. Artık /channel/<id>/live
sayfasını düz fetch ile çekip ytInitialData içindeki isLive/videoId'yi
regex ile okuyoruz — bot-check/cookie/PO-token'a hiç maruz kalmıyor.
yt-dlp + cookie + pot-provider sadece gerçek capture adımında kalıyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cookie bot-check'i çözdü ama "The page needs to be reloaded" hatası
kalıcıydı — YouTube artık proof-of-origin token da istiyor. Coolify
stack'ine brainicism/bgutil-ytdlp-pot-provider sidecar servisi
(sc_pot_provider, port 4416) eklendi, api-daemon imajına Python
plugin'i kuruldu, her yt-dlp çağrısına
--extractor-args youtubepot-bgutilhttp:base_url=... eklendi.
ytdlpCookieArgs -> ytdlpAntiBotArgs olarak birleştirildi.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cookie tek başına bot-check'i çözüyordu ama android client ile
birlikte kullanılınca "No video formats found" hatası sürekli
tekrarladı — android client + cookie kombinasyonu tutarsız bir
manifest döndürüyor gibi. android client'sız, sadece cookie +
varsayılan web client + bestvideo+bestaudio/best format seçiciyle
aynı video id lokal olarak sorunsuz çözüldü.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cookie bot-check'i çözdü ama yeni bir hata çıktı: "No video formats
found" / "Requested format is not available". AyrisTech'in canlı
yayını dikey (1080x1920) ve HLS formatları video-only/audio-only
ayrı geliyor — tek parça "best" format hiç yok. Hem live-check hem
capture çağrısını "bestvideo+bestaudio/best" seçicisine çevirdik;
lokal olarak doğrulandı (yt-dlp video id'yi doğru döndürüyor).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
android player_client YouTube'un "Sign in to confirm you're not a bot"
duvarını aşmadı — Coolify sunucusunun IP'si datacenter olarak
işaretlenmiş. Kimlik doğrulanmış bir tarayıcı oturumunun cookies.txt'i
YTDLP_COOKIES_B64 env değişkeni (Coolify'da ayarlandı, repo'da yok)
üzerinden container'a taşınıp /tmp'e yazılıyor ve her yt-dlp
çağrısına --cookies olarak veriliyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AyrisTech kanalı gerçekten canlıydı ama Coolify sunucusunun IP'si
YouTube'un "Sign in to confirm you're not a bot" duvarına takıldı
(bu ortamdan aynı komut sorunsuz çalıştı — IP itibarına bağlı).
Hem live-check hem capture çağrısına --extractor-args
"youtube:player_client=android" eklendi; bu client genelde aynı
PO-token doğrulamasına tabi değil.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
yt-dlp hem "kanal canlı değil" hem de gerçek bir hata (ağ engeli,
bot tespiti, extractor kırılması) durumunda non-zero exit code
veriyor; ikisi ayırt edilemiyordu. Son hata mesajı artık kanal
başına saklanıp GET /status içinde lastPollError alanında dönüyor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dashboard'a name/youtubeHandle/channelId alanlarıyla bir server action
formu eklendi. Polling ve /status için channel_id gerçek bir YouTube
UC... kimliği olmalı.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Postgres canlıydı ama şema hiç uygulanmamıştı (migrations/ klasörü boştu).
Bu ortamdan Coolify'ın Postgres'ine ağ erişimi yok (5432 filtreli), bu yüzden
`prisma migrate dev` yerine offline schema diff (`migrate diff --from-empty`)
ile ilk migration SQL'i üretildi. api-daemon container'ı artık başlarken
`prisma migrate deploy` çalıştırıyor. Tüm servislere restart: unless-stopped
eklendi ki Postgres henüz hazır değilken başlayan container kendini toparlasın.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>