Muzibu Player v2 — Tam Sistem Analizi

Mevcut sistemin eksiksiz incelemesi • v2 yeniden yazım hazırlığı

28 Kritik Sorun 17.079 Satır JS 24 Dosya 13 Backend Endpoint
17K
Toplam JS Satırı
~70
State Değişkeni
12
Feature Dosyası
10
Backend Controller
346KB
Features Boyutu

İçindekiler

1. Basit Anlatım (Herkes İçin)

Müzik çalar (player) sistemimiz çalışıyor ama büyümüş ve karmaşıklaşmış. Bir odayı düşünün: her şey çalışıyor ama kablolar birbirine dolanmış, anahtar nerede bilinmiyor.

Neden donuyor? Player'da bir şarkıdan diğerine geçerken, eski şarkının sesi kapatılıp yenisi açılıyor (crossfade). Bu geçiş sırasında aynı anda çok fazla iş yapılıyor: eski ses kaynağı temizleniyor, yeni HLS bağlantısı kuruluyor, ilerleme çubuğu güncelleniyor, geçmiş kaydediliyor. Bunlardan biri takılırsa — donma başlıyor.

Neden geçişlerde sorun var? Crossfade sistemi 7 saniyelik bir "çapraz geçiş" yapıyor. Ama şarkının bitişini takip eden zamanlayıcılar (timer) bazen senkrondan çıkıyor — özellikle telefon arka plana alındığında tarayıcı bu zamanlayıcıları yavaşlatıyor, sonra geri dönünce yığılmış eventler bir anda patladığında sorunlar çıkıyor.

v2'de ne değişecek? Aynı özellikler kalacak ama kablolar düzgün döşenecek. Her parça (ses motoru, kuyruk, geçiş, arayüz) ayrı modüller halinde olacak, birbirlerini sadece "mesaj" ile haberdar edecek. Böylece bir parça sorun çıkarsa diğerlerini etkilemeyecek.

2. Mevcut Mimari Genel Bakış

Frontend Katmanı

FrameworkAlpine.js (Livewire ile birlikte)
Ses MotoruHLS.js (HLS) + Howler.js (MP3 fallback)
Ana BileşenmuzibuApp() Alpine data component
State YönetimiAlpine reaktif + Alpine.store proxy
SPA RouterÖzel (fetch + DOM swap + Alpine reinit)
SıkıştırmaAES-128 şifreli HLS segmentleri

Backend Katmanı

FrameworkLaravel 11 + Stancl Tenancy
Ses İşlemeFFmpeg (2-pass loudnorm)
CacheRedis (song:24h, playlist:1h, premium:5m)
CDNCloudflare (MP3 30dk blok expires)
URL İmzalamaHMAC-SHA256 + token flag encoding
KuyrukHorizon (hls queue, 900s timeout)

Tasarım Desenleri (Mevcut)

Object Pool
HlsPool — HLS instance yeniden kullanım
LRU Cache
StreamCache — 20 entry sınırı
Blacklist
Hatalı şarkıları 3 deneme sonrası atla
Watchdog
3 anomali algılama (stall, süre, ilerleme)
Proxy Bridge
Alpine.store('player') → root bileşen
Debounce Guard
Çift tıklama / hızlı geçiş koruma

3. Dosya Haritası

Frontend JS Dosyaları

#DosyaSatırBoyutSorumlulukDurum
1player-core.js~9.200~380KBAna Alpine bileşeni, tüm player state + mantıkAşırı büyük
2play-helpers.js98036KBGenre/playlist/album/radio/song çalıştırıcılarDuplikasyon
3performance-debug.js3.675194KBDev debug panel (20+ veri yapısı, 6+ interval)Prod'da yükleniyor
4spa-router.js68828KBSPA navigasyon, prefetch cache, Alpine destroy/initKarmaşık
5spot-player.js56821KBKurumsal anons sistemi, preload, rotasyonİyi
6speed-tester.js39513KBİnternet hız testi: download + latency + jitterİyi
7device-profiler.js35611KBCihaz parmak izi + backend kayıt + mz_device cookieÇakışma
8buffer-monitor.js2949KBBuffer/donma izleme, otomatik hız testi tetiklemeKısmen devre dışı
9old-device-checker.js2448KBEski cihaz tespiti (RAM/CPU/browser)Çakışma
10favorites.js2319KBFavori toggle, optimistic UI, 7 model desteğiİyi
11auth.js1788KBLogin/register form + validasyonBoş fonksiyonlar!
12api.js1004KBAuthenticated fetch wrapper, 401 kontrolüİyi
13session.js1917KBSession sonlandırma, logout, storage temizlemeİyi
14safe-storage.js~502KBlocalStorage wrapperİyi

Backend Dosyaları

DosyaSorumlulukSorun
SongStreamController.phpStream URL oluşturma, HLS dosya sunma, key sunma, 3-fazlı play trackingÇok sorumluluk
SongController.phpSong CRUD, legacy stream, CDN serve, encryption key (duplicate!)Stream çakışması
PlaylistController.phpPlaylist CRUD, AI oluşturma, seeded shuffleN+1 query
QueueRefillController.phpContext-based sonsuz kuyruk, 9 stratejiDebug query overhead
HomeController.phpBirleşik anasayfa endpoint'iN+1 query
HLSService.phpMP3→HLS dönüşüm, AES-128 şifreleme, FFmpegKod tekrarı
SignedUrlService.phpHMAC-SHA256 URL imzalama, token flag encodingfile_exists hot path
MuzibuCacheService.phpRedis cache yönetimiRedis KEYS komutu
ConvertToHLSJob.phpKuyruktaki HLS dönüşüm işiKod tekrarı
AddHlsVariantsCommand.phpToplu variant ekleme komutuKod tekrarı

4. Oynatma Akış Şeması

Kullanıcı Aksiyonu (playSong/nextTrack/playFromQueue) │ ▼ playSongFromQueue(index) — Ana giriş noktası │ ├─ Concurrent guard kontrolü (çift çağrı engeli) ├─ Queue'dan şarkı bilgisi al ├─ UI state güncelle (isSongLoading=true) │ ▼ loadAndPlaySong(song) │ ├─ Stream URL al: GET /api/muzibu/songs/{id}/stream │ ├─ Auth kontrolü (401/402) │ ├─ resolveAudioFormat() → hls veya mp3 │ ├─ URL imzalama + XOR şifreleme │ └─ JSON response {_: encrypted, __: key, ___: ts} │ ├─ URL deşifre et (XOR) │ ├─ stream_type == 'hls' ? │ ├─ EVET → playHlsStream(url, fallbackUrl) │ │ ├─ HlsPool.acquire() — Pool'dan HLS instance al │ │ ├─ ABR lock: currentLevel=3 (high) başlangıç │ │ ├─ CachingKeyLoader entegrasyonu │ │ ├─ hls.loadSource(url) │ │ ├─ hls.attachMedia(audioElement) │ │ ├─ MANIFEST_PARSED → ABR unlock (5sn sonra) │ │ ├─ ERROR → tipo'ya göre retry/skip/fallback │ │ └─ Watchdog başlat │ │ │ └─ HAYIR → playWithHowler(url) │ ├─ new Howl({ src, format:'mp3', html5:true }) │ ├─ onplay, onend, onerror eventleri │ └─ Crossfade entegrasyonu │ ├─ trackStart() → POST /api/muzibu/songs/{id}/track-start │ └─ play_id döner (30sn sonra trackHit, bitiş'te trackEnd) │ └─ Şarkı çalıyor │ ├─ Progress tracking (requestAnimationFrame) ├─ Crossfade zamanlayıcı (son 7sn'de tetik) ├─ Queue preload (sonraki şarkıyı önceden yükle) └─ Otomatik kuyruk doldurma (5 şarkı kaldığında)

Crossfade Akışı

Şarkı bitmesine 7sn kala (crossfadeThreshold) │ ├─ startCrossfade() tetiklenir │ ├─ Sonraki şarkı preload edilmiş mi? │ │ ├─ EVET → preloadedHowl kullan │ │ └─ HAYIR → yeni Howl/HLS oluştur │ │ │ ├─ Eski ses: volume fade-out (7sn, linear) │ ├─ Yeni ses: volume fade-in (7sn, linear) │ │ │ ├─ ⚠️ SORUN: Arka plan tab'da setInterval throttle → fade zamanlaması kayıyor │ ├─ ⚠️ SORUN: Howler fade-out bazen hata verip durmuyor → iki şarkı üst üste │ └─ ⚠️ SORUN: HLS preload + crossfade aynı audio element'i paylaşamıyor │ └─ cleanupAfterCrossfade() ├─ Eski Howl instance destroy ├─ Eski HLS instance pool'a geri ver └─ Blob URL'leri temizle

5. Kritik Sorunlar — Donma ve Geçiş Hataları

Bu sorunlar doğrudan kullanıcı deneyimini etkiliyor. Donma ve geçiş sorunlarının kök nedenleri:

K1. Arka Plan Tab Throttle — Crossfade Zamanlama Kayması

KRİTİK

Sorun: Tarayıcılar arka plan tab'larda setInterval ve setTimeout'u 1 saniyeye throttle eder. Crossfade sistemi 100ms hassasiyetli zamanlayıcılar kullanıyor. Kullanıcı tab'ı arka plana alıp geri geldiğinde, yığılmış callback'ler bir anda tetikleniyor.

Etki: Şarkı geçişi sırasında iki şarkı aynı anda çalıyor, veya hiç çalmıyor (sessiz donma).

v2 Çözüm: requestAnimationFrame + visibilitychange API ile zamanlama yönetimi. Tab geri geldiğinde anında senkronizasyon.

K2. Concurrent Guard Zayıflığı — Çift Çağrı Yarış Durumu

KRİTİK

Sorun: _playLock ve isTransitioning flag'leri async işlemlerde senkronizasyon sorunları yaşıyor. Hızlı ileri/geri basıldığında iki loadAndPlaySong() çağrısı aynı anda çalışabiliyor. İlk çağrı HLS instance alıp yüklerken, ikinci çağrı da farklı bir instance alıyor.

Etki: İki HLS instance aynı audio element'e bağlanıyor → ses çarpışması veya donma.

v2 Çözüm: AbortController pattern + tek async queue: her yeni çağrı öncekini iptal eder.

K3. Alpine Proxy Cascade — HLS.js Property Setter Çoklu Tetikleme

KRİTİK

Sorun: Alpine.js tüm data object'leri bir Proxy ile sarar. HLS.js'in currentLevel setter'ı Alpine proxy üzerinden çağrıldığında, reactive watcher'lar tetikleniyor ve aynı setter 4x çağrılabiliyor.

Mevcut Fix: var _rawHls = HlsPool.acquire() ile closure variable kullanılıyor. Ama bu kalıp tüm kodda tutarlı değil.

v2 Çözüm: Ses motoru tamamen Alpine scope dışına çıkarılmalı. Ayrı modül, Alpine sadece UI state'i yönetmeli.

K4. Howler Fade Hata Kurtarma Eksikliği

KRİTİK

Sorun: Crossfade sırasında eski şarkının howl.fade() çağrısı hata alırsa (zaten dispose edilmiş, veya audio context kilitlendiyse), fade tamamlanmadan kalıyor. Yeni şarkı başlamış ama eski şarkı hâlâ sessiz ama aktif şekilde kaynak tüketiyor.

Etki: Bellek sızıntısı + CPU kullanımı artışı. Birkaç geçiş sonrası gözle görülür donma.

v2 Çözüm: Fade'e timeout limiti (max 8sn), hata durumunda zorla destroy. Web Audio API doğrudan kullanarak daha güvenilir gain control.

K5. HLS→Howler Fallback Sırasında State Tutarsızlığı

KRİTİK

Sorun: HLS yükleme başarısız olduğunda (ağ hatası, key hatası), MP3 fallback'e geçiliyor. Ama bu geçiş sırasında currentStreamType, isPlaying, isSongLoading flag'leri yarış durumuna giriyor. HLS error handler isPlaying=false yaparken, Howler'ın onplay callback'i isPlaying=true yapıyor — hangisi kazanacak?

v2 Çözüm: Finite State Machine (FSM): idle → loading → playing → paused → error → fallback. Her state geçişi kesin kurallarla.

K6. requestAnimationFrame İlerleme Takibi — Zombie Loop

KRİTİK

Sorun: İlerleme çubuğu requestAnimationFrame ile güncelleniyor. Ama eski şarkının rAF loop'u bazen temizlenmeden yeni şarkının loop'u başlıyor. Sonuç: birden fazla rAF loop aktif, sürekli DOM güncellemesi → jank ve CPU spike.

v2 Çözüm: Tek bir global rAF loop, token-based invalidation. Yeni şarkıda token değişir → eski loop otomatik durur.

K7. Preload Sistemi + Crossfade Çakışması

YÜKSEK

Sorun: Sonraki şarkı için preload başlatılıyor (HLS veya Howler). Crossfade zamanı geldiğinde preloaded instance kullanılıyor. Ama arada kullanıcı manuel "next" basarsa, preloaded instance yanlış şarkıya ait oluyor — ya da aynı instance iki kez kullanılmaya çalışılıyor.

v2 Çözüm: Preload sistemi crossfade'den bağımsız olmalı. Her ikisi de aynı pool'dan instance almalı ama birbirlerinin instance'larını çalmayacak.

K8. Audio Element Paylaşım Sorunu

YÜKSEK

Sorun: İki audio element var: #hlsAudio (ana) ve #hlsAudioNext (crossfade). Ama HLS.js attachMedia() bir audio element'e bağlandığında, önceki bağlantıyı otomatik koparıyor. Crossfade sırasında iki HLS instance iki farklı audio element'e bağlı olmalı — ama bazen ikisi de aynı element'e bağlanıyor.

v2 Çözüm: AudioContext + GainNode tabanlı çıktı yönetimi. Fiziksel audio element değil, programatik gain control ile crossfade.

6. Mimari Sorunlar

M1. Tanrı Dosyası: player-core.js (~9.200 satır, ~380KB)

YÜKSEK

Tek bir dosyada: ses motoru, kuyruk yönetimi, crossfade, UI state, hata yönetimi, preload, watchdog, keyboard shortcuts, history tracking, MediaSession API... Hepsi iç içe.

~70 state değişkeni aynı Alpine data scope'unda. Herhangi biri değiştiğinde Alpine tüm watcher'ları kontrol ediyor.

v2: 8-10 ayrı modül dosyası, her biri tek sorumluluk.

M2. performance-debug.js Production'da Yükleniyor (194KB)

YÜKSEK

3.675 satırlık debug paneli, 20+ veri yapısı, 6+ setInterval, 20+ event listener. Conditional loading var ama dosyanın kendisi her kullanıcıya indirilip parse ediliyor.

v2: Lazy load: sadece ?debug=1 query'si veya console komutuyla dinamik import.

M3. this Context Karışıklığı

YÜKSEK

session.js, spa-router.js, api.js içinde this.isPlaying, this.isLoading gibi çağrılar var ama bu dosyaların kendi context'inde bu property'ler yok. player-core'un .call(this, ...) ile çağırmasına bağımlı. Kırılgan (fragile) bir kalıp.

v2: Event bus veya shared state modülü. Her dosya kendi scope'unda çalışacak.

M4. Dört Farklı Event İletişim Mekanizması

ORTA

Player ile dış dünya arasında 4 farklı yol: (1) doğrudan Alpine method çağrısı, (2) $dispatch custom event, (3) $store.player proxy, (4) window.playPlaylist() global fonksiyon. Hangisinin ne zaman kullanılacağı belirsiz.

v2: Tek bir EventBus pattern: PlayerBus.emit('play', {songId})

M5. auth.js Boş Fonksiyonlar

ORTA

handleLogin(), handleRegister(), handleLogout() tamamen boş implementasyon. Kullanıcı bu formlara bir şey girip gönderdiğinde hiçbir şey olmuyor.

M6. Keyboard Shortcuts Devre Dışı

ORTA

keyboard.js dosyası app.blade.php'de yorum satırına alınmış. Klavye kısayolları sayfası (Space, K, N, P, vb.) gösteriliyor ama hiçbiri çalışmıyor.

7. Performans Sorunları

P1. Alpine ~70 Reaktif Değişken

Tüm state aynı Alpine data'da. Herhangi biri değiştiğinde Alpine tüm DOM binding'leri kontrol ediyor. Crossfade sırasında volume, progress, isPlaying, currentTime... hepsi hızla değişiyor → UI jank.

P2. rAF + setInterval Çoklu Loop

Progress tracking için rAF, crossfade fade için setInterval, watchdog için setInterval, session polling için setInterval. Toplamda 8-10 aktif zamanlayıcı. Hepsi UI thread'inde.

P3. Script Yükleme Sırası (22+ dosya)

22+ ayrı JS dosyası sırayla yükleniyor. Bazıları defer, bazıları senkron. Toplam ~550KB JavaScript. Bundle/code-split yapılmamış.

P4. Browser Tespit Tekrarı (3 dosya)

device-profiler.js, old-device-checker.js, performance-debug.js — üçü de aynı UA regex'leri ve RAM/CPU tespitini ayrı ayrı yapıyor.

P5. play-helpers.js Duplikasyonu

MIN 15 şarkı dolumu 3 kez, addToQueue switch/case 2 kez, premium check 6 kez kopyalanmış. ~300+ satır tekrar eden kod.

P6. ~200 Satır Devre Dışı Kod

buffer-monitor (firstVisit + connectionTest), spa-router (viewport + hover prefetch), spot-player (polling) — ölü/devre dışı bırakılmış kod hâlâ dosyalarda.

8. Çakışmalar ve Duplikasyonlar

Çakışma/DuplikasyonNerede?DetayCiddiyet
İki Stream Controller SongController::stream() vs SongStreamController::stream() İkisi de stream endpoint'i. Birincisi basit path döndürüyor, ikincisi full auth+imza+şifreleme. Hangisi kullanılacak belirsiz. KRİTİK
İki Key Serve Endpoint SongController::serveEncryptionKey() vs SongStreamController::serveKey() İkisi de AES-128 key sunuyor. Farklı imza doğrulama mantıkları. Biri 24h Redis cache yapıyor, diğeri yapmıyor. KRİTİK
loudnorm 3x Duplikasyon HLSService, ConvertToHLSJob, AddHlsVariantsCommand buildTwoPassLoudnormFilter() ve parseLoudnormJson() 3 ayrı dosyada tamamen aynı implementasyon. YÜKSEK
seededShuffle 2x Duplikasyon PlaylistController ve QueueRefillController Aynı Fisher-Yates seeded shuffle implementasyonu 2 controller'da kopyalanmış. ORTA
Cihaz Tespiti 3x Duplikasyon device-profiler.js, old-device-checker.js, performance-debug.js Aynı UA regex'leri, RAM kontrolü, CPU core sayısı tespiti 3 ayrı dosyada. YÜKSEK
formatSongs Pattern Tekrarı Birden fazla controller Song verisi format dönüşümü (cover URL, artist adı, süre) her controller'da tekrar implementasyon. ORTA
3 URL İmza Formatı SongController::serveAudioCdn() Geriye uyumluluk için 3 farklı HMAC signature formatı destekleniyor. En eski kalite parametresiz. ORTA
Event İletişim 4 Yol Alpine method, $dispatch, $store.player proxy, window.playX() Player'a erişim için 4 farklı mekanizma. Bazen fallback zincirleri: window.playPlaylist ? ... : $store.player.playPlaylist() YÜKSEK
Dead Route MuzibuServiceProvider satır 338 muzibu.songs.audio-serve rotası SongController::serveAudio()'yu işaret ediyor ama bu method yok. YÜKSEK

9. Korunacak İyi Mekanizmalar

v2'de yeniden yazılsa bile korunması gereken başarılı tasarımlar:

HlsPool (Object Pool)

HLS instance'ları yeniden kullanarak GC baskısını azaltıyor. Havuz boyutu sınırlı, FIFO mantığıyla çalışıyor.

CachingKeyLoader

AES-128 key'leri cache'leyerek aynı key için tekrar XHR yapmıyor. queueMicrotask ile async cache serve.

Failed Songs Blacklist

3 denemeden sonra şarkıyı blacklist'e alıyor. Aynı hatalı şarkıda sonsuz döngüyü engelliyor.

Watchdog v3

3 anomali algılama: stall (ses durma), duration jump, progress freeze. Otomatik recovery.

Token Flag Encoding

userId.s/u/l/m/h formatıyla soft/level bilgisini URL token'ına gömme. Ekstra query parametresi gerektirmiyor.

CDN 30dk Blok Expires

MP3 URL'lerinde expires'ı 30dk'lık bloklara yuvarlayarak aynı zaman dilimindeki tüm kullanıcılara aynı URL veriyor → Cloudflare cache hit oranı yüksek.

Context-Based Queue Refill

9 farklı bağlam stratejisi (genre, album, playlist, artist, sector, radio, favorites, popular, recent). Bağlam tükendiğinde otomatik geçiş.

3-Fazlı Play Tracking

track-start (başlangıç) → track-hit (30sn sonra sayım) → track-end (bitiş istatistik). Doğru dinleme verisi.

2-Pass Loudnorm

FFmpeg ile ölçüm + uygulama. -16 LUFS hedefi tüm şarkıları aynı ses seviyesinde standartlaştırıyor.

Blob URL Tracking

Oluşturulan tüm Blob URL'leri takip edip temizleme. Memory leak önleme.

10. Backend Sorunları

B1. Redis KEYS Komutu Kullanımı

MuzibuCacheServiceinvalidatePopularSongs(), invalidateFeaturedPlaylists(), flushAll(), getCacheStats()

KEYS komutu O(n) karmaşıklığında ve Redis'i bloklar. Production'da binlerce key ile ciddi yavaşlama yapabilir. SCAN kullanılmalı.

B2. N+1 Query Sorunları (3 Noktada)

1. PlaylistController::index() — Her playlist için songs()->count() ayrı query
2. HomeController::formatPlaylist() — Her playlist için songs()->count() ayrı query
3. HomeController::formatAlbum() — Her albüm için songs()->count() ayrı query

Çözüm: withCount('songs') kullanılmalı veya songs_count kolonu cache'lenmeli.

B3. Playlist Tüm Şarkıları Redis'e Cache'liyor

MuzibuCacheService::getPlaylist() — bir playlist'in TÜM şarkılarını tüm ilişkileriyle (album.artist) Redis'e kaydediyor. 5.000 şarkılık bir playlist'te bu devasa bir Redis bellek tüketimi demek.

B4. file_exists() Hot Path'te

SignedUrlService::generateHlsUrl() her stream isteğinde master.m3u8 dosyasının varlığını disk I/O ile kontrol ediyor. Bu, her şarkı çalma isteğinde gereksiz bir disk erişimi.

B5. Şarkı Başına 10 FFmpeg Çağrısı

2-pass loudnorm (ölçüm + encode) × (high + ultralow + low + mid + mp3_128) = 10 FFmpeg process. 30K şarkı batch'inde = 300K FFmpeg çağrısı.

B6. Queue Refill Auth Yok

POST /api/muzibu/queue/refill sadece throttle middleware'i var, auth yok. Herkes şarkı listesi çekebilir.

B7. getSelectionExplanation() Her İstekte Çalışıyor

QueueRefillController — debug açıklama metni için her refill isteğinde ekstra DB sorguları yapıyor (Song::count, Playlist::find, Genre::find). Client bu veriyi kullanmıyor bile.

B8. Corporate Threshold Check Cache'siz

resolveAudioFormat() — corporate hesaplarda her stream isteğinde users.count() sorgusu çalıştırılıyor. Cache'lenmeli.

11. v2 Önerilen Mimari

Modül Yapısı

player/
├── core/
│ ├── audio-engine.js — HLS.js + Web Audio API yönetimi (Alpine dışı!)
│ ├── state-machine.js — FSM: idle→loading→playing→paused→error→fallback
│ ├── event-bus.js — Tek merkezi event sistemi
│ └── player-api.js — Alpine bileşeni (sadece UI state)
├── features/
│ ├── crossfade.js — AudioContext GainNode tabanlı crossfade
│ ├── queue.js — Kuyruk yönetimi + otomatik doldurma
│ ├── preloader.js — Sonraki şarkı ön yükleme
│ ├── progress.js — Tek global rAF loop + token invalidation
│ ├── media-session.js — MediaSession API (lock screen kontrol)
│ ├── keyboard.js — Klavye kısayolları
│ ├── history.js — Dinleme geçmişi + play tracking
│ └── watchdog.js — Anomali algılama + recovery
├── utils/
│ ├── device-info.js — Tekil cihaz tespiti (3 dosya birleşecek)
│ ├── url-crypto.js — URL deşifre + imza doğrulama
│ └── safe-storage.js — localStorage wrapper
└── ui/
    ├── player-bar.js — Desktop player bar UI
    ├── mobile-player.js — Mobile pill player UI
    └── queue-panel.js — Kuyruk overlay UI

Temel Mimari Değişiklikler

Ses Motoru Alpine Dışına

Mevcut: HLS.js ve Howler.js Alpine proxy'si içinde → setter cascade.

v2: AudioEngine sınıfı tamamen bağımsız. Alpine sadece EventBus.on('stateChange') dinleyerek UI günceller.

Finite State Machine

Mevcut: 5+ boolean flag (isPlaying, isLoading, isSeeking, isTransitioning, isCrossfading)

v2: Tek state enum: idle, loading, buffering, playing, paused, crossfading, error, recovering. Geçersiz state kombinasyonları imkansız.

AbortController Pattern

Mevcut: Concurrent guard flag — ama async gap'lerde yarış durumu.

v2: Her play() çağrısı yeni bir AbortController oluşturur, öncekini iptal eder. Tüm fetch/timeout'lar abort signal'a bağlı.

Web Audio API Crossfade

Mevcut: Howler volume fade + setInterval → tab throttle sorunu.

v2: AudioContext.createGain() + gainNode.gain.linearRampToValueAtTime(). Tarayıcı seviyesinde zamanlama, throttle'dan etkilenmez.

Tek Global rAF Loop

Mevcut: Her bileşen kendi rAF/setInterval'ını yönetiyor → zombie loop'lar.

v2: Tek Ticker sınıfı tüm periyodik işleri yönetir. Token-based — yeni şarkıda token değişir, eski callback'ler otomatik geçersiz.

Visibility API Entegrasyonu

Mevcut: Tab arka plana alınca zamanlayıcılar bozuluyor.

v2: visibilitychange event'inde: hidden → tüm timer'ları duraklat, visible → durum senkronize et, crossfade varsa anında tamamla.

12. Önceliklendirme Tablosu

v2 geçişi için iş paketleri, öncelik sırasına göre:

Öncelikİş PaketiEtkilediği SorunEtkiEfor
P0 AudioEngine modülü — Ses motorunu Alpine dışına çıkar K3 (Proxy cascade), K5 (State tutarsızlığı), K6 (Zombie rAF) Donma sorunlarının %60'ını çözer Büyük
P0 FSM (State Machine) — Boolean flag'ler yerine tek state enum K2 (Race condition), K5 (State tutarsızlığı) Geçersiz state kombinasyonlarını imkansız kılar Orta
P0 Crossfade v2 — Web Audio API GainNode + AbortController K1 (Tab throttle), K4 (Howler fade hatası), K7 (Preload çakışması), K8 (Audio element) Geçiş sorunlarının %90'ını çözer Büyük
P1 EventBus — 4 event mekanizmasını teke indir M4 (Event karmaşası), M3 (this context) Kod karmaşıklığı %40 azalır Orta
P1 player-core.js bölme — 9.200 satırı 8-10 modüle ayır M1 (God file), P1 (70 reaktif değişken) Bakım kolaylığı, debug kolaylığı Büyük
P1 Tek rAF Loop + Visibility API K1 (Tab throttle), K6 (Zombie rAF), P2 (Çoklu loop) CPU kullanımı %30 azalır Küçük
P2 Backend N+1 fix — withCount kullanımı B2 (N+1 query) API response süresi %50 iyileşir Küçük
P2 Redis KEYS → SCAN B1 (Redis KEYS bloklama) Redis bloklama riski ortadan kalkar Küçük
P2 Controller çakışma temizliği — Stream + Key deduplikasyonu Çakışma #1, #2 Bakım karmaşıklığı azalır Orta
P2 performance-debug.js lazy load M2 (194KB production yükü) Sayfa yüklenme hızı iyileşir Küçük
P3 Backend loudnorm/shuffle trait'e taşı Çakışma #3, #4 Kod tekrarı ortadan kalkar Küçük
P3 Device detection birleştirme P4 (3x cihaz tespiti), Çakışma #5 3 dosya → 1 dosya Küçük
P3 play-helpers.js refactor P5 (Duplikasyon) ~300 satır ölü kod temizlenir Küçük
P3 Keyboard shortcuts yeniden etkinleştir M6 (Devre dışı) Kullanıcı deneyimi iyileşir Küçük
P3 auth.js boş fonksiyonları doldur veya kaldır M5 (Boş fonksiyonlar) Hata kaynağı ortadan kalkar Küçük

v2 Geçiş Stratejisi

Tümünü bir anda yazmak yerine kademeli geçiş önerilir:

Faz 1: Altyapı

EventBus + FSM + AudioEngine modülünü yaz. Mevcut player-core'un yanında paralel çalışır. Feature flag ile açılır.

Faz 2: Geçiş

player-core'un fonksiyonları tek tek yeni modüllere taşınır. Her taşıma sonrası A/B test.

Faz 3: Temizlik

Eski kod silinir, backend duplikasyonlar temizlenir, performance-debug lazy load yapılır.

Ek: Mevcut State Değişkenleri (~70 adet)

Auth & UI State (tıklayın)
isLoggedIn, currentUser, isPremium, isRoot, isCorporate
showQueue, showLyrics, showDebugInfo, showMobileMenu
isSlowConnection, lastFallbackReason
Player State
isPlaying, isSongLoading, isLoading, isSeeking, isTransitioning
currentSong, queue[], queueIndex, shuffle, repeatMode
volume, isMuted, currentTime, duration, progressPercent, animatedDuration
currentStreamType, isLiked, playContext
Audio Engine State
currentHowl, currentHlsInstance, _rawHls (closure)
hlsPool (HlsPool singleton), streamCache (LRU)
currentAudioElement, nextAudioElement
_playLock, _loadAbortController
Crossfade State
crossfadeEnabled, crossfadeDuration, isCrossfading
crossfadeTimer, crossfadeOldHowl, crossfadeNewHowl
preloadedSong, preloadedHowl, preloadedHls
Tracking & Preload State
currentPlayId, trackHitSent, trackEndSent
watchdogTimer, watchdogLastProgress, watchdogStallCount
failedSongs (Map), blacklistThreshold
progressRafId, progressTrackingToken
blobUrls[], bufferCheckInterval
Custom Events Haritası
muzibu:songChangedŞarkı değiştiğinde (player-core → tüm dinleyiciler)
muzibu:playerStateChangedPlay/pause değiştiğinde
muzibu:queueUpdatedKuyruk değiştiğinde
muzibu:streamTypeChangedHLS↔MP3 format geçişinde
muzibu:crossfadeStartCrossfade başladığında
muzibu:crossfadeEndCrossfade bittiğinde
muzibu:errorHata oluştuğunda
play-songDış bileşenlerden şarkı çalma talebi
play-all-songsToplu çalma talebi (playlist/album/genre)

Ek: Audio Format Karar Zinciri

ÖNCE: config('muzibu.audio.force_format') → Boş değilse HERKES bu formatı alır │ ▼ ROOT TEST: ?_mz=hsoft/h3/h6/h12/h0/s6/s12/s0 → Root/debug kullanıcı test override │ ▼ ADMİN ATAMASI: user.audio_preference → auto değilse bu değer geçerli │ (mp3_64, mp3_128, mp3_auto, hls_32, hls_64, hls_128, hls_auto, hls_soft) ▼ KURUMSAL: corporate_account > 50 kullanıcı → Otomatik MP3 │ ▼ MOBİL: User-Agent mobil/tablet mi? │ ├─ mz_device=good → HLS │ └─ mz_device=weak/yok → MP3 ▼ PC VARSAYILAN: → HLS (full ABR) Token flag encoding: userId.s (soft), .u (ultralow), .l (low), .m (mid), .h (high)
25 Şubat 2026 • Muzibu.com.tr