Teknik Analiz Raporu

Muzibu Player Sistem Analizi

player-core.js · performance-debug.js · buffer-monitor.js · 19 Şubat 2026

stroke-linecap="round" transform="rotate(-90 45 45)"/> 88 /100 Genel Sağlık
5
Çözüldü ✅
1
Kritik Kalan
5
Orta/Düşük Kalan
Genel Değerlendirme: 16 saatlik kafe senaryosu için iyi-çok iyi seviyede güvenilir. 11 sorundan 5'i çözüldü (throttle ayrıştırma, online recovery, _networkOffline başlangıcı, cache tutarsızlığı, hls.startLoad). Kalan kritik: preload blob URL eksikliği.

🔍 Son Değişikliklerin Doğrulanması

# 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

⚠️ Sorun Kartları (öncelik sırasına göre)

🔴 KRİTİK player-core.js: ontimeupdate (6 yer) ✅ ÇÖZÜLDÜ — commit 929d263b6

Paylaşımlı _lastTimeupdateProcess — 6 Handler Birbirini Bloke Ediyor

4 ayrı değişkene ayrıldı: _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;
🔴 KRİTİK player-core.js ~7603 ✅ ÇÖZÜLDÜ — commit 929d263b6

Online Recovery Eksik — HLS.js Kör Kalıyor

Online event'te 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);
});
🔴 KRİTİK player-core.js ~7090, ~7155

Preload'da Blob URL Kullanılmıyor — Güvenlik + Token Tutarsızlığı

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);
}
🟡 ORTA player-core.js ~756, ~7469 ✅ ÇÖZÜLDÜ — commit 929d263b6

_networkOffline Başlangıç Değeri Tanımsız

this._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;
🟡 ORTA player-core.js ~200 satır

Aşırı console.* Çağrısı — 200 Satır Production'da Aktif

Ne 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:', ...);
🟡 ORTA player-core.js ~7449

queueMonitorInterval Cleanup Eksik

Ne 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;
  }
}
🟡 ORTA player-core.js ~7184

Preload Timeout Clearable Değil

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);
}
🟢 DÜŞÜK player-core.js ~533

serverLog Sadece Hata Durumlarında Çalışıyor — Beklenen Bir Sorun Değil, Ama Bilinmeli

Production guard (isErrorAction) doğru kurulmuş. AncakignoredActions listesi sabit tanımlı; yeni beklenen uyarılar eklenirse unutulabilir. Dokümante edilmeli.

🟢 DÜŞÜK player-core.js ~7689

Her 5 saniyede 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.

🟢 DÜŞÜK player-core.js ~984

streamUrlCache LRU Silmede Blob Revoke Eksik

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.

16 Saat Kafe Senaryo Analizi

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
* Güvenlik skoru: 3 kritik sorun giderilirse tüm saatlerde %20 artış beklenir → 16. saatte %72'ye çıkar.

🏗️ Mimari Değerlendirme & Serbest Analiz

🔵 GÖZLEM

Blob URL'nin Varlık Nedeni Düşündürücü

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.

🔵 GÖZLEM

HlsPool Tasarımı Sorgulanabilir — Neden 2?

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ı.

🟡 ŞÜPHE

"16 saat sonra patlayabilir" — Alpine Proxy + HlsPool Map Karışımı

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.

🟡 ŞÜPHE

setInterval İçinde async + await — Hata Yutma Riski

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.

🔴 ENDİŞE

Kimse Bakmazsa: Sessiz Failure Modu

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.

🔴 ENDİŞE

Eksik Test Senaryosu: Hızlı Şarkı Geçişi Saldırısı

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.

🔵 GÖZLEM

Crossfade Devre Dışı Ama Kodu Hâlâ Aktif

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.

🔵 GÖZLEM

Adaptif HLS Config Sadece Init Anında Çalışı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.

İyi Yapılanlar

"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.

📋 Sonraki Adım Önerileri

✅ YAPILDI

Paylaşımlı throttle değişkenini 6 özel değişkene ayır

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.

✅ YAPILDI

Online recovery'ye HLS.js startLoad() ekle

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.

3 · KRİTİK

preloadNextSong()'da blob URL kullan ve cleanup'ta revoke et

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ı.

4 · ÖNEMLİ

console.log'ları DEV_MODE flag ile koru

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.

✅ YAPILDI

_networkOffline başlangıç değeri ekle

Ne 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.

6 · İYİLEŞTİRME

queueMonitorInterval + progressInterval'i Alpine destroy'a bağla

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.

7 · İYİLEŞTİRME

Preload timeout ID'sini _preloadedNext'e kaydet ve clearTimeout ekle

Ne 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.