Howler.js 2.2.4, HLS.js Streaming, Crossfade (6s/1s), Queue Management, Volume Control, Repeat/Shuffle, Responsive Design, 30s Preview, Premium System - TÜM CORE ÖZELLİKLER AKTİF!
player.blade.php = 2,685 satır monolithic kod! İyileştirme değil, MODÜLER REFACTOR gerekiyor.
Stream endpoint güvenliksiz! Herkes konsol ile şarkı indirebilir.
Amaç: 2,685 satırlık player.blade.php'yi modüler hale getir
Dosya: resources/views/themes/muzibu/player.blade.php
| Satır Aralığı | İçerik | Satır Sayısı | Hedef Dosya |
|---|---|---|---|
| 1-380 | Auth Modal (Login/Register) | ~380 | components/auth-modal.blade.php |
| 383-476 | Player UI Bar | ~93 | components/player-bar.blade.php |
| 478-567 | Queue Panel | ~89 | components/queue-panel.blade.php |
| 570-1080 | CSS Styles | ~510 | css/player.css |
| 1082-2685 | JavaScript | ~1,603 | js/player/*.js (modüler) |
Hedef Klasör Yapısı:
public/themes/muzibu/js/player/
├── core/
│ ├── audio-engine.js # Howler.js + HLS.js wrapper
│ ├── crossfade.js # Crossfade logic
│ └── stream-security.js # Token + signed URL
├── features/
│ ├── queue-manager.js # Queue operations
│ ├── favorites.js # Like/unlike
│ ├── volume-control.js # Volume + mute
│ ├── repeat-shuffle.js # Repeat/shuffle modes
│ └── spa-navigation.js # History API
├── ui/
│ ├── player-ui.js # Progress bar, buttons
│ ├── queue-ui.js # Drag & drop
│ └── modals.js # Auth, limits modals
└── utils/
├── api-client.js # Fetch wrapper
├── formatters.js # Time, duration
└── debounce.js # Performance utils
Modüller arası state paylaşımı için merkezi store
// public/themes/muzibu/js/player-store.js
document.addEventListener('alpine:init', () => {
Alpine.store('player', {
// Audio state
isPlaying: false,
currentSong: null,
currentTime: 0,
duration: 0,
volume: 0.8,
isMuted: false,
// Queue state
queue: [],
queueIndex: 0,
// Playback state
repeatMode: 'off', // off|all|one
isShuffled: false,
isCrossfading: false,
// User state
isLoggedIn: false,
isPremium: false,
// Methods (delegated to modules)
play() { playerCore.play() },
pause() { playerCore.pause() },
next() { queueManager.next() },
prev() { queueManager.prev() }
});
});
Dosya: js/player/core/audio-engine.js
// Dual audio system - exactly as currently working
class AudioEngine {
constructor() {
this.howl = null; // Current Howler instance
this.howlNext = null; // Next track for crossfade
this.hlsPlayer = null; // HLS.js instance
this.hlsPlayerNext = null; // Next HLS for crossfade
this.audioElement = document.getElementById('hlsAudio');
this.audioElementNext = document.getElementById('hlsAudioNext');
}
// Load with automatic format detection
async loadSong(streamUrl, streamType) {
if (streamType === 'hls') {
return this.loadHLS(streamUrl);
} else {
return this.loadHowler(streamUrl);
}
}
loadHowler(url) {
this.howl = new Howl({
src: [url],
html5: true,
pool: 5,
volume: Alpine.store('player').volume,
onplay: () => this.onPlayCallback(),
onend: () => this.onEndCallback(),
onload: () => this.onLoadCallback()
});
return this.howl;
}
loadHLS(url) {
if (!Hls.isSupported()) {
// Safari native HLS
this.audioElement.src = url;
return;
}
this.hlsPlayer = new Hls({
enableWorker: true,
lowLatencyMode: false
});
this.hlsPlayer.loadSource(url);
this.hlsPlayer.attachMedia(this.audioElement);
}
// Crossfade to next track
crossfade(nextUrl, nextType, duration = 6000) {
// Implementation matches current crossfade logic
}
}
Strategy: Auth modal zaten ayrıldı, diğerleri de ayrılacak
| Component | Yeni Dosya | Status |
|---|---|---|
| Auth Modal | components/auth-modal.blade.php | ✅ ZATEN AYRI |
| Player Bar | components/player-bar.blade.php | 🔄 YAPILACAK |
| Queue Panel | components/queue-panel.blade.php | 🔄 YAPILACAK |
| Player CSS | css/player.css | 🔄 YAPILACAK |
Problem: `/api/muzibu/songs/{id}/stream` endpoint açık - konsol ile şarkı indirme mümkün!
Dosya: SongStreamController.php
// Laravel Sanctum token generation
Route::middleware(['auth:sanctum'])->group(function() {
Route::get('/songs/{id}/stream', [SongStreamController::class, 'stream']);
});
// Frontend: Add token to requests
fetch(`/api/muzibu/songs/${songId}/stream`, {
headers: {
'Authorization': `Bearer ${window.userToken}`,
'X-Requested-With': 'XMLHttpRequest'
}
});
URL'ler 5 dakika sonra expire olacak, IP-bound
// Backend - generate signed URL
public function generateSignedStreamUrl($songId, $userId) {
$timestamp = time();
$ipAddress = request()->ip();
$secret = config('app.stream_secret');
$signature = hash_hmac('sha256',
"{$songId}:{$userId}:{$timestamp}:{$ipAddress}",
$secret
);
return url("/api/muzibu/songs/{$songId}/stream?" . http_build_query([
't' => $timestamp,
'u' => $userId,
's' => $signature
]));
}
// Frontend kullanımı
const signedUrl = await fetch(`/api/muzibu/songs/${songId}/signed-url`).json();
audioEngine.loadSong(signedUrl.url, signedUrl.type);
IP bazlı throttling: 60 request/minute
// routes/api.php
Route::middleware('throttle:60,1')->group(function() {
Route::get('/songs/{id}/stream', ...);
Route::post('/songs/{id}/track-progress', ...);
});
Segment şifreleme, dynamic key rotation
// FFmpeg HLS conversion with encryption ffmpeg -i input.mp3 \ -hls_key_info_file keyinfo.txt \ -hls_time 10 \ -hls_list_size 0 \ -hls_segment_filename 'segment_%03d.ts' \ output.m3u8 // keyinfo.txt format: // key_url // key_file_path // initialization_vector
Not: Bu advanced feature, optional olabilir.
Player experience iyileştirmeleri (optional)
Real-time waveform visualization
// js/player/features/waveform.js
import WaveSurfer from 'wavesurfer.js';
const wavesurfer = WaveSurfer.create({
container: '#waveform',
waveColor: '#1db954',
progressColor: '#ffffff',
height: 80,
responsive: true
});
// Sync with Howler.js
howl.on('play', () => wavesurfer.play());
howl.on('pause', () => wavesurfer.pause());
howl.on('seek', (pos) => wavesurfer.seekTo(pos));
Album art'tan renk çıkarma
// Backend precalculation
use ColorThief\ColorThief;
$dominantColor = ColorThief::getColor($albumCoverPath);
$palette = ColorThief::getPalette($albumCoverPath, 5);
// DB'ye kaydet
$song->update([
'theme_color' => "rgb({$dominantColor[0]}, {$dominantColor[1]}, {$dominantColor[2]})"
]);
// Frontend kullanımı
document.body.style.setProperty('--player-accent', song.theme_color);
Web Audio API ile profesyonel EQ
class Equalizer {
constructor(audioContext) {
this.context = audioContext;
this.bands = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000];
this.filters = this.createFilters();
}
createFilters() {
return this.bands.map((freq, i) => {
const filter = this.context.createBiquadFilter();
filter.type = (i === 0) ? 'lowshelf' :
(i === this.bands.length - 1) ? 'highshelf' :
'peaking';
filter.frequency.value = freq;
filter.Q.value = 1;
filter.gain.value = 0;
return filter;
});
}
setBand(index, gain) {
this.filters[index].gain.value = gain;
}
}
| Faz | Task | Estimated Time | Priority |
|---|---|---|---|
| 1 | Mevcut player.blade.php analizi | 30 min | CRITICAL |
| 1 | Alpine Store kurulumu | 1 hour | CRITICAL |
| 1 | audio-engine.js extraction | 2 hours | CRITICAL |
| 1 | queue-manager.js extraction | 1.5 hours | CRITICAL |
| 1 | Diğer modüllerin extraction'ı | 3 hours | CRITICAL |
| 1 | Blade componentlere ayırma | 2 hours | HIGH |
| 1 | CSS dosyasına taşıma | 1 hour | HIGH |
| 2 | Bearer token authentication | 1 hour | HIGH |
| 2 | Signed URLs implementation | 2 hours | HIGH |
| 2 | Rate limiting setup | 30 min | HIGH |
| 2 | HLS AES-128 encryption | 4 hours | MEDIUM |
| 3 | WaveSurfer.js integration | 2 hours | MEDIUM |
| 3 | Color Thief theme system | 1.5 hours | MEDIUM |
| 3 | 10-band equalizer | 3 hours | LOW |
Total Estimated Time: ~24 hours
git add . git commit -m "🔧 CHECKPOINT: Before player refactoring (2,685 lines → modular)"
mkdir -p public/themes/muzibu/js/player/{core,features,ui,utils}
mkdir -p public/themes/muzibu/css/player/
mkdir -p resources/views/themes/muzibu/components/player/
// player-store.js oluştur ve app.blade.php'ye ekle
<script src="{{ asset('themes/muzibu/js/player-store.js') }}"></script>
En kritik kısım - Howler.js + HLS.js logic
// Kontrol listesi: ✓ Şarkı çalıyor mu? ✓ Crossfade çalışıyor mu? ✓ Queue next/prev çalışıyor mu? ✓ Volume control çalışıyor mu? ✓ Repeat/shuffle çalışıyor mu?