E Grubu: Şarkılar arası yumuşak geçiş sistemi
Crossfade, bir şarkı biterken sonraki şarkının yavaşça başlaması (ses geçişi) özelliğidir. Radyo yayınlarındaki gibi kesintisiz müzik deneyimi sağlar. Kod tamamen yazılmış ama kapalı durumda. Daha önce HLS şifreleme hatasını (keyLoadError) çözmemiz gerekiyordu — o hata artık çözüldü. Ancak açmadan önce birkaç hata düzeltilmeli.
// player-core.js:888-891
crossfadeEnabled: false, // Ana anahtar
crossfadeDuration: 0, // Süre sıfır!
Duration 0 olduğu sürece tetikleme koşulu (timeRemaining <= 0)
asla sağlanamaz
// config.php:20
'crossfade_duration' => 7000 // 7 saniye
// app.blade.php:537
crossfadeDuration: {{ config(..., 4000) }}
// → window.PLAYER_CONFIG.crossfadeDuration = 7000
AMA player-core.js'te 0 ile override!
| Fonksiyon | Satır | Görevi |
|---|---|---|
| startCrossfade() | 2500-2685 | Ana orkestratör — geçişi başlatır |
| createNextHowlerPlayer() | 2688-2718 | MP3 için 2. Howler instance |
| createNextHlsPlayer() | 2720-2864 | HLS için 2. HLS.js + audio element |
| completeCrossfade() | 2867-2972 | Eski player'ı temizle, yeni player'ı ata |
| fadeAudioElement() | 6525-6555 | HTML5 audio ses animasyonu (rAF) |
| startProgressTrackingWithElement() | 6613-6637 | Crossfade'e özel progress takibi |
| HlsPool.* | 549-678 | HLS.js instance havuzu (2 slot) |
| performance-debug.js | 1081-1091 | XMIX izleme paneli |
Basit: Performans paneli crossfade'in başladığını/bittiğini hiç görmüyor. Sayaç hep 0 kalıyor.
Teknik: performance-debug.js satır 1081-1091'de
player:crossfadeStart ve player:crossfadeEnd event'leri dinleniyor.
Ancak player-core.js'te bu event'ler hiçbir yerde dispatch edilmiyor.
// performance-debug.js:1081 — DİNLİYOR ama tetikleyen YOK!
window.addEventListener('player:crossfadeStart', function(e) {
history.crossfadeCount++; // Asla artmaz!
});
startCrossfade()'de dispatch başlangıç,
completeCrossfade()'de dispatch bitiş event'i eklenecek.
Basit: Sunucuda 7 saniye ayarlanmış ama player bunu görmezden geliyor, 0 kullanıyor. 0 saniye = crossfade asla tetiklenemez.
Teknik: config.php'da crossfade_duration: 7000,
app.blade.php'de PLAYER_CONFIG.crossfadeDuration: 7000 olarak inject ediliyor.
Ama player-core.js:891'de crossfadeDuration: 0 hardcode — config değeri override ediliyor.
// ŞU AN: Hardcode 0 — config'i yok sayıyor!
crossfadeDuration: 0,
// OLMASI GEREKEN: Config'den al
crossfadeDuration: window.PLAYER_CONFIG?.crossfadeDuration || 7000,
Basit: MP3 ile crossfade sırasında hata olursa müzik duruyor, kendi kendini kurtaramıyor. HLS'de bu sorun yok.
Teknik: createNextHowlerPlayer() satır 2711-2712'de
onloaderror sadece console.error yapıyor.
HLS tarafında fatal error → playSongFromQueue() ile otomatik skip var.
onloaderror'da
isCrossfading = false + playSongFromQueue(nextIndex) eklenmeli.
Basit: Tarayıcı başka sekmede iken crossfade tamamlanması 1 dakikaya kadar gecikebilir. Eski şarkı susturulmuş ama bellek tüketiyor.
Teknik: Satır 2641'de setTimeout(..., crossfadeDuration) kullanılıyor.
Chrome background tab'da setTimeout'u 1dk'ya kadar throttle eder. Eski HLS instance + audio element
1dk boyunca serbest bırakılmaz.
completeCrossfade() hemen çağrılmalı (fade zaten atlanıyor, neden bekleyelim?).
Basit: Crossfade sırasında dinleme süresi limiti (play-limits) çalışmıyor.
Teknik: startProgressTracking('hls') satır 6587-6596'da
player:timeupdate event'i dispatch ediyor. Ama crossfade'e özel
startProgressTrackingWithElement() satır 6613-6637'de bu dispatch yok.
Basit: Crossfade ile geçilen şarkılarda 30 saniyelik dinleme sayacı (hit +1) çalışmıyor.
Teknik: startProgressTracking('hls') satır 6598-6602'de 30s sonra
trackSongPlay() çağrılıyor. startProgressTrackingWithElement()'te bu kontrol yok.
Crossfade sonrası completeCrossfade() startProgressTrackingWithElement()
çağırıyor (satır 2943) — sonra startProgressTracking('hls')'e geçişte (satır 2943 aslında element versiyonu)
bu kayıp oluyor.
startProgressTrackingWithElement() içine
timeupdate dispatch ve playTracked kontrolü eklenmeli. Aslında en temiz çözüm:
completeCrossfade()'de startProgressTracking('hls') kullanmak
(getActiveHlsAudio() zaten doğru element'i döndürüyor).
Satır 2605: const self = this; tanımlanıyor ama kullanılmıyor
(arrow function içinde this zaten doğru). Üst kapsamda da satır 2528'de
const self = this; var — shadowed variable. Hata değil ama karışıklık yaratıyor.
queueIndex ve currentSong hem startCrossfade() satır 2575-2576'da
hem de completeCrossfade() satır 2959-2960'da set ediliyor. İkincisi gereksiz çünkü değerler
zaten aynı. Ancak güvenlik amaçlı (savunmacı programlama) bırakılabilir.
Satır 2605: const self = this; — Arrow function içinde gereksiz (silinmeli)
Satır 2959-2960: queueIndex ve currentSong çift ataması
— startCrossfade'de zaten set ediliyor (savunmacı kod, bırakılabilir)
Satır 2964: updatePlayerColors() çift çağrı
— startCrossfade satır 2585'te de çağrılıyor. completeCrossfade'deki kaldırılabilir
(zaten aynı şarkı bilgisi)
Şarkı 4:00, crossfadeDuration: 7000ms (7 saniye). currentTime = 3:53'e geldiğinde tetiklenir.
startProgressTracking: timeRemaining = 7s ≤ 7s → startCrossfade() tetiklenir
Guard: isPlaying ✓, isCrossfading=false ✓, hasActiveHls ✓, nextIndex ≠ -1 ✓
Preload cleanup: Varsa temizle (crossfade kendi instance'ını kullanacak)
isCrossfading = true — tekrar tetiklenme önlenir
getCachedStream: Preload sayesinde URL cache'de → anında alınır
createNextHlsPlayer: HlsPool.acquire() → hlsNext oluştu
prefetchHlsKey → ABR lock → loadSource → attachMedia → MEDIA_ATTACHED → startLoad
MANIFEST_PARSED: nextAudio.play() → fadeAudioElement(0 → volume, 7s)
UI güncelle: queueIndex, currentSong, currentTime=0, progressPercent=0, trackSongStart()
Eski player fade-out: fadeAudioElement(volume → 0, 7s) — iki şarkı aynı anda çalıyor!
completeCrossfade: Eski HLS release → swap (hlsNext → hls) → yeni progress tracking → preloadNextSong()
Sonuç: Sorunsuz geçiş. 7 saniye boyunca iki şarkı üst üste çalar, yumuşak ses geçişi sağlanır.
document.hidden = true → isBackgroundTab = true
Yeni audio: nextAudio.volume = targetVolume (fade yok, direkt set)
Eski audio: audio.volume = 0 (fade yok, direkt mute)
setTimeout(completeCrossfade, 7000) — Chrome throttle: gerçekte ~60 saniye sonra çalışabilir!
1 dakika boyunca: Eski HLS instance + audio element bellekte duruyor (volume=0 ama aktif). HlsPool'da 2 slot dolu → yeni crossfade başlatılamaz.
Sorun: Background'da fade yapılmıyor (doğru) ama completeCrossfade gecikiyor (yanlış). Bellek sızıntısı ve HlsPool tıkanması riski.
Kullanıcı pause'a basıyor
crossfadeTimeoutId iptal: clearTimeout → 5s sonra completeCrossfade tetiklenmeyecek
completeCrossfade hemen çağrılıyor: Swap yapılır, yeni şarkı aktif olur
Tüm audio pause: hlsAudio + hlsAudioNext pause, event listener temizliği
Sonuç: Doğru çalışıyor. Play'e basınca yeni şarkıdan devam eder.
startCrossfade: isCrossfading = true, createNextHlsPlayer başlatılıyor
HLS Fatal Error: bufferAppendError veya mediaError
Auto-recover: taint(hlsNext) → release(hlsNext) → safeAudioCleanup → isCrossfading=false
playSongFromQueue(nextIndex): 100ms gecikme sonra direkt geçiş (fade yok ama müzik devam eder)
Sonuç: HLS hatalarında iyi kurtarma mekanizması var. Müzik kesilmez, sadece keskin geçiş olur.
8 sorunu düzeltip crossfade'i açacağız. İşler 3 aşamada yapılacak: Önce kritik hataları düzelt (2 adet — bunlar olmadan çalışmaz), sonra orta seviye sorunları düzelt (4 adet — çalışır ama eksik kalır), son olarak temizlik yap ve aç. Her aşamadan sonra test edeceğiz.
player-core.js satır 891
// ESKİ:
crossfadeDuration: 0,
// YENİ:
crossfadeDuration: window.PLAYER_CONFIG?.crossfadeDuration || 7000,
player-core.js satır 888-890
// ESKİ:
// 🚫 CROSSFADE DEVRE DIŞI — HLS keyLoadError çözülmeden açılamaz
// E grubu hazır ama HLS key sorunu önce düzeltilmeli
crossfadeEnabled: false,
// YENİ:
// 🎵 CROSSFADE: Şarkılar arası yumuşak ses geçişi (E grubu)
crossfadeEnabled: true,
startCrossfade() başına + completeCrossfade() sonuna
// startCrossfade() — isCrossfading = true'dan hemen sonra:
window.dispatchEvent(new CustomEvent('player:crossfadeStart', {
detail: {
fromSong: this.currentSong?.song_id,
toSong: nextSong?.song_id,
duration: this.crossfadeDuration
}
}));
// completeCrossfade() — isCrossfading = false'dan hemen sonra:
window.dispatchEvent(new CustomEvent('player:crossfadeEnd'));
createNextHowlerPlayer() satır 2711-2713
onloaderror: function(id, error) {
console.error('Howler load error (crossfade):', error);
// AUTO-RECOVER: Crossfade başarısız, direkt geçiş
self.isCrossfading = false;
self.crossfadeNextIndex = -1;
if (self.howlNext) {
try { self.howlNext.unload(); } catch(e) {}
self.howlNext = null;
}
const idx = self.getNextSongIndex();
if (idx >= 0) setTimeout(() => self.playSongFromQueue(idx), 100);
}
startCrossfade() satır 2641 civarı
// Background'daysa fade yok, beklemek de gereksiz!
if (isBackgroundTab) {
// Hemen tamamla (setTimeout throttle riski yok)
this.completeCrossfade(nextIndex, nextIsHls);
} else {
this.crossfadeTimeoutId = setTimeout(() => {
this.crossfadeTimeoutId = null;
this.completeCrossfade(nextIndex, nextIsHls);
}, this.crossfadeDuration);
}
timeupdate dispatch + playTracked kontrolü ekle
// startProgressTrackingWithElement() interval içine ekle:
// player:timeupdate dispatch
if (Math.floor(audioElement.currentTime) !== self._lastDispatchedSecond) {
self._lastDispatchedSecond = Math.floor(audioElement.currentTime);
window.dispatchEvent(new CustomEvent('player:timeupdate', {
detail: { currentTime: Math.floor(audioElement.currentTime), isLoggedIn: self.isLoggedIn }
}));
}
// 30s playTracked kontrolü
if (!self.playTracked && audioElement.currentTime >= self.playTrackedAt
&& self.currentSong && self.isLoggedIn) {
self.playTracked = true;
self.trackSongPlay(self.currentSong.song_id);
}
3A. Gereksiz const self = this; kaldır (satır 2605)
3B. Çift updatePlayerColors() çağrısını kaldır (satır 2964)
3C. ABA Test — 3 şarkılık crossfade döngüsü testi
| Risk | Olasılık | Etki | Önlem |
|---|---|---|---|
| Bellek sızıntısı (iki HLS instance) | Orta | Orta | HlsPool destroy+fresh zaten çözüyor. Background fix ek koruma. |
| Audio glitch (eşzamanlı 2 ses) | Düşük | Düşük | requestAnimationFrame tabanlı fade + volume clamp |
| HLS decryptdata race condition | Düşük | Yüksek | ABR lock FRAG_BUFFERED'a kadar + key prefetch + destroy+fresh |
| Mobil cihaz performansı | Orta | Orta | Düşük bellekli cihazlarda crossfade devre dışı bırakılabilir (gelecek) |
| Ağ bant genişliği (2x stream) | Orta | Düşük | HLS ABR adapte olur, soft mode zaten düşük bitrate |
| # | İş | Dosya | Öncelik | Zorluk |
|---|---|---|---|---|
| 1A | crossfadeDuration config'den al | player-core.js:891 | KRİTİK | Kolay |
| 1B | crossfadeEnabled = true | player-core.js:890 | KRİTİK | Kolay |
| 2A | Event dispatch ekle | player-core.js:2527, 2967 | ORTA | Kolay |
| 2B | Howler hata kurtarma | player-core.js:2711 | ORTA | Kolay |
| 2C | Background tab hemen completeCrossfade | player-core.js:2641 | ORTA | Kolay |
| 2D | progressTrackingWithElement iyileştir | player-core.js:6625 | ORTA | Orta |
| 3A | Gereksiz self kaldır | player-core.js:2605 | DÜŞÜK | Kolay |
| 3B | Çift updatePlayerColors kaldır | player-core.js:2964 | DÜŞÜK | Kolay |
| 3C | ABA Test (crossfade döngüsü) | - | TEST | - |
Her tetikleme noktası crossfadeEnabled && timeRemaining <= (crossfadeDuration/1000) && timeRemaining > 0 && !isCrossfading
kontrolü yapar. Bu çoklu tetikleme "son şans" güvenlik ağıdır — birden fazla yerde kontrol edilerek
şarkı sonu kaçırılması engellenir.
| # | Kaynak | Satır | Path |
|---|---|---|---|
| 1 | startProgressTracking('hls') | 6604-6607 | Ana HLS interval |
| 2 | startProgressTrackingWithElement() | 6630-6633 | Crossfade sonrası HLS |
| 3 | Fresh stream ontimeupdate | 6051-6055 | HLS.js fresh |
| 4 | Fresh stream onended | 6068-6072 | Son şans: HLS.js |
| 5 | Preloaded Safari ontimeupdate | 4542-4546 | Safari native preload |
| 6 | Preloaded Safari onended | 4552-4554 | Safari native son şans |
| 7 | Preloaded HLS.js ontimeupdate | 4731-4735 | Preloaded HLS.js |
| 8 | Preloaded HLS.js onended | 4741-4743 | Preloaded son şans |
| 9 | Preloaded BUFFER_EOS | 4675-4677 | Buffer sonu |
| 10 | Fresh stream BUFFER_EOS | 5736-5738 | Fresh buffer sonu |
| 11 | completeCrossfade ontimeupdate | 2936-2937 | Crossfade sonrası handler |
| 12 | Howler MP3 onend | 5374-5377 | MP3 son şans |
| 13 | Safari ontimeupdate | 6270-6274 | Safari native |
| 14 | Safari trackEndFallback | 6307-6310 | Safari 0.5s fallback |
| 15 | Safari onended | 6329-6331 | Safari son şans |