Analiz Raporu

Şarkı Geçiş Sorunları Analizi

Mobil, Tablet, Eski Cihaz ve Ekran Kapanma Senaryoları

Basit Anlatım (Herkes İçin)

Telefonlarda ve tabletlerde bir şarkıdan diğerine geçerken sorun yaşanmasının birkaç temel nedeni var:

Ekran Kapanınca Ne Oluyor?

Telefon ekranını kapattığınızda veya uyku moduna geçtiğinde, tarayıcı arka plandaki işlemleri yavaşlatıyor. Şarkı çalmaya devam ediyor ama bir sonraki şarkıyı önceden hazırlama (preload) işlemi aksıyor. Şarkı bittiğinde yeni şarkı hazır olmadığı için sessizlik veya takılma oluşuyor.

Eski/Zayıf Cihazlar

2 GB RAM'li veya 2 çekirdekli eski telefonlarda, hem mevcut şarkıyı çalmak hem de sonraki şarkıyı hazırlamak için yeterli kaynak olmayabiliyor. Tarayıcı hafıza tasarrufu için arka plan işlemlerini durduruyor.

Yavaş Bağlantı + Geçiş

3G veya zayıf WiFi'da şarkı bitmeden yeni şarkının verisi (stream URL + segment'ler) gelemeyebiliyor. Sonuç: Şarkı bitiyor ama yeni şarkı henüz yüklenmemiş.

iOS Özel Davranışı

iPhone/iPad'lerde Safari, arka plandaki sekmelere çok sert davranıyor. Özellikle güç tasarrufu modunda HLS segment yüklemesini tamamen durdurabiliyor. Android'de bu kadar agresif değil.

Neden Önemli? Müzik dinleme deneyiminin en kritik noktası kesintisiz geçiştir. Kullanıcı telefonu cebine koyup müzik dinlerken, her şarkı arasında duraksa veya takılsa deneyim kötüleşir ve kullanıcı başka platforma yönelir.

Teknik Detaylar (Geliştiriciler İçin)

Sorun 1: Arka Plan / Ekran Kapanma Throttling

KRİTİK

Kök Neden: Ekran kapanınca (screen lock) veya sekme arka plana düşünce tarayıcılar setTimeout, setInterval, fetch/XHR isteklerini throttle eder veya tamamen durdurur.

Platform Sekme Arka Planda Ekran Kapalı Güç Tasarrufu
iOS Safari Timer 1dk'ya yavaşlar JS tamamen durur Agresif freeze
Android Chrome Timer 1dk throttle 5dk sonra freeze Pil düşükse agresif
iPadOS Safari Timer suspend Tab discard (15dk) Stage Manager'da agresif
Android WebView Tamamen freeze Tamamen freeze Tamamen freeze

MEVCUT KOD — Etkilenen Yerler:

// player-core.js:3310 — Watchdog setInterval(10s) → Ekran kapalıyken çalışmaz
// player-core.js:4609 — Preload trigger %80 timeupdate → Timer throttle'da geç tetiklenir
// player-core.js:7761preloadNextSong() → fetch() arka planda bloke
// player-core.js:8405visibilitychange → Sadece tab visible olunca çalışır (çok geç)
// player-core.js:6050-6064 — onpause auto-resume setTimeout(300ms) → Throttle'da 60sn

Sorun 2: Preload Zamanlama ve Başarısızlık

YÜKSEK

Sorun: preloadNextSong() şarkının %80'inde tetikleniyor. Ancak ekran kapalıyken timeupdate event'i throttle olur veya hiç gelmez.

MEVCUT AKIŞ (Sorunlu)
  1. Şarkı çalıyor, ekran kapalı
  2. timeupdate event'leri throttle (iOS: hiç gelmez)
  3. %80 eşiği hiç tetiklenmez
  4. preloadNextSong() çağrılmaz
  5. Şarkı bitiyor → onended tetiklenir
  6. nextTrack() → playSongFromQueue() → stream URL fetch
  7. fetch() arka planda bloke veya yavaş
  8. SONUÇ: 5-15sn sessizlik
İDEAL AKIŞ (Hedef)
  1. Şarkı çalıyor, ekran kapalı
  2. BUFFER_EOS (HLS.js event) — throttle'dan bağımsız
  3. Preload zaten başlamış (veya stream URL cache'de)
  4. Şarkı bitiyor → onended
  5. Preloaded audio element hemen devreye girer
  6. SONUÇ: <500ms geçiş

KRİTİK KOD YOLLARI:

player-core.js:5655-5693 — BUFFER_EOS handler mevcut (iyi)
player-core.js:4459 — Preloaded gapless path mevcut (iyi)
player-core.js:7777_preloadNextInProgress flag takılabilir
player-core.js:7963 — Preload timeout 15s → arka planda fetch bloke ise timeout
player-core.js:2607-2636 — preloadFirstInQueue() → fetch arka planda bloke

Sorun 3: HLS.js Instance Lifecycle ve Bellek

YÜKSEK

HlsPool (3 instance): Her geçişte acquire()release() döngüsü var. Arka planda preload acquire yapınca 3 slot dolabilir.

SENARYO: Eski Cihaz + Ekran Kapalı

1. Slot A: Mevcut şarkı (aktif çalıyor)
2. Slot B: Preloaded sonraki şarkı
3. Slot C: Crossfade next (veya spot player)
→ Pool dolu! Arka planda fetch de bloke.
→ Şarkı bitiyor, preload timeout, nextTrack yeni acquire istiyor
→ HlsPool HARD LIMIT (player-core.js:673)
→ Eski cihazda 3 HLS instance = ~36MB buffer bellek
→ 2GB RAM cihazda %30 tarayıcı hafızası → OOM riski

Sorun 4: iOS Safari — Çoklu Audio Element Kısıtlaması

KRİTİK

iOS'un katı kuralı: Aynı anda sadece 1 audio element ses çalabilir. İkincisine play() çağırdığında birincisi otomatik pause olur.

MEVCUT AUDIO ELEMENT'LER
  • #hlsAudio — Ana şarkı
  • #hlsAudioNext — Preloaded/Crossfade next
  • spotAudio — Spot (reklam) player
iOS SORUNLARI
  • Preload audio.play() → mevcut şarkıyı durdurur
  • Gapless geçiş mümkün değil (2 audio aynı anda ×)
  • Ekran kapalıyken autoplay policy daha katı
  • Safari native HLS path farklı davranıyor

İLGİLİ KOD:

player-core.js:6242-6268 — Safari track end fallback (timeupdate son 0.5sn)
player-core.js:6273-6286 — Safari onended fallback (double trigger koruması)
player-core.js:6296-6318 — Safari unexpected pause → auto-resume (3 deneme)
⚠ Bu fallback'ler iyi düşünülmüş ama ekran kapalıyken timeupdate gelmezse 0.5sn fallback da çalışmaz!

Sorun 5: nextTrack Guard Lock Takılması

ORTA

Guard mekanizması: _nextTrackInProgress flag ile concurrent nextTrack() çağrıları engelleniyor. 10sn timeout ile kilit açılıyor.

SORUN SENARYOLARı:

player-core.js:2208-2223
1. nextTrack() çağrılıyor → _nextTrackInProgress = true
2. playSongFromQueue() → fetch (stream URL) başlıyor
3. Ekran kapanıyor → fetch bloke / gecikiyor
4. 10sn guardTimeout tetikleniyor → _nextTrackInProgress = false
5. Watchdog (her 10sn) → audio.ended görüyor → onTrackEnded çağırıyor
6. İkinci nextTrack() → bu sefer lock açık, tekrar deniyor
⚠ SORUN: Fetch hâlâ devam ediyorsa iki paralel geçiş race condition
⚠ 300ms double-trigger guard + 1000ms debounce → yavaş geçişlerde yetersiz

Sorun 6: Watchdog 30sn Bekleme Süresi

ORTA

player-core.js:3365 — Audio paused + 30 saniye bekleme süresi var. Ekran kapalıyken onpause auto-resume başarısız olursa, watchdog 30sn sonra devreye giriyor.

Ama watchdog setInterval(10s) kendisi de throttle olur! iOS'ta ekran kapalıyken hiç çalışmayabilir.

// Watchdog zinciri — her adım bağımsız timer
setInterval(10s) → paused kontrol → 30s sayaç → resume dene → başarısız → nextTrack
Toplam beklenme: 10s (interval) + 30s (sayaç) = ~40sn sessizlik!
iOS ekran kapalı: setInterval çalışmaz → SONSUZ beklenme

Çözüm Önerileri (Öncelik Sırasına Göre)

Çözüm 1: Erken ve Agresif Preload (En Etkili)

ÖNCELİK 1

Mevcut: Şarkının %80'inde preload tetikleniyor. Ama bu timeupdate event'ine bağlı → arka planda çalışmaz.

Önerilen Değişiklikler:

  • A) Stream URL'yi şarkı başlarken al — Şarkı çalmaya başladığında hemen sonraki şarkının stream URL'sini fetch et ve cache'le. Böylece sadece HLS attach kalsın, ağır kısım (API çağrısı) bitmiş olur.
  • B) BUFFER_EOS'u preload trigger olarak kullan — HLS.js BUFFER_EOS tüm segment'ler yüklendiğinde tetiklenir. Bu, timeupdate'ten bağımsız ve güvenilir. Mevcut kodda BUFFER_EOS zaten var ama sadece track-end için kullanılıyor; preload trigger olarak da eklenebilir.
  • C) HLS.js FRAG_BUFFERED son segment algılama — Son 2-3 segment yüklendiğinde preload'ı başlat. Bu, ekran kapalıyken bile çalışır çünkü HLS.js kendi worker thread'inde segment yüklemeye devam eder.

Çözüm 2: Stream URL Ön-Cacheleme

ÖNCELİK 2

En yavaş adım: authenticatedFetch('/api/muzibu/songs/{id}/stream') — Arka planda bloke olabilir.

Önerilen:

  • A) Queue'daki sonraki 3 şarkının stream URL'sini önceden al — queue dolduğunda veya her yeni şarkı başladığında, sonraki 2-3 şarkının stream URL'sini batch olarak al ve cache'e koy. Mevcut streamCache zaten var, daha agresif doldurulabilir.
  • B) Batch API endpoint — Tek istekle birden fazla şarkının stream bilgisini al: /api/muzibu/songs/batch-stream?ids=1,2,3

Çözüm 3: iOS/Safari İçin Web Audio API Fallback

ÖNCELİK 3

Önerilen:

  • A) Media Session API aktif tutma — Mevcut updateMediaSession() (player-core.js:3671) var. iOS'ta MediaSession metadata doğru set edildiğinde tarayıcı audio'yu arka planda yaşatır. navigator.mediaSession.playbackState = 'playing' her geçişte set edilmeli.
  • B) Audio Context keep-alive — Sessiz bir AudioContext sürekli çalıştırarak iOS'un arka plan audio thread'ini canlı tut. Spotify web player bu tekniği kullanıyor.
  • C) Tek audio element stratejisi (iOS) — iOS'ta 2 audio element yerine tek element kullan. Geçişte source değiştir (crossfade olmadan). Gapless imkansız ama tutarlı çalışır.

Çözüm 4: Eski Cihaz İçin Hafif Mod

ÖNCELİK 3

Önerilen:

  • A) HlsPool size'ı düşür — Eski cihazlarda (RAM≤4GB, cores≤2) pool size'ı 3→2 yap. Preload'ı devre dışı bırak, sadece stream URL cache kullan.
  • B) Buffer boyutunu adaptif düşür — Mevcut getAdaptiveHlsConfig() (player-core.js:596) maxBufferLength=12 yapıyor. Ama eski cihazlarda 8'e düşürülebilir.
  • C) Crossfade otomatik kapatma — Eski cihazlarda crossfade kapatılarak 2. HLS instance ihtiyacı ortadan kalkar.

Çözüm 5: Watchdog İyileştirme

ÖNCELİK 4
  • A) visibilitychange + watchdog combo — Sekme görünür olduğunda watchdog'u anında tetikle (bekleme süresi olmadan). Mevcut syncPlayerState() sadece UI sync yapıyor, audio durumunu kontrol etmiyor.
  • B) Paused bekleme süresini düşür — 30sn → 10sn. Ekran kapalıyken zaten watchdog çalışmıyor, bu değer sadece visible durumdaki sorunları etkiliyor.
  • C) onpause handler'ı güçlendir — 3 deneme + 300ms/1s/1s yerine requestAnimationFrame veya MutationObserver ile anında recovery dene.

Sorun → Etki → Çözüm Özet Tablosu

Sorun Risk Etkilenen Cihaz Birincil Çözüm
Arka plan throttling Kritik Tüm mobil, özellikle iOS Erken preload + BUFFER_EOS trigger
Preload zamanlama Yüksek Yavaş bağlantı + mobil Stream URL ön-cacheleme
HLS pool bellek Yüksek Eski cihaz (≤4GB RAM) Eski cihaz → pool 2, crossfade kapalı
iOS çoklu audio Kritik iPhone, iPad, iPod Tek audio element + AudioContext keep-alive
nextTrack lock Orta Yavaş bağlantı + tüm cihaz Guard timeout + abort controller
Watchdog yavaş Orta Tüm platform (visible) visibilitychange anında kontrol

Önerilen Uygulama Sırası

Faz 1 — Hızlı Kazanım
  • ✅ Stream URL batch pre-fetch
  • ✅ visibilitychange → anında watchdog
  • ✅ Watchdog pause süresini 30s→10s
  • ✅ MediaSession playbackState sync
Etki: Geçiş süresini ~10sn → ~3sn
Faz 2 — Derin Fix
  • 🔧 BUFFER_EOS → preload trigger
  • 🔧 Eski cihaz hafif mod
  • 🔧 HlsPool adaptif boyut
  • 🔧 iOS tek audio element modu
Etki: Geçiş süresini ~3sn → ~1sn
Faz 3 — Gelişmiş
  • 🚀 AudioContext keep-alive (iOS)
  • 🚀 Service Worker ön-cache
  • 🚀 WebTransport / Push API
Etki: Gapless geçiş (Spotify seviyesi)

24 Şubat 2026 • Muzibu.com