M

Muzibu Player Tam Analiz Raporu

20.326 satir kod, 19 dosya, satir satir okundu

28 Subat 2026 | 18 Kritik Bulgu | 10 Orphan Fonksiyon | 6 Kategori

Basit Anlatim (Herkes Icin)

Player neden donuyor? Muzik calar yazilimi 9.463 satirlik tek bir dev dosyadan olusuyor. Icinde yuzlerce fonksiyon, zamanlayici (timer) ve olaydinleyici (event listener) var. Bunlarin bir kismi birbirleriyle carpisip "donma" etkisi yaratiyor.

Sarki gecislerinde neden bekleme oluyor? Bir sonraki sarkiya gecerken 6-7 adimlik bir sira bekleniyor: URL al, baglanti kur, sifreleme anahtarini indir, ses kalitesini belirle, tamponu doldur, cal. Bu adimlarin HER BIRI internete bagimli. Yavas internette toplam 5-15 saniye surebilir.

Neden bazen sarki ortasinda donuyor? Arka planda calisan "debug paneli" her saniye tum sayfayi tarayip rapor olusturuyor. Bu islem surasinda muzik calicisi bir anligina duraksiyor. Ayrica 4 farkli "gozlemci" ayni anda kuyruk durumunu kaydetmeye calisiyor - bu da gereksiz yuk olusturuyor.

Ne yapilmali? Asagida 18 kritik sorun detayli aciklaniyor. En onemlisi: dev dosyayi parcalamak, gereksiz timer'lari temizlemek ve gecikmeli adimlari paralellestirmek.

18
Kritik Bulgu
10
Orphan Fonksiyon
20.326
Toplam Satir
19
Dosya

Donma (Freeze) Nedenleri

Kullanicinin "player dondu, tepki vermiyor" dedigi durumlar

KRITIK

D1: _hlsRetryCount Asla Sifirlanmiyor

HLS hatasi aldiktan sonra _hlsRetryCount sayaci hic resetlenmiyor. Bir sarkida 3 deneme yapildiysa, sonraki TUM sarkilarda HLS retry devre disi kalir ve kalici MP3 fallback'e duser.

player-core.js ~satir 6730-6734
// SORUN: _hlsRetryCount hic resetlenmiyor! // Bir sarkida hata alindiktan sonra // tum sonraki sarkilar direkt MP3'e duser if (this._hlsRetryCount >= 3) { this.playDirectMp3(song); // kalici fallback return; }

COZUM:

Her yeni sarki basladiginda this._hlsRetryCount = 0 yapmak yeterli. playSong() fonksiyonunun basina eklenmeli.

KRITIK

D2: _nextTrackInProgress 10 Saniye Kilit

Bir sarki gecisi basladiginda 10 saniyelik kilit konuyor. Gecis sirasinda hata olursa veya takilirsa, kullanici 10 saniye boyunca next/prev yapamaz. "Player dondu" hissinin en buyuk kaynaklarindan biri.

player-core.js ~satir 2229-2235
// 10 saniyelik kilit - gecis takildiysa // kullanici 10s boyunca hicbir sey yapamaz this._nextTrackInProgress = true; setTimeout(() => { this._nextTrackInProgress = false; }, 10000); // 10 SANiYE!

COZUM:

Kilidi 3-4 saniyeye dusurmek + hata durumunda hemen serbest birakmak. Ayrica gecis tamamlandiginda (basarili veya basarisiz) kilidi temizlemek.

KRITIK

D3: saveQueueState 4x Tetikleniyor ($watch)

Alpine.js'in 4 farkli $watch handler'i her sarki degisiminde saveQueueState() fonksiyonunu cagiriyor. Bu fonksiyon tum kuyrugu JSON.stringify ile serialize ediyor. 100+ sarkilik kuyrukta bu islem her seferinde ~5-15ms suruyor (4x = 20-60ms).

player-core.js ~satir 9150-9167
// 4 ayri $watch AYNI fonksiyonu cagiriyor! $watch('currentSong', () => this.saveQueueState()); // #1 $watch('queue', () => this.saveQueueState()); // #2 $watch('currentIndex', () => this.saveQueueState()); // #3 $watch('shuffleEnabled', () => this.saveQueueState()); // #4 // Her degisiklikte 4x JSON.stringify(this.queue)!

COZUM:

Debounce eklemek: saveQueueState() icinde 500ms debounce ile sadece son cagriyi islemek. 4 $watch yerine tek bir requestAnimationFrame bazli queue save mekanizmasi.

KRITIK

D4: Performance-Debug updatePanel() Her Saniye

iRONi: Donmalari tespit etmek icin yazilan debug araci, kendisi donmalarin en buyuk kaynaklarindan biri! updatePanel() her saniye calistiriliyor ve her cagrisinda: tum audio elementlerini tarayan captureFullState(), cache boyutunu sayan collectMetrics(), ve TAMAMEN yeniden olusturulan innerHTML atamasi yapiliyor.

performance-debug.js ~satir 3253-3726
// Her 1 saniyede bir calistiriliyor: updateInterval = setInterval(updatePanel, 1000); function updatePanel() { collectMetrics(); // DOM query + Alpine store captureFullState(); // querySelectorAll('audio') + HLS introspection analyzeMemoryTrend(); // 120 entry iteration content.innerHTML = `...`; // 400+ satirlik HTML yeniden olusturma // TOPLAM: ~15-30ms jank her saniye }

COZUM:

Panel gorunur degilken interval'i durdurmak. Gorunurken bile 3-5 saniyeye cikarmak. innerHTML yerine hedefli DOM guncellemeleri kullanmak.

KRITIK

D5: startAutoLog() TUM Kullanicilar Icin Calisiyor

Debug paneli acilmasa bile startAutoLog() TUM kullanicilar icin calistiriliyor. Her 60 dakikada bir generateReport() senkron olarak calisarak buyuk bir string olusturuyor. Bu islem ~20-50ms sureli GC spike'a neden oluyor.

performance-debug.js ~satir 4075, 2632-2641
function init() { setupEventListeners(); // TUM event listener'lari kaydet startAutoLog(); // <-- HER KULLANICI icin 60dk interval! // ... } // init() document ready'de KOSULSUZ cagriliyor

COZUM:

startAutoLog() cagrisini isDebugAllowed() kosuluna baglamak. Sadece debug izni olan kullanicilar icin calistirmak.

YUKSEK

D6: Crossfade Race Condition (250ms Pencere)

Crossfade baslama kontrolunde 250ms'lik bir pencere var. Bu pencerede startCrossfade() iki kez tetiklenebilir. Iki ayri crossfade sureci ayni anda calisirsa ses karismasi, duraklama veya tamamen durma olur.

player-core.js ~satir 6941-6943

COZUM:

Mutex/flag mekanizmasi: _crossfadeInProgress flag'i ile cift tetiklemeyi onlemek.

YUKSEK

D7: Timer Birikimi (Tab Switching)

visibilitychange dinleyicisi her tab gecisinde yeni setTimeout olusturuyor. Hizli alt-tab yapildiginda timer'lar birikiyor ve tab'a geri donuldugunde hepsi ayni anda tetikleniyor.

player-core.js ~satir 8935

COZUM:

Onceki timeout'u clearTimeout() ile iptal edip sonra yenisini olusturmak.

YUKSEK

D8: MutationObserver document.body subtree:true

Performance-debug modulu document.body uzerinde childList: true, subtree: true ile MutationObserver kaydediyor. Bu, sayfadaki HER DOM degisikliginde (Alpine.js reaktivite, Livewire guncellemeleri) callback tetikleniyor.

performance-debug.js ~satir 1638-1641

COZUM:

Sadece debug aktifken baslat. Kapsamini daralt: sadece player container'i izle veya throttle ekle.

YUKSEK

D9: _safePlayFromQueue Recursive Retry

Kuyruktan sarki calma fonksiyonu recursive olarak kendini cagiriyor. 100 sarkilik kuyrukta her sarki icin 3 deneme yapilirsa potansiyel 300 fetch istegi gonderilir. Tum sarkilar hataliysa CPU ve ag yuku artar.

player-core.js ~satir 2322-2365

COZUM:

Maksimum 5-10 deneme limiti koymak. Ard arda X hata alindiktan sonra kuyrugu durdurmak.

Gecikme (Delay) Nedenleri

Kullanicinin "sarki arasi cok uzun bekliyor" veya "next'e basinca gecikme var" dedigi durumlar

KRITIK

G1: Waterfall Await Zinciri (6-7 Sirali Adim)

Bir sarki baslatilirken 6-7 sıralı (sequential) await calistirilir. Her biri ag geckmesine bagimli. Yavas internette toplam 5-15 saniye surebilir.

player-core.js ~satir 5598+
// SIRALI BEKLEME ZINCIRI: const streamData = await this.fetchStreamUrl(song.id); // 1. API cagirisi ~200-500ms const hls = HlsPool.acquire(); // 2. HLS instance olustur ~10ms await hls.loadSource(url); // 3. Manifest indir ~100-300ms await this.lockABRLevel(hls); // 4. Kalite kilitle ~50ms await this.waitForBuffer(audio); // 5. Buffer dolmasini bekle ~500-2000ms await audio.play(); // 6. Cal ~50ms // TOPLAM MINIMUM: ~900ms, YAVAS AG: ~3-5s+

COZUM:

Preload sistemi daha agresif kullanilmali. Manifest ve key indirme parallellestirilmeli. Buffer bekleme suresi kisaltilmali (yeterli buffer = 2 segment).

KRITIK

G2: 8 Saniye Loading Guard

Sarki yuklenirken 8 saniyelik bir "guard" timer baslatiliyor. Bu sure doldiginda otomatik olarak nextTrack() cagriliyor. Bu zincirleme etki yaratabilir: sarki yuklenemedi → next → o da yuklenemedi → next → sonsuz dongu.

player-core.js ~satir 4269

COZUM:

Guard timeout'unu 5 saniyeye dusurmek. Ard arda 3 basarisiz guard'dan sonra kuyrugu durdurmak. Kullaniciya "baglanti sorunu" mesaji gostermek.

YUKSEK

G3: DEVRALMA (Preload Handoff) 3 Saniye Blogu

Preload edilen sarki devralinirken (handoff) polling mekanizmasi 3 saniye boyunca bloke olabiliyor. Kullanici next'e basmis ama 3 saniye bekliyor.

player-core.js ~satir 4963-4977

COZUM:

Polling yerine event-driven handoff kullanmak. Preload hazir oldugunda event dispatch edip dinlemek.

YUKSEK

G4: Promise Constructor Antipattern (Crossfade HLS)

Crossfade HLS olusturma kodunda new Promise(async (resolve, reject)) kullaniliyor. Bu antipattern'de async fonksiyon icindeki hatalar yakalanmaz, sessiz hatalara neden olur.

player-core.js ~satir 2615-2804

COZUM:

new Promise(async ...) yerine duz async function kullanmak.

ORTA

G5: Dual Progress Interval Birikimi

HLS ve crossfade progress interval'lari birlikte calisabilir. Iki ayri setInterval her 250ms'de progress bar gunceller.

player-core.js ~satir 6882 + 6960
ORTA

G6: setInterval(attachSegmentStatListener, 5000) Asla Temizlenmiyor

Performance-debug'daki segment stat listener baglama interval'i ASLA temizlenmiyor. Basarili baglandiktan sonra bile her 5 saniyede bir gereksiz yere calisir.

performance-debug.js ~satir 1720

Backend Sorunlari

Stream URL'leri ve HLS dosya sunumundaki performans darbogazlari

KRITIK

B1: getFormattedBitrate() Her Stream Isteginde Dosya Parse Ediyor

stream() metodu her cagrildiginda getFormattedBitrate() uzerinden getID3::analyze() calistiriyor. Bu, 5MB'lik bir MP3 dosyasinin her seferinde disk'ten okunup parse edilmesi demek. Her sarki basinda yapiliyor!

SongStreamController.php ~satir 109

COZUM:

Bitrate degerini veritabaninda saklamak (songs tablosuna bitrate kolonu). Ilk parse'dan sonra DB'ye yazip, sonraki iseklerde DB'den okumak.

YUKSEK

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

MuzibuCacheService::getSong() sadece album.artist eager load yapiyor. genre dahil degil. Her stream cagrisinda genre_name erisildikce ekstra SQL sorgusu atiliyor.

SongStreamController.php ~satir 70, 95, 114

COZUM:

CacheService'deki eager load'a genre ve coverMedia eklemek.

YUKSEK

B3: HLS Playlist Regex Isleme Cache'siz

HLS playlist icerigi Redis'te cache'leniyor (1 saat) ama regex islemleri (key URI yeniden yazma, variant filtreleme, query parametreleri ekleme) her istekte yeniden yapiliyor. 5-6 preg_replace her playlist isteginde calisir.

SongStreamController.php ~satir 770-842
ORTA

B4: trackStart/trackHit Auth Tutarsizligi

trackStart() ve trackHit() sadece auth('sanctum') kontrol ediyor. trackEnd() ise hem auth('web') hem auth('sanctum') kontrol ediyor. Session-based kullanicilar icin trackStart/trackHit 401 donebilir.

SongStreamController.php ~satir 293, 383, 442

Orphan (Kullanilmayan) Kod

Hicbir yerden cagirilmayan veya devre disi birakilan fonksiyonlar

Fonksiyon Dosya Satir Durum
detectDevice() SongStreamController.php 528 ORPHAN Hicbir metottan cagirilmiyor
detectBrowserFromUA() SongStreamController.php 909 ORPHAN Hicbir metottan cagirilmiyor
getSessionTerminationMessage() SongStreamController.php 881 ORPHAN DeviceService kapali
incrementPlayCount() SongStreamController.php 258 DEPRECATED trackStart/trackHit ile replace edildi
trackProgress() SongStreamController.php 429 DEPRECATED trackStart'a delege ediyor
login/register handlers auth.js - STUB Bos fonksiyonlar, icerik yok
runFirstVisitTest() buffer-monitor.js - DISABLED Flag ile devre disi
setupConnectionChangeTest() buffer-monitor.js - DISABLED Flag ile devre disi
startPolling() spot-player.js 65 DISABLED Yavaslama nedeniyle kapatildi
prefetchVisible/prefetchHover spa-router.js - DISABLED Devre disi birakildi

Dosya Haritasi

Tum okunan dosyalar, satirlari ve riskseviyeleri

Dosya Satir Risk Aciklama
player-core.js 9.463 KRITIK Ana player kodu - "God File" sorunu
performance-debug.js 4.145 YUKSEK Debug paneli - prod'da 194KB yukleniyor
SongStreamController.php 1.062 YUKSEK Stream + HLS + tracking + sifreleme
play-helpers.js 979 DUSUK playGenres, playPlaylist, playAlbum vb.
spa-router.js 687 DUSUK SPA navigasyon, prefetch (devre disi)
SongController.php 628 ORTA Sarki listeleme/detay sayfalari
spot-player.js 564 DUSUK Kurumsal spot/anons sistemi
speed-tester.js 395 DUSUK Hiz testi (100MB / 1MB)
device-profiler.js 356 DUSUK Cihaz tespit ve fingerprint
player.blade.php 330 DUSUK Player UI template
buffer-monitor.js 294 DUSUK Buffer izleme (cogu devre disi)
old-device-checker.js 244 DUSUK Eski cihaz/tarayici kontrolu
MuzikStreamController.php 232 DUSUK Muzik stream kontrolcu
favorites.js 230 DUSUK Favori sistemi (optimistic update)
PlayController.php 213 DUSUK AI Assistant play endpoint
session.js 190 DUSUK Oturum sonlandirma
auth.js 177 STUB Login/register (bos handler'lar)
api.js 99 DUSUK API istemci (authenticatedFetch)
safe-storage.js 38 DUSUK localStorage wrapper
TOPLAM 20.326

Sarki Calma Cagri Akisi

Kullanici Play'e bastiginda neler oluyor - uc uca (end-to-end) akis

Normal Sarki Baslatma Akisi

Kullanici: Play tiklar play-helpers.js: playAlbum() / playPlaylist()
player-core.js: playSongFromQueue(index) _nextTrackInProgress = true (10s kilit)
player-core.js: _safePlayFromQueue() fetchStreamUrl(songId)
API: /api/muzibu/songs/{id}/stream getID3::analyze() DISK I/O
SongStreamController::stream() encryptStreamUrls() XOR sifreleme
player-core.js: decryptStreamUrl() resolveFormat: HLS mi MP3 mi?
(HLS yolu)
playHlsStream() 6-7 sirali await zinciri
HlsPool.acquire() hls.loadSource(master.m3u8) serveHls()
lockABRLevel() waitForBuffer() audio.play()

Sarki Gecisi (Crossfade) Akisi

Sarki sonu yaklasinca (kalan < 7s) startCrossfade()
Mevcut sarki: fade-out baslat || Yeni sarki: fetchStreamUrl + HLS/MP3 hazirlama
RISK: Promise(async) antipattern RISK: 250ms race condition
Yeni sarki hazir → fade-in baslat Eski sarki bitti → temizle

Timer ve Interval Envanteri

Sistemde calisan tum zamanlayicilar

Timer Sure Dosya Temizleniyor mu? Risk
updatePanel interval 1s perf-debug.js Evet KRITIK
metric collection interval 1s perf-debug.js Evet YUKSEK
segmentStatListener interval 5s perf-debug.js HAYIR! ORTA
autoLog interval 60dk perf-debug.js Evet ORTA
expireCheck interval 30s perf-debug.js Evet DUSUK
_nextTrackInProgress timeout 10s player-core.js Kismen KRITIK
loadingGuard timeout 8s player-core.js Evet YUKSEK
crossfade progress interval 250ms player-core.js Kismen ORTA
HLS progress interval 250ms player-core.js Evet DUSUK
visibilitychange setTimeout'lar 1-5s player-core.js HAYIR! YUKSEK
8x fakeCore timeout zinciri 0.6-7s perf-debug.js DOM'a bagli DUSUK

Oncelikli Oneriler

P0 - Hemen Yapin (Donmayi Azaltir)

  1. _hlsRetryCount her yeni sarkida sifirla
  2. saveQueueState() icine 500ms debounce ekle
  3. startAutoLog() sadece debug izinli kullanicilarda calistir
  4. _nextTrackInProgress timeout'unu 10s → 3s yap
  5. Tab switching timeout'larina clearTimeout ekle

P1 - Kisa Vadede (Gecikmeyi Azaltir)

  1. getFormattedBitrate() sonucunu DB'ye cache'le
  2. CacheService eager load'a genre, coverMedia ekle
  3. Crossfade _crossfadeInProgress mutex ekle
  4. segmentStatListener interval temizleme ekle
  5. DEVRALMA polling → event-driven yap

P2 - Orta Vadede (Mimari iyilestirme)

  1. player-core.js'i modullere ayir (AudioEngine, StateManager, Crossfader, Preloader)
  2. performance-debug.js prod'da yuklenmemeli (veya lazy load)
  3. Alpine proxy bypass: kritik yollar icin closure variable kullan
  4. Waterfall await zincirini paralellestir
  5. Orphan kodlari temizle (~1000 satir kazanc)

P3 - Uzun Vadede (Tam Yeniden Yazim)

  1. AudioEngine: Alpine.js disinda, Web Audio API tabanli
  2. State Machine (FSM): idle → loading → playing → paused → transitioning
  3. Crossfade v2: Web Audio API GainNode ile donanim hizlandirmali
  4. Worker-based preload: ana thread'i bloke etmeyen preload

Korunacak Iyi Mekanizmalar

Yeniden yazimda muhafaza edilmesi gereken basarili yapilar

HlsPool

Instance pooling - her sarkida yeni HLS olusturma yerine havuzdan al

CachingKeyLoader

HLS sifreleme anahtarini cache'le, tekrar indirme

Blacklist Sistemi

Hata veren sarkilari gecici kara listeye al

Token Flag Encoding

Format bilgisini URL'e gomme (s/u/l/m/h)

CDN Blok Expires

30dk blok bazli URL → Cloudflare cache uyumlu

Context-Based Refill

Kuyruk bitince turune gore otomatik doldurma

3-Fazli Tracking

trackStart → trackHit (30s) → trackEnd

Blob URL Tracking

activeBlobUrls Set ile bellek sizintisi onleme