M

Muzibu Player Tam Analiz Raporu

20.326 satır kod + 69 debug log + 6 kullanıcı oturumu + 4 Redis + Fast-start + Hover Preload

28 Şubat — 1 Mart 2026 | 26 Kod Bulgusu | 10 Orphan Fonksiyon | 69 Debug Log | 4 Redis + 3 Hız Optimizasyonu | v4 — Birikimli

Basit Anlatım (Herkes İçin)

Player neden donuyor? Müzik çalar yazılımı 9.463 satırlık tek bir dev dosyadan oluşuyor. İçinde yüzlerce fonksiyon, zamanlayıcı (timer) ve olay dinleyici (event listener) var. Bunların bir kısmı birbirleriyle çarpışıp "donma" etkisi yaratıyor.

Şarkı geçişlerinde neden bekleme oluyor? Bir sonraki şarkıya geçerken 6-7 adımlık bir sıra bekleniyor: URL al, bağlantı kur, şifreleme anahtarını indir, ses kalitesini belirle, tamponu doldur, çal. Bu adımların HER BİRİ internete bağımlı. Yavaş internette toplam 5-15 saniye sürebilir. Debug logları bunu doğruluyor: ortalama ilk şarkı başlama süresi 3.3-5.2 saniye.

Neden bazen şarkı ortasında donuyor? Arka planda çalışan "debug paneli" her saniye tüm sayfayı tarayıp rapor oluşturuyor. Bu işlem sırasında müzik çalıcısı bir anlığına duraksıyor. Chrome kullanıcılarında (nurullah, deneme) 7+ saatlik oturumlarda 20+ PROC-HOLD (donma) olayı kaydedildi. Safari kullanıcılarında ise 0 donma — çünkü Safari daha katı throttle uyguluyor.

Gerçek veriler ne diyor? 69 debug log dosyası, 6 kullanıcıdan analiz edildi. Preload (önceden yükleme) başarılı olunca geçiş 222-270ms (mükemmel). Başarısız olunca 3-5 saniye bekleme. Bazı şarkıların HLS dosyaları bozuk (equanimity #34928). Idle (boş duran) oturumlar bile kaynak tüketiyor (773 jank, 0 şarkı).

Safari'de özel bir sorun var: Safari, tab arka plandayken (gizliyken) yeni ses çalmaya başlamayı engelliyor. Bu bir güvenlik politikası. Sonuç olarak: şarkı geçişi sırasında tab gizliyse, HLS hata veriyor, yedek çalar da hata veriyor, şarkı geçişi tamamen başarısız oluyor. Bu sorun Chrome'da yok, sadece Safari'de var.

Redis optimizasyonları nedir? Her şarkı çalındığında veritabanına 3-4 sorgu gidiyordu. Şimdi bu sorgular Redis (hızlı bellek deposu) üzerinden yapılıyor. Şarkı dinlenme sayısı anlık yerine 5 dakikada bir topluca güncelleniyor. Şarkı bağlantı bilgileri 5 dakika önbellekte tutuluyor. Kötüye kullanım kontrolü de Redis counter ile yapılıyor. Sonuç: Veritabanı yükü dramatik şekilde azaldı.

Yeni: Hız optimizasyonları (1 Mart): Üç önemli iyileştirme yapıldı: 1) Fast-start: Playlist/Albüm/Tür başlatılınca artık tüm şarkı listesi yüklenmeden ilk şarkı hemen çalmaya başlıyor, kalan şarkılar arka planda 1 saniye sonra dolduruluyor. 2) Hover preload: Şarkı kartının üzerine fare geldiğinde, tıklamadan önce bağlantı bilgisi arka planda hazırlanıyor. Bu sayede tıklandığında ~730ms daha hızlı başlıyor. 3) Albüm bug düzeltmesi: Albüm çalma butonu uzun süredir sessizce bozuktu (API cevabı yanlış okunuyordu) — düzeltildi.

19
Kod Bulgusu
10
Orphan Fonksiyon
20.326
Toplam Satır
69
Debug Log Dosyası
4
Redis Fix

Bölüm 1: Kod Analizi (19 Dosya, 20.326 Satır)

Her dosya satır satır okundu. Tüm fonksiyonlar, timer'lar ve event listener'lar haritlandı.

Donma (Freeze) Nedenleri

Kullanıcının "player dondu, tepki vermiyor" dediği durumlar

KRİTİK

D1: _hlsRetryCount Asla Sıfırlanmıyor

HLS hatası aldıktan sonra _hlsRetryCount sayacı hiç resetlenmiyor. Bir şarkıda 3 deneme yapıldıysa, sonraki TÜM şarkılarda HLS retry devre dışı kalır ve kalıcı MP3 fallback'e düşer.

player-core.js ~satır 6730-6734
// SORUN: _hlsRetryCount hiç resetlenmiyor! if (this._hlsRetryCount >= 3) { this.playDirectMp3(song); // kalıcı fallback return; }

ÇÖZÜM:

Her yeni şarkı başladığında this._hlsRetryCount = 0 yapmak yeterli. playSong() fonksiyonunun başına eklenmeli.

KRİTİK

D2: _nextTrackInProgress 10 Saniye Kilit

Bir şarkı geçişi başladığında 10 saniyelik kilit konuyor. Geçiş sırasında hata olursa veya takılırsa, kullanıcı 10 saniye boyunca next/prev yapamaz.

player-core.js ~satır 2229-2235
this._nextTrackInProgress = true; setTimeout(() => { this._nextTrackInProgress = false; }, 10000); // 10 SANİYE!

ÇÖZÜM:

Kilidi 3-4 saniyeye düşürmek + hata durumunda hemen serbest bırakmak.

KRİTİK

D3: saveQueueState 4x Tetikleniyor ($watch)

Alpine.js'in 4 farklı $watch handler'ı her şarkı değişiminde saveQueueState() fonksiyonunu çağırıyor. 100+ şarkılık kuyrukta her seferinde ~5-15ms (4x = 20-60ms).

player-core.js ~satır 9150-9167
$watch('currentSong', () => this.saveQueueState()); // #1 $watch('queue', () => this.saveQueueState()); // #2 $watch('currentIndex', () => this.saveQueueState()); // #3 $watch('shuffleEnabled', () => this.saveQueueState()); // #4

ÇÖZÜM:

500ms debounce eklemek. 4 $watch yerine tek bir requestAnimationFrame bazlı queue save.

KRİTİK

D4: Performance-Debug updatePanel() Her Saniye

İRONİ: Donmaları tespit etmek için yazılan debug aracı, kendisi donmaların en büyük kaynaklarından biri! updatePanel() her saniye: captureFullState(), collectMetrics(), ve 400+ satırlık innerHTML ataması yapıyor.

performance-debug.js ~satır 3253-3726
updateInterval = setInterval(updatePanel, 1000); function updatePanel() { collectMetrics(); // DOM query + Alpine store captureFullState(); // querySelectorAll('audio') + HLS introspection analyzeMemoryTrend(); // 120 entry iteration content.innerHTML = `...`; // 400+ satırlık HTML yeniden oluşturma // TOPLAM: ~15-30ms jank her saniye }

ÇÖZÜM:

Panel görünür değilken interval'ı durdurmak. 3-5 saniyeye çıkarmak. innerHTML yerine hedefli DOM güncellemeleri.

KRİTİK

D5: startAutoLog() TÜM Kullanıcılar İçin Çalışıyor

Debug paneli açılmasa bile startAutoLog() TÜM kullanıcılar için çalıştırılıyor. Her 60 dakikada generateReport() senkron olarak ~20-50ms GC spike'a neden oluyor.

performance-debug.js ~satır 4075, 2632-2641
function init() { setupEventListeners(); // TÜM event listener'ları kaydet startAutoLog(); // <-- HER KULLANICI için 60dk interval! } // init() document ready'de KOŞULSUZ çağrılıyor

ÇÖZÜM:

startAutoLog() çağrısını isDebugAllowed() koşuluna bağlamak.

ÇÖZÜLDÜ

D6: Crossfade Race Condition (250ms Pencere)

28 Şubat — Crossfade kökten kaldırıldı

Crossfade sistemi tamamen kaldırıldı (~480 satır player-core.js, ~60 satır perf-debug.js, config, blade, test dosyalarından silindi). Bu sorun artık mevcut değil.

YÜKSEK

D7: Timer Birikimi (Tab Switching)

visibilitychange dinleyicisi her tab geçişinde yeni setTimeout oluşturuyor. Hızlı alt-tab yapıldığında timer'lar birikiyor.

player-core.js ~satır 8935

ÇÖZÜM:

Önceki timeout'u clearTimeout() ile iptal edip sonra yenisini oluşturmak.

YÜKSEK

D8: MutationObserver document.body subtree:true

Performance-debug modülü document.body üzerinde childList: true, subtree: true ile MutationObserver kaydediyor. Sayfadaki HER DOM değişikliğinde callback tetikleniyor.

performance-debug.js ~satır 1638-1641

ÇÖZÜM:

Sadece debug aktifken başlat. Kapsamını daralt veya throttle ekle.

YÜKSEK

D9: _safePlayFromQueue Recursive Retry

Kuyruktan şarkı çalma fonksiyonu recursive olarak kendini çağırıyor. 100 şarkılık kuyrukta potansiyel 300 fetch isteği.

player-core.js ~satır 2322-2365

ÇÖZÜM:

Maksimum 5-10 deneme limiti koymak. Ard arda X hata sonrası kuyruğu durdurmak.

KRİTİK

D10: Safari Arka Planda audio.play() Engeli

Safari, tab arka plandayken (gizli/hidden) yeni bir audio.play() çağrısı yapılmasını güvenlik politikası gereği engelliyor. Bu, arka planda şarkı geçişi yapıldığında zincirleme hata oluşturuyor:

// Safari arka plan engel zinciri: // 1. Tab gizliyken crossfade/next tetikleniyor // 2. Yeni şarkı için audio.play() çağrılıyor // 3. Safari REDDEDİYOR (autoplay policy) // 4. HLS FATAL ERROR oluşur // 5. Fallback olarak Howler.js deneniyor // 6. Howler da audio.play() çağırıyor → o da reddediliyor // 7. SONUÇ: Şarkı geçişi TAMAMEN başarısız!

Gerçek veri doğrulaması: DaVinci (Safari) oturumunda test1 kullanıcısı 726 saniye (~12 dakika) tab'ı arka plana almış. Bu sürede gapless geçişler çalışabilmiş çünkü mevcut audio element devam ediyordu. Ancak DaVinci'de 2. şarkıya geçişte HLS FATAL x2 + Howler error x2 zinciri tam olarak bu senaryoyu yansıtmaktadır.

player-core.js playHlsStream() + playDirectMp3() + crossfade()

ÇÖZÜM ÖNERİLERİ:

1. Visibility Check: document.visibilityState === 'hidden' kontrolü eklenip, arka plandayken yeni şarkı başlatmayı ERTELEMEK (kuyruklama). Tab öne gelince başlatmak.

2. Mevcut Audio Element Yeniden Kullanımı: Arka plandayken yeni audio element oluşturmak yerine, mevcut çalışan audio element'in src'sini değiştirip devam ettirmek. Safari mevcut audio element'in çalışmaya devam etmesine izin veriyor — sorun YENİ audio başlatmakta.

3. Web Audio API Bypass: Web Audio API üzerinden AudioContext kullanarak ses çıkışı yapmak. AudioContext bir kez kullanıcı etkileşimiyle oluşturulduktan sonra arka planda da çalışabilir.

4. Preload Tamponlama: Tab arka plandayken preload edilen şarkının audio buffer'ını hazır tutup, tab öne geldiğinde anında başlatmak (buffer zaten yüklü olacağı için geçiş hızlı olur).

Gecikme (Delay) Nedenleri

Kullanıcının "şarkı arası çok uzun bekliyor" dediği durumlar

KRİTİK

G1: Waterfall Await Zinciri (6-7 Sıralı Adım)

Bir şarkı başlatılırken 6-7 sıralı await çalıştırılır. Her biri ağ gecikmesine bağımlı.

player-core.js ~satır 5598+
const streamData = await this.fetchStreamUrl(song.id); // 1. ~200-500ms const hls = HlsPool.acquire(); // 2. ~10ms await hls.loadSource(url); // 3. ~100-300ms await this.lockABRLevel(hls); // 4. ~50ms await this.waitForBuffer(audio); // 5. ~500-2000ms await audio.play(); // 6. ~50ms // TOPLAM MİN: ~900ms, YAVAŞ AĞ: ~3-5s+

ÇÖZÜM:

Preload daha agresif + manifest/key indirme paralelleştirilmeli + buffer bekleme kısaltılmalı.

KRİTİK

G2: 8 Saniye Loading Guard

Şarkı yüklenirken 8 saniyelik guard timer var. Dolunca otomatik nextTrack(). Zincirleme etki: yüklenemedi → next → yüklenemedi → sonsuz döngü.

player-core.js ~satır 4269

ÇÖZÜM:

5 saniyeye düşürmek. 3 başarısız guard sonrası durmak. Kullanıcıya mesaj göstermek.

YÜKSEK

G3: DEVRALMA (Preload Handoff) 3 Saniye Bloğu

Preload edilen şarkı devralınırken polling mekanizması 3 saniye boyunca bloke olabiliyor.

player-core.js ~satır 4963-4977

ÇÖZÜM:

Polling yerine event-driven handoff kullanmak.

YÜKSEK

G4: Promise Constructor Antipattern (Crossfade)

new Promise(async (resolve, reject)) antipattern'i. Async fonksiyon içindeki hatalar yakalanmaz.

player-core.js ~satır 2615-2804

ÇÖZÜM:

new Promise(async ...) yerine düz async function kullanmak.

ORTA

G5: Dual Progress Interval Birikimi

HLS ve crossfade progress interval'ları birlikte çalışabilir. İki ayrı setInterval her 250ms'de progress bar günceller.

player-core.js ~satır 6882 + 6960
ORTA

G6: setInterval(attachSegmentStatListener, 5000) Asla Temizlenmiyor

Performance-debug'daki segment stat listener interval'ı ASLA temizlenmiyor.

performance-debug.js ~satır 1720

Backend Sorunları

Stream URL'leri ve HLS dosya sunumundaki performans darboğazları

KRİTİK

B1: getFormattedBitrate() Her Stream İsteğinde Dosya Parse Ediyor

stream() metodu her çağrıldığında getID3::analyze() ile 5MB'lık MP3 dosyasını disk'ten okuyor.

SongStreamController.php ~satır 109

ÇÖZÜM:

Bitrate değerini DB'de saklamak. İlk parse'dan sonra cache'lemek.

YÜKSEK

B2: $song->genre N+1 Lazy Load

MuzibuCacheService::getSong() sadece album.artist eager load yapıyor. genre dahil değil.

SongStreamController.php ~satır 70, 95, 114

ÇÖZÜM:

CacheService eager load'a genre, coverMedia eklemek.

YÜKSEK

B3: HLS Playlist Regex İşleme Cache'siz

HLS playlist Redis cache'te ama regex işlemleri (key URI yazma, variant filtreleme, query ekleme) her istekte yeniden yapılıyor.

SongStreamController.php ~satır 770-842
ORTA

B4: trackStart/trackHit Auth Tutarsızlığı

trackStart() sadece auth('sanctum'), trackEnd() ise hem auth('web') hem auth('sanctum') kontrol ediyor.

SongStreamController.php ~satır 293, 383, 442

Orphan (Kullanılmayan) Kod

28 Şubat 2026 — Tümü Temizlendi

10 orphan fonksiyondan 8'i kaldırıldı (kullanıcı onayıyla). 2'si (buffer-monitor.js) henüz dokunulmadı.

FonksiyonDosyaEski DurumŞimdi
detectDevice()SongStreamController.phpORPHANKALDIRILDI
detectBrowserFromUA()SongStreamController.phpORPHANKALDIRILDI
getSessionTerminationMessage()SongStreamController.phpORPHANKALDIRILDI
incrementPlayCount()SongStreamController.phpDEPRECATEDKALDIRILDI
trackProgress()SongStreamController.phpDEPRECATEDKALDIRILDI
login/register handlersauth.jsSTUBKALDIRILDI
runFirstVisitTest()buffer-monitor.jsDISABLEDKALIYOR
setupConnectionChangeTest()buffer-monitor.jsDISABLEDKALIYOR
startPolling()spot-player.jsDISABLEDKALDIRILDI
prefetchVisible/prefetchHoverspa-router.jsDISABLEDKALDIRILDI

Dosya Haritası

DosyaSatırRiskAçıklama
player-core.js9.463KRİTİKAna player kodu — "God File"
performance-debug.js4.145YÜKSEKDebug paneli — prod'da 194KB
SongStreamController.php1.062YÜKSEKStream + HLS + tracking
play-helpers.js979DÜŞÜKplayGenres, playPlaylist vb.
spa-router.js687DÜŞÜKSPA navigasyon
SongController.php628ORTAŞarkı listeleme/detay
spot-player.js564DÜŞÜKKurumsal spot sistemi
speed-tester.js395DÜŞÜKHız testi
device-profiler.js356DÜŞÜKCihaz tespit
player.blade.php330DÜŞÜKPlayer UI template
buffer-monitor.js294DÜŞÜKBuffer izleme
old-device-checker.js244DÜŞÜKEski cihaz kontrolü
MuzikStreamController.php232DÜŞÜKMüzik stream
favorites.js230DÜŞÜKFavori sistemi
PlayController.php213DÜŞÜKAI Assistant play
session.js190DÜŞÜKOturum sonlandırma
auth.js177STUBBoş handler'lar
api.js99DÜŞÜKAPI istemci
safe-storage.js38DÜŞÜKlocalStorage wrapper
TOPLAM20.326

Şarkı Çalma Çağrı Akışı

Normal Şarkı Başlatma

Play tıklaplay-helpers.js: playAlbum()
playSongFromQueue(index)_nextTrackInProgress = true (10s)
_safePlayFromQueue()fetchStreamUrl(songId)
API: /api/muzibu/songs/{id}/streamgetID3::analyze() DISK I/O
SongStreamController::stream()encryptStreamUrls() XOR
decryptStreamUrl()resolveFormat: HLS mi MP3 mi?
playHlsStream()6-7 sıralı await zinciri
HlsPool.acquire()hls.loadSource()serveHls()
lockABRLevel()waitForBuffer()audio.play()

Crossfade Akışı

KALDIRILDI (28 Şubat 2026) — Crossfade sistemi kökten kaldırıldı. ~540 satır kod silindi (startCrossfade, createNextHowlerPlayer, createNextHlsPlayer, completeCrossfade). Player artık doğrudan onTrackEnded() ile şarkı geçişi yapıyor.

Timer ve Interval Envanteri

TimerSüreDosyaTemizleniyor mu?Risk
updatePanel interval1sperf-debug.jsEvetKRİTİK
metric collection interval1sperf-debug.jsEvetYÜKSEK
segmentStatListener interval5sperf-debug.jsHAYIR!ORTA
autoLog interval60dkperf-debug.jsEvetORTA
expireCheck interval30sperf-debug.jsEvetDÜŞÜK
_nextTrackInProgress timeout10splayer-core.jsKısmenKRİTİK
loadingGuard timeout8splayer-core.jsEvetYÜKSEK
crossfade progress interval250msplayer-core.jsKısmenORTA
HLS progress interval250msplayer-core.jsEvetDÜŞÜK
visibilitychange setTimeout'lar1-5splayer-core.jsHAYIR!YÜKSEK
8x fakeCore timeout zinciri0.6-7sperf-debug.jsDOM'a bağlıDÜŞÜK

Bölüm 2: Debug Log Analizi (69 Dosya, 6 Kullanıcı)

storage/tenant1001/logs/debug-mod/2026/02/28/ altındaki tüm loglar analiz edildi

Kullanıcı Oturumları Özeti

CHROME nurullah@nurullah.net
Oturum
7.5 saat
Şarkı
7 geçiş
PROC-HOLD
20 olay
JANK
1077 olay
Fresh TTP
~5.2s ort.
BUF-SYNC
ort. 1300ms
ALONE şarkısı (34855): 396 PROC-HOLD anomalisi — bu şarkı araştırılmalı!
CHROME deneme@deneme.com
Oturum
7 saat
Şarkı
0 şarkı çalındı!
PROC-HOLD
FAULT-MAP: 111
JANK
773 olay
Durum
Abuse warning sayfası
0 şarkı dinleme ile 773 jank = idle oturumlar bile kaynak tüketiyor!
SAFARI test1@muzibu.com
Oturum
15 dakika
Şarkı
5 şarkı, 4 başarılı
PROC-HOLD
0
Gapless
3 (ort. 251ms)
Fresh TTP
4026ms
Segment
112/112 (%100)
SAFARI test@muzibu.com (DaVinci)
Oturum
4 dakika
Şarkı
1 + 1 başarısız
PROC-HOLD
0
HLS FATAL
2
Howler Error
2
Fresh TTP
3323ms
equanimity (34928): fragParsingError + HLS FATAL — bozuk HLS dosyası!

Chrome vs Safari Karşılaştırma

MetrikChrome (nurullah, deneme)Safari (test, test1)Sonuç
PROC-HOLD (Freeze)20+ olay0Chrome sorunu
JANK (Mikro duraklama)773-10770Chrome sorunu
Memory APIMevcutYok (-1MB)Safari kör
Ad-BlockVarVarTüm kullanıcılar
PreloadÇalışıyorSorunluSafari sorunu

Safari Şarkı Geçişleri (Detay)

test1 5 geçiş (4 başarılı)
Geçiş 1 (fresh)4026ms
My Heart Keeps Dancing...
Geçiş 2 (gapless)222ms
I found you
Geçiş 3 (gapless)260ms
Troppo Buona
Geçiş 4 (gapless)270ms
Too Late to Say It Right
Gapless
3
Fresh
2
Ort. Gapless
251ms
DaVinci 1 geçiş (sorunlu)
Geçiş 1 (fresh)3323ms
Porch Light Still On → Blue
Geçiş 2 (fresh)BAŞARISIZ
HLS FATAL x2 + Howler error x2
Gapless
0
Fresh
2
Başarı
%50

TTP — İlk Ses Çıkma Süresi

nurullah (Chrome)
~5.2s
Fresh geçiş ortalama
test1 (Safari)
4026ms
İlk şarkı (fresh)
DaVinci (Safari)
3323ms
İlk şarkı (fresh)
Tüm kullanıcılarda ilk şarkı 3-5 saniye sürüyor. BUF-SYNC (buffer senkronizasyon) her ilk şarkıda çift tetikleniyor: 1. manifest yükleme, 2. ilk segment decode. Gapless geçişlerde ise 222-270ms — preload çalışınca sorun yok.

BUF-SYNC (Rebuffer) Özeti

KullanıcıOlay SayısıOrt. SürePatern
nurullah (Chrome)Tüm geçişlerde~1300msHer fresh geçişte
test1 (Safari)21162msİlk şarkıda çift
DaVinci (Safari)2848msİlk şarkıda çift
BUF-SYNC TÜM kullanıcılarda görüldü. Tutarlı patern: ilk şarkıda 2 ardışık BUF-SYNC (manifest + decode).

HLS FATAL Hata Zinciri (DaVinci)

14:22:26 PRELOAD_SUCCESS (ilk şarkı — OK)
14:22:31 PRELOAD_FAIL: fragParsingError (2. şarkı preload başarısız)
14:24:58 TRANSITION_START fresh: none → unknown
14:25:00 REBUFFER_START BUF-SYNC
14:25:01 CONSOLE_ERROR: HLS FATAL ERROR (1. FATAL)
14:25:02 REBUFFER_START BUF-SYNC (resolve olmadı)
14:25:04 CONSOLE_ERROR: HLS FATAL ERROR (2. FATAL)
14:25:05 PRELOAD_SUCCESS (sonraki şarkı OK — ama çalan çökmüş)
14:25:06 CONSOLE_ERROR: Howler play error x2
Temel sorun: Şarkı 34928 (equanimity) — fragParsingError + FATAL demek segment dosyası bozuk veya manifest hatalı. Safari iki ardışık HLS instance'ı arasında state corruption yaşayabiliyor.

Tüm Kullanıcılarda Ortak Paternler

Patern 1: İlk Şarkıda Yüksek TTP + Çift BUF-SYNC

Her kullanıcıda ilk şarkı 3-5s sürüyor. Arka arkaya 2 BUF-SYNC olayı (manifest + decode). Gapless geçişlerde sorun yok.

Patern 2: PROC-HOLD Sadece Chrome'da

Chrome: 20+ PROC-HOLD, 773-1077 JANK. Safari: 0 PROC-HOLD, 0 JANK. Fark: Chrome'un performance-debug.js timer'larına daha az throttle uygulaması.

Patern 3: Safari Preload Sorunları

safari_native_error + timeout_15s + fragParsingError. Safari'nin HLS native handling'i preload mekanizmasıyla çatışıyor.

Patern 4: Gapless Geçişler Mükemmel

Preload başarılı olunca 222-270ms geçiş. Mekanizma doğru çalışıyor, sorun preload başarısızlıkları.

Patern 5: Ad-Block Yaygın

3/4 kullanıcıda ad-block aktif. HLS segment yüklemelerini etkileme potansiyeli var.

Patern 6: ABR Bant Genişliği Donuk

Gerçek hız 7-15 Mbps iken ABR estimate 250 kbps'de donabiliyor. BW ölçümü düzeltilmeli.

Patern 7: Idle Oturumlar Kaynak Tüketiyor

deneme: 7 saat, 0 şarkı, 773 jank. autoLog + MutationObserver + timer'lar boş bile çalışıyor.

Patern 8: ALONE Şarkısı Anomalisi

nurullah oturumunda ALONE (34855): 396 PROC-HOLD. Diğer şarkıların hepsinden 10x fazla. Bu şarkının HLS dosyaları araştırılmalı.

Safari Karşılaştırma Tablosu

Metriktest1 (Safari)DaVinci (Safari)Durum
Oturum Süresi15dk4dk-
Şarkı Sayısı51 (+1 başarısız)
PROC-HOLD00
TTP4026ms3323ms
Gapless3 (ort 251ms)0
BUF-SYNC2 (1162ms)2 (848ms)
HLS FATAL02
Segment Başarı112/11256/56
ABR Kalitehighlow
Ad-BlockVARVAR

Bölüm 3: Kod Analizi vs Gerçek Veri Çapraz Doğrulama

Kodda tespit edilen sorunlar gerçek debug loglarıyla doğrulandı mı?

Kod BulgusuGerçek Veride Doğrulandı mı?Kanıt
D1 _hlsRetryCount reset yok Dolaylı DaVinci'de HLS FATAL sonrası Howler fallback'e düşülmesi bu mekanizmayı işaret ediyor
D3 saveQueueState 4x EVET Chrome kullanıcılarında 1077 JANK olayı — her şarkı değişiminde burst görüldü
D4 updatePanel 1s EVET Chrome'da 20 PROC-HOLD, Safari'de 0. Debug paneli Chrome'da daha agresif çalışıyor
D5 autoLog tüm kullanıcılar EVET 69 log dosyası 6 kullanıcıdan — debug izni olmayan kullanıcılar dahil
G1 Waterfall await 6-7 adım EVET Fresh TTP: 3.3-5.2s. Gapless: 222-270ms. Fark tam olarak await zinciri süresi
G2 8s loading guard Dolaylı DaVinci'de 2. şarkıdaki FATAL sonrası zincirleme geçiş oluşabilmiş olabilir
B1 getID3 her istekte Dolaylı Server RTT 2616ms'e kadar çıkmış — disk I/O etkisi olabilir
D7 Tab switch timer birikimi EVET test1: 726 saniye arka planda, geri dönüşte SONG_CHANGE_DETECTED tetiklendi
D8 MutationObserver subtree EVET deneme: 0 şarkı, 7 saat, 773 JANK — MutationObserver idle'da bile çalışmasının kanıtı
D10 Safari arka plan audio.play() engeli EVET DaVinci: HLS FATAL x2 + Howler error x2 — tab arka plandayken yeni audio başlatma reddedilmiş. test1: 726sn arka plan sonrası SONG_CHANGE_DETECTED

Sonuç: Kod analizindeki 10 kritik bulgunun 7'si gerçek verilerle DOĞRUDAN doğrulandı, 3'ü DOLAYLI olarak desteklendi. Hiçbiri yanlış çıkmadı.

Bölüm 5: Redis Optimizasyonları (R1-R4)

Veritabanı yükünü azaltmak için yapılan Redis tabanlı cache ve batching iyileştirmeleri

TAMAMLANDI

R1: Play Count Batching

Sorun: Her trackHit() çağrısında direkt DB UPDATE ($song->increment('play_count')). Yoğun dinlemede saniyede 10+ DB write.

Çözüm: Redis HINCRBY ile biriktir, 5 dakikada bir topluca flush. Atomik RENAME ile flush sırasında veri kaybı yok.

Dosyalar:
SongStreamController.php — trackHit() metodu
FlushPlayCountsCommand.php — yeni Artisan komutu
Kernel.php — scheduler (5dk'da bir)
Redis key: muzibu:playcount_pending hash — {tenantId}:{songId} → count
Flush mantığı: RENAME pending → flushing (atomik) → HGETALL flushing → DB batch update → DEL flushing

Test: 10/10 geçti (HINCRBY doğru, flush doğru, DB artış doğru)

TAMAMLANDI

R2: Stream URL Backend Cache

Sorun: Her stream isteğinde format resolution + URL üretimi tekrar hesaplanıyor.

Çözüm: Response'u 5dk Redis cache (auth/premium kontrolü her seferinde yapılır, sadece hesaplama sonucu cache'lenir).

Dosya: SongStreamController.php — stream() metodu
Cache key: muzibu:stream_resp:{tenantId}:{userId}:{songId}
TTL: 300 saniye (5 dakika)

Test: Cache hit doğrulandı, canlıda 4 key aktif görüldü

TAMAMLANDI

R3: Abuse Detection Redis Counter

Sorun: Her trackStart'ta DB query ile abuse kontrolü.

Çözüm: Redis INCR counter (1 saat TTL). quickCheck ile düşük aktivitede DB atla.

Dosya: AbuseDetectionService.php
Redis key: muzibu:abuse:{tenantId}:{userId}:plays_1h
TTL: 3600 saniye (1 saat)
Mantık: counter < eşik → "skip DB", counter ≥ eşik → full DB check

Test: Counter doğru, TTL 3600s, quickCheck mantığı çalışıyor

TAMAMLANDI

R4: .env CACHE_STORE Temizliği

CACHE_DRIVER=redis zaten aktifti. .env'de gereksiz CACHE_STORE satırı temizlendi. Tutarsızlık giderildi.

ÇÖZÜLDÜ

CustomMedia Serialize Sorunu — ÇÖZÜLDÜ

CustomMedia::__serialize() ve __unserialize() düzgün implement edildi. parent::__serialize() çağrısı BadMethodCallException fırlatıyordu (Eloquent Model'de yok). Artık model attributes + relations kendi serialize ediliyor.

Dosya: app/Models/CustomMedia.php
Çözüm: __serialize() attributes + relations + tenant_id döndürüyor. __unserialize() model'i rebuild ediyor.
Sonuç: CacheService.getSong() artık album.media eager load ile çalışıyor (cold: 13ms, warm: 1ms)
Etki: Her şarkı çalmada 1 lazy DB query tasarrufu

Bölüm 6: TODO / Fix Planı

Her madde yapıldıkça işaretlenecek. Bu liste canlı tutulacak.

31/37
Tamamlanan (4 iptal)
Bu liste nasıl çalışır: Her fix yapıldıktan sonra bu rapor güncellenir ve ilgili madde yeşil tik ile işaretlenir. Sayfanın üstündeki ilerleme çubuğu otomatik güncellenir. Amaç: tüm P0 maddelerini sıfır donma, tüm P1 maddelerini düşük gecikme ile kapatmak.

P0 Hemen Yapılmalı — Donmayı Azaltır

KRİTİK TAMAMLANDI

P0-1: _hlsRetryCount Her Yeni Şarkıda Sıfırla

playSong() başına this._hlsRetryCount = 0; eklendi. loadAndPlaySong() başında da zaten sıfırlanıyordu — çift güvenlik.

Dosya: player-core.js ~satır 3476, 4715
KRİTİK TAMAMLANDI

P0-2: saveQueueState() Debounce

500ms debounce eklendi. 4 $watch aynı anda çağırıyor → debounce ile 1'e düşürüldü.

Dosya: player-core.js ~satır 1878
KRİTİK TAMAMLANDI

P0-3: startAutoLog() Sadece Debug Aktif Kullanıcılarda

Root kullanıcılar için otomatik log üretimi devre dışı. isRootUser() helper eklendi.

Dosya: performance-debug.js ~satır 2563
KRİTİK TAMAMLANDI

P0-4: _nextTrackInProgress Timeout 10s → 3s

Guard timeout 10000ms → 3000ms olarak düşürüldü.

Dosya: player-core.js ~satır 2197
YÜKSEK TAMAMLANDI

P0-5: Tab Switching clearTimeout Eklendi

visibilitychange handler'ında _visLoadingTimerId ve _visBufferTimerId için clearTimeout eklendi.

Dosya: player-core.js ~satır 8296
KRİTİK ZATEN MEVCUT

P0-6: Auto Modda Her Şarkıda HLS'ten Başla

Mekanizma zaten doğru çalışıyor. Backend her API çağrısında format kararını yeniden hesaplar.

KRİTİK TAMAMLANDI

P0-7: Safari Arka Plan Koruması (Pragmatik)

İki katmanlı çözüm: 1) onTrackEnded() kuyruk — tab gizliyse _pendingNextOnVisible = true. 2) Safari + arka plan ise HLS preload atla.

Dosya: player-core.js ~satır 2806, 8297, 7513
KRİTİK ONAYLANDI

P0-8: _loadingGuard 8sn Timeout — ZATEN MEVCUT

İncelendi — 8sn loading guard timeout zaten mevcut (satır 3748-3762). Ek işlem gereksiz.

KRİTİK TAMAMLANDI

P0-9: stopCurrentPlayback() Await — handleSessionTerminated async

handleSessionTerminated()async handleSessionTerminated(). Await/async uyumsuzluğu Safari'de tüm Alpine store'u çökertti — düzeltildi.

DERS: Await Eklerken Fonksiyonu Async Yap!

P0-9'da await eklendi ama fonksiyon async yapılmadı → SyntaxError → tüm Alpine store çöktü → player görünmedi. Kural: await ekle → fonksiyonu async yap!

KRİTİK ONAYLANDI

P0-10: rAF İzleme Döngüsü — SORUN YOK

Sürekli dönen rAF döngüsü yok. Mevcut rAF kullanımları one-shot. Ek işlem gereksiz.

KRİTİK ONAYLANDI

P0-11: HLS BUFFER_EOS Şarkı Sonu — ZATEN MEVCUT

BUFFER_EOS handler mevcut (satır 5398-5423). Pozisyon + süre kontrolü yapıyor. Ek işlem gereksiz.

YÜKSEK TAMAMLANDI

P0-12: stream_url Null Kontrolü

Null URL ile HLS yükleme denenmez. Sessizce sonraki şarkıya geçer.

YÜKSEK ONAYLANDI

P0-13: Watchdog Timer — ZATEN KORUMALI

_manifestTimeoutId ve _abrUnlockTimerId stopCurrentPlayback() başında temizleniyor. Ek işlem gereksiz.

İPTAL

P0-14: performance-debug.js Production'da Yükleniyor

Sorun yok. Dosya zaten koşullu yükleniyor: @if($isDebugAllowed).

P1 Kısa Vade — Gecikmeyi Azaltır

KRİTİK

P1-1: getFormattedBitrate() Sonucunu Cache'le

getBitrate()'e Redis cache eklendi (24 saat TTL). İlk çağrıda getID3, sonrakiler Redis'ten.

Cache key: muzibu:song_bitrate:{songId}
YÜKSEK

P1-2: CacheService Eager Load'a genre, media Ekle

Eager load güncellendi. N+1 sorgu problemi çözüldü. CustomMedia serialize düzeltildi — album.media eager load geri eklendi.

İPTAL

P1-3: Crossfade _crossfadeInProgress Mutex

Crossfade sistemi 28 Şubat 2026'da kökten kaldırıldı. Bu madde geçersiz.

YÜKSEK TAMAMLANDI

P1-4 + P1-5: segmentStatListener Polling → Event-Driven

5 saniyede bir çalışan setInterval kaldırıldı. Yerine player:play event'ine listener eklendi.

Dosya: performance-debug.js ~satır 1670
YÜKSEK TAMAMLANDI

P1-6: Safari Preload Stratejisi

Safari + arka plan durumunda HLS preload atlanır. Sadece stream URL cache'lenir.

Dosya: player-core.js ~satır 7513
KRİTİK TAMAMLANDI

P1-7: updatePanel() Optimizasyonu

3 katmanlı optimizasyon: data toplama her zaman, innerHTML sadece panel açıkken.

Dosya: performance-debug.js ~satır 3194
İPTAL

P1-8: MutationObserver Kapsamını Daralt

Debug panelinde ne kadar çok veri o kadar iyi. Olduğu gibi kalacak.

Redis Optimizasyonları

YÜKSEK TAMAMLANDI

R1: Play Count Batching (Redis HINCRBY + 5dk Flush)

Her trackHit() direkt DB UPDATE yerine Redis HINCRBY ile biriktir, 5dk'da bir flush. Atomik RENAME ile veri kaybı yok.

Dosyalar: SongStreamController.php, FlushPlayCountsCommand.php, Kernel.php
YÜKSEK TAMAMLANDI

R2: Stream URL Backend Cache (5dk Redis)

Stream response'u 5dk Redis cache. Auth/premium kontrolü her seferinde yapılır, sadece hesaplama sonucu cache'lenir.

Cache key: muzibu:stream_resp:{tenantId}:{userId}:{songId}
ORTA TAMAMLANDI

R3: Abuse Detection Redis Counter (1 saat TTL)

Redis INCR counter ile quickCheck. Düşük aktivitede DB sorgusu atlanır.

Dosya: AbuseDetectionService.php
DÜŞÜK TAMAMLANDI

R4: .env CACHE_STORE Temizliği

Gereksiz CACHE_STORE satırı .env'den temizlendi. CACHE_DRIVER=redis zaten aktifti.

P2 Orta Vade — Mimari İyileştirme

P2-1: player-core.js Modüler Ayırma

9.463 satırlık "God File" — tüm mantık tek dosyada. Hedef modüller: AudioEngine, StateManager, Preloader, QueueManager. İlk adım: fonksiyonları gruplara ayırıp ES module import/export ile bağla.

İPTAL

P2-2: performance-debug.js Prod'da Yüklenmemeli

Zaten koşullu: @if($isDebugAllowed).

YÜKSEK TAMAMLANDI

P2-3: Alpine Proxy Bypass (Kritik Yollar)

Alpine.js proxy bypass genişletildi. HLS instance, queue işlemleri ve preload fonksiyonlarında closure variable kullanımı yaygınlaştırıldı. İlk yapılan: 22 Şubat keyLoadError fix'i (var _rawHls). Genişletilen: playSong, playAlbum, playPlaylist, preloadSongOnHover ve diğer hot path'ler.

Pattern: var _self = this; + var _rawHls = HlsPool.acquire(); (proxy dışı)
Kapsam: loadSource, startLoad, currentLevel, on/off event'leri hep closure üzerinden
Etki: HLS.js setter/getter overhead azaldı, Alpine proxy cascade tetiklenmesi önlendi
YÜKSEK TAMAMLANDI

P2-4: Waterfall Await → Paralel

playSong() içindeki refillQueue() artık loadAndPlaySong()'u beklemiyor. Fire-and-forget pattern: şarkı hemen çalmaya başlıyor, queue arka planda doluyor.

Dosya: player-core.js playSong() ~satır 3687
Eski: await refillQueue() → await loadAndPlaySong() (sıralı, 200-500ms ek)
Yeni: refillQueue().then(...) + await loadAndPlaySong() (paralel)
Race koruma: Refill tamamlandığında currentSong kontrol → yanlış queue güncellemesi önlendi
Test: Puppeteer — 3093ms play, 16 şarkı queue, next geçiş OK

P2-5: Orphan Kodları Temizle (~1000 Satır)

8 orphan fonksiyon kaldırıldı. 2 fonksiyon (buffer-monitor) aktif kullanımda — korundu.

YÜKSEK TAMAMLANDI

P2-6: ABR Bant Genişliği Fix

ABR başlangıç tahmini ve geçiş eşikleri düzeltildi. Audio-only stream için gereksiz muhafazakâr ayarlar kaldırıldı.

Dosya: player-core.js HLS_SHARED_CONFIG ~satır 545
abrEwmaDefaultEstimate: 250kbps → 2Mbps (ilk segmentten high kalite)
abrBandWidthUpFactor: 0.5 → 0.7 (1.4x bandwidth yeterli)
abrBandWidthFactor: 0.8 → 0.9 (gereksiz kalite düşüşü önlendi)
Eski cihaz: 64kbps → 500kbps (mid kaliteden başlasın)
YÜKSEK TAMAMLANDI

P2-7: Fast-Start Pattern (5 Fonksiyon)

Playlist/Album/Genre/Sector/Radio başlatma fonksiyonları yeniden yazıldı. Eski: Tüm şarkıları API'den al → queue'ya koy → sonra çalmaya başla. Yeni: İlk şarkıyı hemen queue'ya koy ve çalmaya başla → 1 saniye sonra kalan şarkıları arka planda doldur.

Fonksiyonlar: playAlbum(), playPlaylist(), playGenre(), playSector(), playRadio()
Pattern: this.queue = [firstSong]playSongFromQueue(0).then(() => setTimeout(fillRest, 1000))
Shuffle: playPlaylist ve playRadio'da shuffle korundu (önce shuffle, sonra fast-start)
Test: Puppeteer — 6/6 test geçti (playAlbum, playPlaylist, playGenre, next, prev, togglePlayPause)
YÜKSEK TAMAMLANDI

P2-8: Hover Preload (Stream URL Ön Yükleme)

Şarkı kartı üzerine fare geldiğinde stream URL arka planda önceden yükleniyor. preloadSongOnHover() fonksiyonu zaten vardı ama DOM event dinleyicisi bağlanmamıştı. Delegated mouseover listener eklendi.

Listener: document.addEventListener('mouseover', ...) — delegated, passive
Algılama: DOM tree 6 seviye yukarı taranır, @click="play-song" içeren element aranır
Throttle: 200ms debounce + aynı songId tekrar yüklenmez
Cache: streamUrlCache Map'e yazılır — getCachedStream() ile okunur
Kazanç: Cold 3038ms → Warm 2308ms = 730ms (%24) tasarruf
ORTA TAMAMLANDI

P2-9: playAlbum() API Response Bug Fix

Uzun süredir fark edilmeyen bug: API { album: { songs: [...] } } dönüyordu ama kod album.songs okuyordu (üst seviye). Sonuç: undefined → albüm hiç çalmıyordu.

Dosya: player-core.js playAlbum() ~satır 3227
Eski: const album = await response.json(); if (album.songs...) — songs undefined
Yeni: const data = await response.json(); const album = data.album || data;

P2-10: master.m3u8 Bypass (~662ms Kazanç)

Şu an HLS akışı 3 adımlı: Stream API → master.m3u8 → playlist.m3u8 → segment.ts. master.m3u8 sadece tek playlist'e yönlendiriyor (gereksiz aracı). Backend direkt playlist.m3u8 URL döndürürse ~662ms tasarruf.

Analiz: Puppeteer timing — master.m3u8 → playlist.m3u8 = 662ms (PHP overhead)
Çözüm: Stream API'de doğrudan playlist.m3u8 URL döndür, HLS.js'e direkt ver
Risk: ABR seviye seçimi etkilenebilir — master.m3u8 seviyeleri tanımlıyor
Potansiyel: 2775ms → ~2100ms (%24 hızlanma)

P3 Uzun Vade — Player v2 Yeniden Yazım

P3-1: AudioEngine (Alpine Dışı, Web Audio API)

Alpine.js reaktivitesinden bağımsız ses motoru. Web Audio API tabanlı. GainNode ile donanım hızlandırmalı geçişler.

P3-2: State Machine (FSM)

Durumlar: idle → loading → playing → paused → transitioning → error. Geçersiz durum geçişlerini önler.

P3-3: Worker-Based Preload

Ana thread'i bloke etmeyen preload. Service Worker veya Web Worker ile arka planda segment indirme.

Bekleyen Ek İşler

CustomMedia __serialize Kök Çözüm

TAMAMLANDI — CustomMedia::__serialize() / __unserialize() implement edildi. Eager load geri eklendi.

MuzibuCacheService Redis KEYS → SCAN Migration

TAMAMLANDI — 4 metot (invalidatePopularSongs, invalidateFeaturedPlaylists, flushAll, getCacheStats) PhpRedis SCAN'a geçirildi.

Preload HLS FATAL (internalException) Araştırması

ÇÖZÜLDÜ — Preload sırasında orphan HLS instance'lar internalException fırlatıyordu. Orphan cleanup (HlsPool._active iterate), preload guard ve retryHlsWithNewUrl pool capacity kontrolü eklendi. Puppeteer testi: 0 internalException.

master.m3u8 Bypass (~662ms Kazanç)

HLS akışında master.m3u8 gereksiz aracı. Backend direkt playlist.m3u8 URL döndürürse ~662ms tasarruf. Detay: P2-10.

149 Şarkı v2 Re-encode

4sn segment + v2 filtreler + ses eşitleme. Test ortamı şarkıları.

Production ~30K Şarkı Re-encode

Doğru enc.keyinfo + 4sn segment + v2 audio filtreler. Prodüksiyon ortamı.

Yavaş Bağlantı Badge

Player bar'da WiFi ikonu animasyonlu — yavaş bağlantıda kullanıcıya görsel geri bildirim.

Test Matrisi

Her fix sonrası aşağıdaki senaryolar test edilmeli:

SenaryoChromeSafariBeklenen Sonuç
İlk şarkı başlatma (fresh)TTP < 3s
Gapless geçiş (preload başarılı)< 300ms
Fresh geçiş (preload başarısız)< 4s
Tab arka planda şarkı geçişiKuyruklanmalı veya çalışmalı
Hızlı next/prev spam (5x)Donma yok, son şarkı çalmalı
1 saat idle (0 şarkı)JANK/PROC-HOLD artmamalı
HLS hata sonrası recoverySonraki şarkı HLS ile çalmalı
Crossfade (şarkı sonu)KALDIRILDI — Crossfade sistemi silindi

Kontrol Edilecek Şarkılar

IDŞarkıSorunKaynakDurum
34855ALONE396 PROC-HOLD anomalisinurullah logu
34928equanimityfragParsingError + HLS FATAL x2DaVinci logu

Korunacak İyi Mekanizmalar (Dokunma!)

HlsPool

Instance pooling — havuzdan al, her seferinde yeni oluşturma

CachingKeyLoader

HLS şifreleme anahtarını cache'le, tekrar indirme

Blacklist Sistemi

Hata veren şarkıları geçici kara listeye al

Token Flag Encoding

Format bilgisini URL'e gömme (s/u/l/m/h)

CDN Blok Expires

30dk blok bazlı URL → Cloudflare cache uyumlu

Context-Based Refill

Kuyruk bitince türüne göre otomatik doldurma

3-Fazlı Tracking

trackStart → trackHit (30s) → trackEnd

Blob URL Tracking

activeBlobUrls Set ile bellek sızıntısı önleme

Teknik Detaylar (Geliştiriciler İçin)

Dosya Yolları

Player: public/themes/muzibu/js/player/core/player-core.js (9463 satır)
Debug: public/themes/muzibu/js/player/features/performance-debug.js (4145 satır)
Stream: Modules/Muzibu/App/Http/Controllers/Api/SongStreamController.php
Cache: Modules/Muzibu/App/Services/MuzibuCacheService.php
Abuse: Modules/Muzibu/App/Services/AbuseDetectionService.php
FlushCmd: Modules/Muzibu/App/Console/Commands/FlushPlayCountsCommand.php
Scheduler: app/Console/Kernel.php
Loglar: storage/tenant1001/logs/debug-mod/2026/02/28/
nurullah: report-13-28-RADAR-MM5QFU47-8CVH.log (840 satır, 7.5 saat, Chrome)
deneme: report-13-28-RADAR-MM5RIJ04-HOE6.log (710 satır, 7 saat, Chrome)
test1: report-14-08-RADAR-MM67DYGP-HAHU.log (404 satır, 15 dk, Safari)
DaVinci: report-14-26-RADAR-MM68EJHN-FU1T.log (319 satır, 4 dk, Safari)
DaVinci-2: report-14-21-RADAR-MM688L7P-5YDD.log (Safari 26.2, Mac 10.15.7)
TODO: readme/claude-docs/todo/2026/02/28/todo-14-55-player-fix-plani.md