Teknik Analiz Raporu
player-core.js · performance-debug.js · buffer-monitor.js · 19 Şubat 2026
| # | Değişiklik | Bulgu | Durum |
|---|---|---|---|
| 1 | enableWorker: true | Satır 309'da enableWorker: true mevcut. HLS_SHARED_CONFIG ilk property olarak tanımlı. |
✅ DOĞRU |
| 2 | ontimeupdate throttle 250ms | 4 ayrı değişkene ayrıldı: _lastTimeupdateMain, _lastTimeupdateCrossfade, _lastTimeupdateSpot, _lastTimeupdatePreload. |
✅ ÇÖZÜLDÜ |
| 3 | Progress interval 100ms → 250ms | Satır 2319, 5814, 5840'da , 250) ile biten interval'ler mevcut. Yorum da var: "🚀 100ms → 250ms". |
✅ DOĞRU |
| 4 | Blob URL variant absolutization | Satır 209–220'de // 3.5. Variant playlist URL'lerini absolute yap bloğu mevcut. Double query string koruması: trimmed.includes('?') ? baseUrl + trimmed : baseUrl + trimmed + queryString ile sağlanıyor. |
✅ DOĞRU |
| 5 | Network offline/online recovery | Tamamlandı: _networkOffline = !navigator.onLine başlangıcı eklendi, online'da hls.startLoad() çağrılıyor, guard aktif. |
✅ ÇÖZÜLDÜ |
| 6 | Cihaz/bağlantı bilgisi debug paneli | performance-debug.js satır 345–354'te device: { ram, cores, effectiveType, downlink, rtt, saveData, isOldDevice, isSlowConnection } tamamen mevcut. |
✅ DOĞRU |
_lastTimeupdateProcess — 6 Handler Birbirini Bloke Ediyor_lastTimeupdateMain, _lastTimeupdateCrossfade, _lastTimeupdateSpot, _lastTimeupdatePreload
Ne oluyor?
Spot audio, ana audio, preloaded audio ve Safari path'lerinin hepsi self._lastTimeupdateProcess adlı tek değişkeni paylaşıyor. Bir handler'ın 250ms throttle'ı diğer tüm handler'ları otomatik olarak susturuyor.
16 saatte etkisi?
Spot reklam çalarken ana oynatıcının crossfade/preload tetikleme mantığı tamamen askıya alınabilir. Spot bittikten sonra ana şarkıya geçişte uzun sessizlik yaşanabilir.
Düzeltme önerisi
Her handler kendi özel değişkenini kullanmalı:
// Spot audio handler'ı const _now = performance.now(); if (_now - (self._lastTimeupdateSpot || 0) < 250) return; self._lastTimeupdateSpot = _now; // Ana audio handler'ı const _now = performance.now(); if (_now - (self._lastTimeupdateMain || 0) < 250) return; self._lastTimeupdateMain = _now;
hls.startLoad() eklendi. _networkOffline = !navigator.onLine ile başlangıç değeri de düzeltildi.
Ne oluyor?
İnternet kesilip geldiğinde sadece checkAndRefillQueue() çağrılıyor. HLS.js fragment loader'ı offline süresinde fatal error'a düşmüşse veya stopLoad() haline gelmişse, startLoad() hiç çağrılmıyor.
16 saatte etkisi?
Kafe Wi-Fi'ı kesilip geri geldiğinde müzik sessiz kalır. Müşteri fark etmeden saat boyu sessizlik yaşanabilir. Uygulamayı yeniden başlatmak gerekir.
Düzeltme önerisi
window.addEventListener('online', () => {
this._networkOffline = false;
// HLS.js yeniden başlat
if (this.hls && !this.isPlaying) {
try {
this.hls.startLoad();
} catch(e) {}
}
// Token yenile + queue doldur
if (this.currentSong) {
this.refreshHlsStream?.();
}
setTimeout(() => this.checkAndRefillQueue(), 1000);
});
Ne oluyor?
Ana oynatıcı createHlsBlobUrl() kullanarak URL gizlerken, preloadNextSong() doğrudan stream_url kullanıyor. Token süresi dolduğunda preload sessizce başarısız olup gapless geçişi bozar.
16 saatte etkisi?
Uzun oturumda preload'un tokenı dolabilir. Preload hazır değil → gapless geçiş çalışmaz → her şarkıda 2–3 saniye donma oluşur. 16 saatte yüzlerce geçiş = sürekli takılma.
Düzeltme önerisi
// preloadNextSong() içinde:
const preloadUrl = await createHlsBlobUrl(
data.stream_url
);
trackBlobUrl(preloadUrl);
this._preloadedNext.blobUrl = preloadUrl;
hlsPreload.loadSource(preloadUrl);
// _cleanupPreloadedNext()'e ekle:
if (preloadInfo.blobUrl) {
revokeBlobUrl(preloadInfo.blobUrl);
}
_networkOffline Başlangıç Değeri Tanımsızthis._networkOffline = !navigator.onLine enableBackgroundPlayback() içine eklendi.
Ne oluyor?
Alpine store'da _networkOffline property'si undefined olarak başlıyor. offline eventi hiç ateşlenmezse ve sayfa ilk yüklendiğinde zaten offline ise flag set edilmez.
16 saatte etkisi?
undefined falsy olduğu için pratik sorun oluşturmaz. Ancak tarayıcı offline modda başlarsa (kafe elektrik + internet aynı anda kesilmişse) queue refill başarısız API çağrıları yapar.
Düzeltme önerisi
// Alpine store init'e ekle: _networkOffline: !navigator.onLine, // VEYA init() fonksiyonunda: this._networkOffline = !navigator.onLine;
console.* Çağrısı — 200 Satır Production'da AktifNe oluyor?
7958 satırlık dosyada 200 adet console.* çağrısı var. Her şarkı geçişi, her HlsPool.acquire/release, her preload adımı log atıyor. Chrome DevTools kapalıysa bile string concatenation maliyeti var.
16 saatte etkisi?
Eski cihazda (4GB RAM, 2 çekirdek) Chrome console buffer dolabilir. Daha kritik: her geçiş 8–12 console satırı → 16 saatte 400–600 şarkı × 10 log = ~5000 console.log. Bellek birikimi riski.
Düzeltme önerisi
// Global debug flag ekle:
const DEV_MODE = location.search.includes('debug=1')
|| location.hostname === 'localhost';
// Sonra:
DEV_MODE && console.log('🔄 HlsPool.acquire:', ...);
queueMonitorInterval Cleanup EksikNe oluyor?
startQueueMonitor()'da guard var (if (this.queueMonitorInterval) return), ancak sayfa SPA geçişinde Alpine store destroy olursa veya queueMonitorInterval sıfırlanmadan yeniden başlatılırsa birden fazla interval çalışabilir.
16 saatte etkisi?
SPA geçişi her yapıldığında yeni bir monitor başlayabilir. Bunun önüne geçen guard var ama destroy sırasında clearInterval yapılmadıysa zombie interval kalabilir.
Düzeltme önerisi
// Alpine store destroy() hook'una ekle:
destroy() {
if (this.queueMonitorInterval) {
clearInterval(this.queueMonitorInterval);
this.queueMonitorInterval = null;
}
if (this.progressInterval) {
clearInterval(this.progressInterval);
this.progressInterval = null;
}
}
Ne oluyor?
Satır 7184'teki 15 saniyelik timeout handle'ı saklanmıyor. Preload başarıyla tamamlansa ve şarkı çalmaya başlasa bile 15. saniyede timeout callback çalışır; o anda farklı bir preload varsa onu temizleyebilir.
16 saatte etkisi?
songId === preloadSongId kontrolü koruma sağlıyor, bu yüzden yanlış cleanup riski düşük. Ancak yoğun kullanımda (hızlı şarkı geçişleri) timeout sızıntısı birikebilir.
Düzeltme önerisi
// Timeout'u kaydet:
this._preloadedNext.timeoutId =
setTimeout(() => { ... }, 15000);
// _cleanupPreloadedNext'e ekle:
if (preloadInfo.timeoutId) {
clearTimeout(preloadInfo.timeoutId);
}
Production guard (isErrorAction) doğru kurulmuş. AncakignoredActions listesi sabit tanımlı; yeni beklenen uyarılar eklenirse unutulabilir. Dokümante edilmeli.
saveQueueState() — Eski Cihazda I/O BaskısısetInterval(() => saveQueueState(), 5000) satır 7689'da tanımsız. saveQueueState()'in localStorage'a yazıp yazmadığına bağlı olarak eski SSD veya eMMC'li cihazlarda birikimli gecikme yaratabilir. 5sn → 30sn gibi daha uzun aralık değerlendirilebilir.
Cache'ten atılan entry'lerin blobUrl'i varsa revoke edilmiyor. Cache sadece metadata tutuyor (stream_url string), blob URL'sini değil — bu yüzden pratikte sorun yok. Ama cache ile blob lifecycle bağlanmamış, ileride sorun çıkabilir.
| Saat | Beklenen Durum | Risk Faktörü | Güvenlik |
|---|---|---|---|
| 0h | İlk şarkı yüklendi, HlsPool 2 instance hazır, queue dolu | Başlangıç: _networkOffline=undefined (falsy, sorun yok). Adaptif config aktif. |
✅ %95 |
| 1h | ~14 şarkı çalındı, queue 2 kez refill edildi, bellek kararlı | streamUrlCache ~5 entry. Blob URL'ler düzenli revoke ediliyor. İlk GC olayları başlayabilir. | ✅ %92 |
| 4h | ~56 şarkı, bellek %30–50 bölgesinde, HlsPool yüzlerce kez acquire/release döngüsü | console.log birikimi başlar. Paylaşımlı throttle değişkeni spot reklamlarda sorun çıkarabilir. Preload tokenları dolmaya başlayabilir. | ⚠️ %80 |
| 8h | ~112 şarkı. Queue düzenli temizleniyor (5 önceki şarkı tutulur). Bellek büyük ihtimalle kararlı. | Kafe Wi-Fi kesilmesi ihtimali arttı. Online recovery HLS.js başlatmıyor → sessizlik riski. Chrome console memory dolabilir. | ⚠️ %72 |
| 12h | ~168 şarkı. HlsPool'da _nextTrackId ~500+ değere ulaştı. streamUrlCache LRU döngüsü aktif. | Gece 8 → gece 12 arası: eski cihazda Chrome GC baskısı artar. Preload blob sızıntısı birikmiş olabilir. queueMonitorInterval zombie riski. | ❗ %62 |
| 16h | ~224 şarkı. Tüm interval'ler çalışıyor. activeBlobUrls temizlenmiş olmalı. | En yüksek risk: Wi-Fi kesintisi + HLS.js sessizliği (Sorun #2), preload blob sızıntısı (Sorun #3), 200 console.log birikimi. Kritik 3 sorun çözülmemişse kurtarma yapılmamış olabilir. | ❗ %52 |
Stream URL'leri blob'a çevirmek "DevTools'ta URL gizleme" amacıyla yapılmış. Ancak AES-128 şifreli HLS zaten DRM koruması sağlıyor; blob URL sadece network tab'ı gizliyor, gerçek koruma sağlamıyor. Üstelik işlem async, preload tutarsızlığına (Sorun #3) zemin hazırladı. Blob layer kaldırılıp sadece XOR şifreli URL decode ile yetinilebilir — bu aynı güvenliği daha az karmaşıklıkla sağlar.
Havuz boyutu 2: 1 aktif + 1 preload. Bu makul. Ancak crossfade aktif olduğunda teorik olarak 3 instance gerekebilir (eski, yeni, preload). Hard limit 4 × poolSize = 4 ile sınırlanmış. Crossfade şu an kapalı (crossfadeEnabled: false), bu yüzden kritik değil. Eğer gelecekte açılırsa pool boyutu 3'e çıkarılmalı.
HlsPool _active: new Map() kullanarak _trackId ile instance takip ediyor. Bu Alpine proxy bypass için tasarlanmış akıllı bir çözüm. Fakat this._nextTrackId integer sınırına ulaşırsa (JavaScript Number.MAX_SAFE_INTEGER = 9e15, pratikte imkânsız) sorun çıkar. Daha gerçekçi risk: Alpine reaktivite sistemi store'u serialize etmeye çalışırsa Map içindeki HLS instance referansları patlar.
queueMonitorInterval içinde this.checkAndRefillQueue() async fonksiyon çağrısı var. setInterval async fonksiyonu beklemez; bir önceki çağrı bitmeden yenisi başlayabilir. _queueRefillInProgress guard bunu önlüyor — bu iyi. Ancak try/catch dışında kalan await'ler unhandled rejection oluşturabilir.
Sistem genelinde hataları "sessizce atla ve devam et" felsefesiyle tasarlanmış. Bu kafe senaryosu için doğru — müziğin durması hata mesajından daha kötü. Ancak sessiz hata birikimleri debug'u imkânsız kılar. Örnek: preload HLS error geldiğinde _cleanupPreloadedNext() sessizce çağrılıyor. 16 saat boyunca her preload başarısız olsa bile kimse fark etmez.
Kafe senaryosunda kimse başında durmasa da, uygulama güncelleme sonrası sayfa yenileme veya ağ sorunuyla birden fazla şarkı tetiklenirse ne olur? _nextTrackInProgress guard var, ama burst geçişlerde HlsPool'un overflow path'i aktif olur ve "yeni HLS instance oluşturuldu" logları birikir. Bu 3–4 instance'ın aynı anda decode etmesine yol açabilir.
crossfadeEnabled: false olmasına rağmen crossfade için yazılmış tüm kod (startCrossfade, fadeAudioElement, howlNext, hlsNext yönetimi, onCrossfadeComplete vb.) çalışmaya devam ediyor. Güvenli olarak tasarlanmış — flag false ise crossfade tetiklenmiyor. Ancak bu dead code binlerce satır boyunca yayılmış durumda ve bakım yükünü artırıyor.
getAdaptiveHlsConfig() sayfa yüklenirken bir kez çalışıyor. Ama kafe senaryosunda bağlantı kalitesi sürekli değişir. Öğlen saati yoğun → akşam sakin → gece neredeyse offline. Dinamik ABR (HLS.js'in kendi mekanizması) bunu kısmen hallediyor, ancak maxBufferLength gibi statik parametreler gün boyunca ideal olmayabilir.
"Bu sistemi bir rakiple kıyaslasaydım, şu noktalarda öne çıkar:"
HlsPool — Gerçek Anlamda Spotify Tarzı
Instance destroy/create yerine havuzdan al-ver döngüsü. Alpine proxy bypass için _trackId ile Map takibi zekice. Tainted instance mekanizması nadir hata durumlarını temizce hallediyor.
getAdaptiveHlsConfig() — Cihaz Farkındalığı
RAM ≤ 4GB veya çekirdek ≤ 2 algılayıp buffer boyutlarını düşürüyor, 64kbps ABR tahminiyle başlıyor. Kafe donanımı için birebir uygun.
streamUrlCache LRU + TTL — Standardize Edildi
MAX_CACHE_SIZE=30 ve 5 dakika TTL ile otomatik temizlik. 3 farklı yazma paterni (direkt .set, max 10, max 30) tek addToStreamCache() fonksiyonunda birleştirildi.
_failedSongs Blacklist + Otomatik Expiry
Çalınamayan şarkı 5 dakika bloklanıyor, sonra otomatik silinip tekrar deneniyor. Sonsuz döngüyü önlüyor ve geçici server hatalarına karşı dirençli.
safeAudioCleanup() — src='' Yasağı
src='' yerine removeAttribute('src') kullanmak "empty src" hatasını önlüyor. audio.load() ile MediaSource buffer'ı serbest bırakma profesyonel bir yaklaşım.
backBufferLength: 0
Çalınmış segmentleri bellekte tutmuyor. Kafe senaryosunda geri sarma olmayacağından bu doğru bir karar — RAM tasarrufu için idealdir.
Double Query String Önleme (Blob URL)
Variant URL'lerde trimmed.includes('?') kontrolü ile token'ın iki kez eklenmesi önlenmiş. İnce ama önemli bir edge case.
Race Condition Guard'ları
_queueRefillInProgress, _nextTrackInProgress, _preloadNextInProgress — üç ayrı concurrent call guard. Production'da yaygın bir ihmal, burada özenli uygulanmış.
MuzibuPerformanceDebug — Endüstriyel Seviye İzleme
Root cause analizi, freeze snapshot, HLS ABR geçmişi, segment istatistikleri, JS fault log, cihaz bilgisi — 2500 satır debug altyapısı. Çoğu startup bu seviyeyi asla oluşturmaz.
Queue Emergency Fallback Zinciri
Context yok → currentSong'dan oluştur → popular fallback → emergency queue. 4 aşamalı fallback zinciri kuyruk hiç boş kalmasın diye özenle kurulmuş. Kafe "kimse bakıyor" senaryosu için mükemmel.
Ne yapılacak: Her ontimeupdate handler'ında self._lastTimeupdateProcess yerine handler'a özgü değişken kullan: _lastTimeupdateMain, _lastTimeupdateSpot, _lastTimeupdatePreload, vb.
Neden: Spot reklam ve ana oynatıcının birbirini bloke etmesi gapless geçişi bozuyor.
Tahmini etki: Spot → şarkı geçişindeki sessizlik tamamen ortadan kalkar.
Ne yapılacak: window.addEventListener('online', ...) callback'ine this.hls?.startLoad() + şarkı token yenileme + şarkı çalmıyorsa yeniden başlatma ekle.
Neden: Wi-Fi kesildiğinde HLS.js durabilir, online olunca sadece queue doluyor ama müzik başlamıyor.
Tahmini etki: 16 saatlik kesintisiz çalmada Wi-Fi kesintisi sonrası otomatik kurtarma sağlar.
Ne yapılacak: hlsPreload.loadSource(data.stream_url) yerine createHlsBlobUrl() çağır, dönen URL'yi _preloadedNext.blobUrl'de sakla, _cleanupPreloadedNext()'e revoke ekle.
Neden: Ana oynatıcı ile tutarsızlık ve uzun oturumda token expiry → preload başarısız.
Tahmini etki: Gapless geçişin 16 saat boyunca güvenilir çalışması.
Ne yapılacak: Dosya başına const DEV = location.search.includes('debug=1') ekle, tüm debug/trace log'ları DEV && console.log(...) ile sar.
Neden: 200 console çağrısı eski Chrome'da bellek ve CPU baskısı yaratır.
Tahmini etki: Eski cihazlarda %5–15 CPU azalması, bellek stabilizasyonu.
_networkOffline başlangıç değeri ekleNe yapılacak: Alpine store init'e _networkOffline: !navigator.onLine ekle.
Neden: Sayfa internet kesiliyken açılırsa queue refill gereksiz API çağrıları yapar.
Tahmini etki: Edge case kapandı, 2 satır değişiklik.
Ne yapılacak: Alpine store destroy() hook'unda tüm interval'leri temizle.
Neden: SPA geçişlerinde zombie interval birikmesini önler.
Tahmini etki: Uzun oturumlarda bellek ve CPU leak önlenir.
_preloadedNext'e kaydet ve clearTimeout ekleNe yapılacak: this._preloadedNext.timeoutId = setTimeout(... yapısı + _cleanupPreloadedNext'te clearTimeout.
Neden: Zombie timeout birikimini önler, 2 satır değişiklik.
Tahmini etki: Düşük ama temiz bir hygiene fix.