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.
Donma (Freeze) Nedenleri
Kullanicinin "player dondu, tepki vermiyor" dedigi durumlar
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.
COZUM:
Her yeni sarki basladiginda this._hlsRetryCount = 0 yapmak yeterli. playSong() fonksiyonunun basina eklenmeli.
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.
COZUM:
Kilidi 3-4 saniyeye dusurmek + hata durumunda hemen serbest birakmak. Ayrica gecis tamamlandiginda (basarili veya basarisiz) kilidi temizlemek.
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).
COZUM:
Debounce eklemek: saveQueueState() icinde 500ms debounce ile sadece son cagriyi islemek. 4 $watch yerine tek bir requestAnimationFrame bazli queue save mekanizmasi.
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.
COZUM:
Panel gorunur degilken interval'i durdurmak. Gorunurken bile 3-5 saniyeye cikarmak. innerHTML yerine hedefli DOM guncellemeleri kullanmak.
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.
COZUM:
startAutoLog() cagrisini isDebugAllowed() kosuluna baglamak. Sadece debug izni olan kullanicilar icin calistirmak.
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.
COZUM:
Mutex/flag mekanizmasi: _crossfadeInProgress flag'i ile cift tetiklemeyi onlemek.
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.
COZUM:
Onceki timeout'u clearTimeout() ile iptal edip sonra yenisini olusturmak.
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.
COZUM:
Sadece debug aktifken baslat. Kapsamini daralt: sadece player container'i izle veya throttle ekle.
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.
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
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.
COZUM:
Preload sistemi daha agresif kullanilmali. Manifest ve key indirme parallellestirilmeli. Buffer bekleme suresi kisaltilmali (yeterli buffer = 2 segment).
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.
COZUM:
Guard timeout'unu 5 saniyeye dusurmek. Ard arda 3 basarisiz guard'dan sonra kuyrugu durdurmak. Kullaniciya "baglanti sorunu" mesaji gostermek.
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.
COZUM:
Polling yerine event-driven handoff kullanmak. Preload hazir oldugunda event dispatch edip dinlemek.
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.
COZUM:
new Promise(async ...) yerine duz async function kullanmak.
G5: Dual Progress Interval Birikimi
HLS ve crossfade progress interval'lari birlikte calisabilir. Iki ayri setInterval her 250ms'de progress bar gunceller.
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.
Backend Sorunlari
Stream URL'leri ve HLS dosya sunumundaki performans darbogazlari
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!
COZUM:
Bitrate degerini veritabaninda saklamak (songs tablosuna bitrate kolonu). Ilk parse'dan sonra DB'ye yazip, sonraki iseklerde DB'den okumak.
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.
COZUM:
CacheService'deki eager load'a genre ve coverMedia eklemek.
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.
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.
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
Sarki Gecisi (Crossfade) Akisi
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)
_hlsRetryCounther yeni sarkida sifirlasaveQueueState()icine 500ms debounce eklestartAutoLog()sadece debug izinli kullanicilarda calistir_nextTrackInProgresstimeout'unu 10s → 3s yap- Tab switching timeout'larina
clearTimeoutekle
P1 - Kisa Vadede (Gecikmeyi Azaltir)
getFormattedBitrate()sonucunu DB'ye cache'le- CacheService eager load'a
genre, coverMediaekle - Crossfade
_crossfadeInProgressmutex ekle segmentStatListenerinterval temizleme ekle- DEVRALMA polling → event-driven yap
P2 - Orta Vadede (Mimari iyilestirme)
- player-core.js'i modullere ayir (AudioEngine, StateManager, Crossfader, Preloader)
- performance-debug.js prod'da yuklenmemeli (veya lazy load)
- Alpine proxy bypass: kritik yollar icin closure variable kullan
- Waterfall await zincirini paralellestir
- Orphan kodlari temizle (~1000 satir kazanc)
P3 - Uzun Vadede (Tam Yeniden Yazim)
- AudioEngine: Alpine.js disinda, Web Audio API tabanli
- State Machine (FSM): idle → loading → playing → paused → transitioning
- Crossfade v2: Web Audio API GainNode ile donanim hizlandirmali
- Worker-based preload: ana thread'i bloke etmeyen preload
Korunacak Iyi Mekanizmalar
Yeniden yazimda muhafaza edilmesi gereken basarili yapilar
Instance pooling - her sarkida yeni HLS olusturma yerine havuzdan al
HLS sifreleme anahtarini cache'le, tekrar indirme
Hata veren sarkilari gecici kara listeye al
Format bilgisini URL'e gomme (s/u/l/m/h)
30dk blok bazli URL → Cloudflare cache uyumlu
Kuyruk bitince turune gore otomatik doldurma
trackStart → trackHit (30s) → trackEnd
activeBlobUrls Set ile bellek sizintisi onleme