Fix Brifing Dokümanı · v2
Dosya: public/themes/muzibu/js/player/core/player-core.js
Bu doküman fix yapacak developer/AI için hazırlanmıştır. Kod değiştirilmemiştir.
_lastTimeupdateProcess
Dosyada 6 farklı ontimeupdate handler var. Her biri
saniyede yüzlerce kez ateşlenen bu event'i 250ms'ye throttle etmek için aynı tekniği kullanıyor —
"son ne zaman çalıştım?" diye bir değişkene bakıyor, 250ms geçmediyse erken çıkıyor.
Problem şu: 6 handler'ın hepsi self._lastTimeupdateProcess adlı tek bir değişkeni paylaşıyor.
Handler A çalıştı ve timestamp güncelledi. Handler B 30ms sonra çalışmak istiyor ama bakıyor: "250ms geçmemiş" →
geri dönüyor. B'nin çalışmaması gereken bir durum değildi — aynı değişkeni paylaştığı için bloke oldu.
Çözüm basit: Her handler kendi değişkenini kullanacak. 6 satır değişiklik (her handler'da 1 rename).
Her handler'da bu üç satır var, sadece bağlam (hangi audio) farklı:
// 🎵 CROSSFADE TRIGGER: timeupdate event for crossfaded HLS (throttled 250ms)
nextAudio.ontimeupdate = function() {
// 🚀 THROTTLE: 250ms
const _now = performance.now();
if (_now - (self._lastTimeupdateProcess || 0) < 250) return; // ← SORUN
self._lastTimeupdateProcess = _now; // ← SORUN
if (!self.duration || self.duration <= 0) return;
if (self.isCrossfading) return;
// ... crossfade tetikleme mantığı ...
// 🎙️ PROGRESS: Spot için timeupdate listener (progress bar güncelleme + preload)
spotAudio.ontimeupdate = function() {
// 🚀 THROTTLE: 250ms
const _now = performance.now();
if (_now - (self._lastTimeupdateProcess || 0) < 250) return; // ← SORUN
self._lastTimeupdateProcess = _now; // ← SORUN
if (self._isPlayingSpot && spotAudio.duration > 0) {
self.currentTime = spotAudio.currentTime;
// ... spot progress güncelleme ...
// 🎵 TIMEUPDATE: Preload + Spot Preload + Crossfade trigger (throttled 250ms)
preloadedAudio.ontimeupdate = function() {
// 🚀 THROTTLE: 250ms
const _now = performance.now();
if (_now - (self._lastTimeupdateProcess || 0) < 250) return; // ← SORUN
self._lastTimeupdateProcess = _now; // ← SORUN
// (Safari native preload path)
// 🎵 TIMEUPDATE: Preload + Spot Preload + Crossfade trigger (HLS.js preloaded, throttled 250ms)
preloadedAudio.ontimeupdate = function() {
// 🚀 THROTTLE: 250ms
const _now = performance.now();
if (_now - (self._lastTimeupdateProcess || 0) < 250) return; // ← SORUN
self._lastTimeupdateProcess = _now; // ← SORUN
// (HLS.js preload path)
// 🎵 CROSSFADE TRIGGER: timeupdate event (throttled 250ms)
// Bu event page hidden olsa bile düzgün çalışır
audio.ontimeupdate = function() {
// 🚀 THROTTLE: 250ms — saniyede 4-250 ateşlenme → max 4
const _now = performance.now();
if (_now - (self._lastTimeupdateProcess || 0) < 250) return; // ← SORUN
self._lastTimeupdateProcess = _now; // ← SORUN
// ... duration kontrol, preload tetikleme, crossfade tetikleme ...
// 🎵 CROSSFADE TRIGGER: timeupdate event for Safari (throttled 250ms)
audio.ontimeupdate = function() {
// 🚀 THROTTLE: 250ms
const _now = performance.now();
if (_now - (self._lastTimeupdateProcess || 0) < 250) return; // ← SORUN
self._lastTimeupdateProcess = _now; // ← SORUN
// ... Safari path ...
Yapılacak işlem: Her handler'daki iki satırda sadece değişken adını değiştir. Başka hiçbir şey değişmiyor — throttle süresi, mantık, konum aynı kalıyor.
Handler 1 · Satır ~2605–2606 → Değişken adı: _lastTimeupdateCrossfadeNext
const _now = performance.now();
if (_now - (self._lastTimeupdateCrossfadeNext || 0) < 250) return;
self._lastTimeupdateCrossfadeNext = _now;
Handler 2 · Satır ~2945–2946 → Değişken adı: _lastTimeupdateSpot
const _now = performance.now();
if (_now - (self._lastTimeupdateSpot || 0) < 250) return;
self._lastTimeupdateSpot = _now;
Handler 3 · Satır ~4028–4029 → Değişken adı: _lastTimeupdatePreloadSafari
const _now = performance.now();
if (_now - (self._lastTimeupdatePreloadSafari || 0) < 250) return;
self._lastTimeupdatePreloadSafari = _now;
Handler 4 · Satır ~4211–4212 → Değişken adı: _lastTimeupdatePreloadHls
const _now = performance.now();
if (_now - (self._lastTimeupdatePreloadHls || 0) < 250) return;
self._lastTimeupdatePreloadHls = _now;
Handler 5 · Satır ~5235–5236 → Değişken adı: _lastTimeupdateMain
const _now = performance.now();
if (_now - (self._lastTimeupdateMain || 0) < 250) return;
self._lastTimeupdateMain = _now;
Handler 6 · Satır ~5446–5447 → Değişken adı: _lastTimeupdateSafari
const _now = performance.now();
if (_now - (self._lastTimeupdateSafari || 0) < 250) return;
self._lastTimeupdateSafari = _now;
Önemli not:
Bu yeni değişkenler Alpine store'a önceden tanımlanmasına gerek yok.
JavaScript'te undefined → || 0 ile sıfıra düşüyor,
ilk çalışmada zaten doğru davranıyor. Sadece handler içindeki isimleri değiştirmek yeterli.
// 16 saatlik çalmada, reklam çalıyorken ne oluyor?
T=0ms : spotAudio.ontimeupdate ateşlendi
→ _lastTimeupdateProcess = 1000ms
T=30ms : audio.ontimeupdate ateşlendi (ANA PLAYER)
→ (1000+30) - 1000 = 30ms < 250 → RETURN! ← Bloke oldu
→ Crossfade tetikleme mantığı çalışmadı
→ Preload tetikleme mantığı çalışmadı
// Fix sonrası:
T=0ms : spotAudio.ontimeupdate → _lastTimeupdateSpot = 1000ms
T=30ms : audio.ontimeupdate → _lastTimeupdateMain bakıyor
→ _lastTimeupdateMain = 0 (hiç çalışmamış)
→ 30ms > 0ms → ÇALIŞTI ✅
İnternet kesilip geri geldiğinde sistem sadece şarkı kuyruğunu dolduruyor. Ama şu an çalmakta olan şarkıyı kurtarmıyor.
HLS.js, internet kesildiğinde segmentleri yüklemeye çalışır, defalarca başarısız olur, sonunda
fatal error durumuna geçip tamamen durur.
Müzik sessiz kalır. Online gelince sistem "queue doldu, hazırım" diyor ama HLS.js hâlâ dondurulmuş,
audio element çalmıyor. Sessizlik devam eder.
Bunun yanı sıra, offline süresince şarkının imzalı URL'si (token) süresi dolmuş olabilir. HLS.js uyansa bile 403/401 alır.
satır ~7598–7608
Fonksiyon: enableBackgroundPlayback()
içindeki window.addEventListener('online', ...) bloğu
window.addEventListener('online', () => {
this._networkOffline = false;
console.log('✅ Bağlantı geldi — queue yenileniyor');
// 1 saniye bekle (bağlantı stabilize olsun) sonra queue doldur
setTimeout(() => this.checkAndRefillQueue(), 1000);
// ← Bitti. Sadece bu kadar.
// ← HLS.js'e hiçbir şey söylenmiyor
// ← audio.play() çağrılmıyor
// ← Token yenileme yok
});
Müzik çalıyor. HLS.js segment yüklüyor. Her şey normal.
Wi-Fi kesildi. window 'offline' ateşlendi → _networkOffline = true. HLS.js segment isteği başarısız, retry başladı.
HLS.js 5 kez retry yaptı (errorRetry: maxNumRetry: 5). Hepsi başarısız. HLS.js fatal error durumuna geçti ve stopLoad() yaptı. Müzik sustu.
Şarkının imzalı token'ı 5–15 dakika içinde süre dolmasıyla geçersiz olabilir (sunucu konfigürasyonuna bağlı).
Wi-Fi geri geldi. window 'online' ateşlendi → _networkOffline = false → 1sn sonra checkAndRefillQueue().
checkAndRefillQueue() API'den yeni şarkılar çekti, queue doldu. Ama HLS.js hâlâ durdurulmuş. audio.play() çağrılmadı. Müzik çalmıyor.
Kafe görevlisi fark etti (ya da etmedi). El müdahalesi olmadan müzik kendi kendine başlamaz.
enableBackgroundPlayback() içindeki online handler
Mevcut window.addEventListener('online', ...) bloğunu tamamen şununla değiştir:
window.addEventListener('online', () => {
this._networkOffline = false;
console.log('✅ Bağlantı geldi — tam recovery başlatılıyor');
setTimeout(async () => {
// ── ADIM 1: HLS.js'i uyandır ──────────────────────────────
// Fatal error sonrası durmuş olabilir, startLoad ile tekrar başlat
if (this.hls) {
try {
this.hls.startLoad();
console.log('✅ HLS.js startLoad() çağrıldı');
} catch (e) {
console.warn('⚠️ HLS startLoad hatası:', e.message);
}
}
// ── ADIM 2: Audio element'i çal ───────────────────────────
// isPlaying=true ama audio duruyorsa (offline sırasında durdu)
const audio = this.getActiveHlsAudio?.();
if (audio && audio.paused && this.isPlaying) {
try {
await audio.play();
console.log('✅ Audio play() başarılı (online recovery)');
} catch (e) {
// Autoplay policy veya token expired → şarkıyı yeniden yükle
console.warn('⚠️ Audio play() başarısız, şarkı yeniden yüklenecek:', e.message);
if (this.currentSong) {
// refreshHlsStream varsa token yenile, yoksa playSong ile yeniden başlat
if (typeof this.refreshHlsStream === 'function') {
await this.refreshHlsStream();
} else {
// Şarkıyı kaldığı yerden yeniden başlat
const savedTime = this.currentTime || 0;
await this.playSong(this.queueIndex, true);
// İsteğe bağlı: kaldığı yerden devam et
// const a = this.getActiveHlsAudio?.();
// if (a && savedTime > 0) a.currentTime = savedTime;
}
}
}
}
// ── ADIM 3: Buffer boşsa şarkıyı yeniden yükle ───────────
// startLoad çalışsa bile buffer sıfır olabilir
const buffered = this.getBufferedAmount?.() ?? 0;
if (buffered < 2 && this.currentSong && !audio?.src) {
console.log('⚠️ Buffer boş, şarkı yeniden yükleniyor...');
if (typeof this.refreshHlsStream === 'function') {
await this.refreshHlsStream();
} else {
await this.playSong(this.queueIndex, true);
}
}
// ── ADIM 4: Queue doldur ──────────────────────────────────
// (mevcut mantık — değişmedi)
await this.checkAndRefillQueue();
}, 1500); // 1.5sn: bağlantı stabilize olsun (eskiden 1sn)
});
_networkOffline başlangıç değeri
Şu an Alpine store'da bu değişken tanımsız (undefined) başlıyor.
Sayfa internet kesiliyken açılırsa offline eventi hiç ateşlenmez ve flag set edilmez.
Küçük ama tamamlayıcı bir fix:
Konum: Alpine store tanımı içinde (satır ~756 civarı, diğer _ ile başlayan property'lerin yanına)
// MEVCUT — tanımsız, undefined olarak başlıyor _preloadedNext: null, _preloadNextInProgress: false, // ... _networkOffline YOK
// FIX — başlangıç değeri ekle _preloadedNext: null, _preloadNextInProgress: false, _networkOffline: !navigator.onLine, // ← bunu ekle
Kısa kesinti (30sn–2dk): Buffer'da hâlâ müzik var, HLS.js startLoad ile devam eder, audio.play() çağrılır → müzik durmadan devam eder.
Uzun kesinti (5–15dk): Buffer boş, token dolmuş. refreshHlsStream veya playSong yeni token ile şarkıyı yeniden yükler → müzik kaldığı yerden değil ama yeniden başlar.
Her iki durumda: El müdahalesi gerekmez. 16 saatlik kesintisiz çalma güvencesi sağlanır.