22 Şubat 2026 • v2 • Muzibu.com.tr
Müzik dinlerken şarkılar en düşük kalitede çalıyordu. İnternet hızı ne kadar iyi olursa olsun, herkes maksimum 64kbps (telefon kalitesinin bile altında) ses duyuyordu. Orijinal kalite hiç çalmıyordu.
Şarkıları korumak için kullandığımız şifreleme sisteminde bir hata vardı. Orijinal kaliteli dosyaların "şifre kombinasyonu" (IV değeri) sıfır olarak yazılmıştı. Bu yüzden orijinal kalite çözülemiyordu ve biz de onu tamamen kapattık. Sonuç: herkes düşük kalitede dinliyordu.
Dosya: Modules/Muzibu/App/Jobs/ConvertToHLSJob.php:95-98
// ESKİ KOD (HATALI):
$keyInfoContent = $keyUri . "\n" . $keyPath . "\n";
// 3. satır (IV) yazılmamış! FFmpeg IV=0x0000...0000 kullanıyor
// YENİ KOD (DÜZELTİLMİŞ):
$iv = bin2hex(random_bytes(16));
$keyInfoContent = $keyUri . "\n" . $keyPath . "\n" . $iv;
// Artık her şarkı benzersiz IV alıyor
Neden önemli: AES-128'de IV her dosya için benzersiz olmalı. Sıfır IV hem güvenlik açığı hem de HLS.js'te "decryptdata unset or changed" hatasının ana nedeni.
Dosya: app/Services/Muzibu/HLSService.php:509
// ESKİ KOD (HATALI):
$highBitrate = round($totalSize / ($segCount * self::CHUNK_DURATION) * 8);
// CHUNK_DURATION=4sn ama orijinal segmentler 10sn → hesap 2.5x şişik
// 268kbps yerine 669kbps yazıyordu
// YENİ KOD (DÜZELTİLMİŞ):
$playlistContent = file_get_contents($highPlaylist);
preg_match_all('/#EXTINF:([\d.]+),/', $playlistContent, $matches);
foreach ($matches[1] as $dur) { $totalDuration += (float) $dur; }
$highBitrate = round($totalSize * 8 / $totalDuration);
// Playlist'ten gerçek süre okunuyor
Dosya: Modules/Muzibu/App/Http/Controllers/Api/SongStreamController.php:746-755
// ESKİ KOD: master.m3u8'den orijinal kaliteyi tamamen siliyordu
$content = preg_replace(
'/^#EXT-X-STREAM-INF:[^\n]*\nplaylist\.m3u8\n?/m',
'', $content // HIGH SİLİNİYORDU!
);
// YENİ KOD: Filtre kaldırıldı, yerine:
// 1. High playlist'e query parametreleri ekleniyor
// 2. High segmentleri storage URL'sine yönlendiriliyor
// 3. High key URI'sine &level=high ekleniyor (race condition önlemi)
storage/tenant{ID}/app/public/muzibu/hls/{song_id}/
├── enc.bin ← 16 byte AES-128 key (TÜM kaliteler paylaşır)
├── enc.keyinfo ← FFmpeg keyinfo (key URL + key path + IV)
├── master.m3u8 ← Ana playlist (3 kaliteyi listeler)
├── playlist.m3u8 ← Orijinal kalite playlist
├── segment-000.ts ← Orijinal kalite segmentleri (6sn)
├── segment-001.ts
├── ...
├── ultralow/
│ ├── enc.keyinfo ← Farklı IV!
│ ├── playlist.m3u8
│ ├── segment-000.ts ← 32kbps segmentler (4sn)
│ └── ...
└── low/
├── enc.keyinfo ← Farklı IV!
├── playlist.m3u8
└── segment-000.ts ← 64kbps segmentler (4sn)
Satır 1: Key URI → https://domain.com/storage/tenant{ID}/muzibu/hls/{song_id}/enc.bin
Satır 2: Key Path → /var/www/.../storage/tenant{ID}/app/public/muzibu/hls/{song_id}/enc.bin
Satır 3: IV → a1b2c3d4e5f6789012345678abcdef01 (32 hex char = 16 byte)
Bug: Satır 3 yazılmamıştı! FFmpeg boş satırı görünce IV=0x0000...0000 kullanıyordu.
| Sistem | Dosya | Ne Üretiyor | IV | Segment |
|---|---|---|---|---|
| ConvertToHLSJob | Modules/.../Jobs/ConvertToHLSJob.php |
Orijinal (high) | Eksikti! → 0x00 | 10sn → 6sn |
| HLSService | app/Services/Muzibu/HLSService.php |
Ultralow + Low | random_bytes(16) ✓ | 4sn ✓ |
ConvertToHLSJob Horizon queue üzerinden çalışıyor (ilk upload). HLSService ise variant'ları sonradan ekliyor. İkisi farklı davranıyordu.
| Kalite | Bitrate | Sample Rate | Kanal | Segment | Kullanım |
|---|---|---|---|---|---|
| ultralow | 32 kbps | 22050 Hz | Mono | 4 sn | Çok kötü bağlantı, ilk başlangıç |
| low | 64 kbps | 22050 Hz | Mono | 4 sn | Orta bağlantı |
| high (orijinal) | ~180-280 kbps (MP3'e göre) | 48000 Hz | Stereo | 6 sn | Normal/iyi bağlantı |
ffmpeg -i {MP3_PATH} \ -map 0:a \ -c:a aac \ -b:a {TARGET_BITRATE} \ -profile:a aac_low \ -ar 48000 \ -ac 2 \ -vn \ -hls_key_info_file {HLS_DIR}/enc.keyinfo \ -start_number 0 \ -hls_time 6 \ -hls_list_size 0 \ -hls_segment_filename {HLS_DIR}/segment-%03d.ts \ -f hls \ {HLS_DIR}/playlist.m3u8 -y
Ana sunucuda (muzibu.com) uygulanacak adımlar. Test sunucusunda (mztest) 66 şarkıda başarıyla test edildi.
Önce kodu düzelt ki yeni encode edilen şarkılar doğru olsun:
Modules/Muzibu/App/Jobs/ConvertToHLSJob.php
→ IV eklendi + hls_time 10→6
app/Services/Muzibu/HLSService.php
→ Bitrate hesabı düzeltildi
Modules/Muzibu/App/Http/Controllers/Api/SongStreamController.php
→ High filtre kaldırıldı
Tinker ile HLS'i olan tüm şarkıları dosyaya yaz:
php artisan tinker --execute="
\$tenant = App\Models\Tenant::find(1001);
tenancy()->initialize(\$tenant);
\$songs = DB::connection('tenant')->table('muzibu_songs')
->whereNotNull('hls_path')->get(['song_id','file_path']);
foreach(\$songs as \$s) {
echo \$s->song_id . '|' . \$s->file_path . PHP_EOL;
}
" > /tmp/hls-songs.txt
Not: Bu script sadece orijinal (high) kaliteyi yeniden encode eder. Ultralow ve low'a dokunmaz. Zaten doğru IV'si olan şarkıları atlar.
#!/bin/bash # re-encode-high.sh — Orijinal kaliteyi IV + 6sn segment ile yeniden encode et HLS_BASE="/path/to/storage/tenant{ID}/app/public/muzibu/hls" SONGS_BASE="/path/to/storage/tenant{ID}/app/public/muzibu/songs" DOMAIN="muzibu.com" TENANT_ID="1001" SONG_LIST="/tmp/hls-songs.txt" SUCCESS=0; FAIL=0; SKIP=0; TOTAL=0 for dir in "$HLS_BASE"/*/; do SONG_ID=$(basename "$dir") PL="$dir/playlist.m3u8" # Sadece IV=0x00 olanları işle (zaten doğru olanları atla) [ ! -f "$PL" ] && continue grep -q "IV=0x00000000" "$PL" || { SKIP=$((SKIP+1)); continue; } TOTAL=$((TOTAL + 1)) # MP3 dosyasını bul MP3_FILE=$(grep "^${SONG_ID}|" "$SONG_LIST" | cut -d'|' -f2) MP3_PATH="$SONGS_BASE/$MP3_FILE" [ -z "$MP3_FILE" ] || [ ! -f "$MP3_PATH" ] && { FAIL=$((FAIL+1)); continue; } KEY_PATH="$dir/enc.bin" [ ! -f "$KEY_PATH" ] && { FAIL=$((FAIL+1)); continue; } # Eski orijinal segmentleri sil rm -f "$dir"/segment-*.ts "$dir/playlist.m3u8" # Yeni IV oluştur ve enc.keyinfo'yu güncelle NEW_IV=$(openssl rand -hex 16) KEY_URI="https://$DOMAIN/storage/tenant$TENANT_ID/muzibu/hls/$SONG_ID/enc.bin" printf '%s\n%s\n%s' "$KEY_URI" "$KEY_PATH" "$NEW_IV" > "$dir/enc.keyinfo" # Orijinal bitrate'i tespit et BR=$(/usr/bin/ffprobe -v quiet -show_entries format=bit_rate \ -of csv=p=0 "$MP3_PATH" 2>/dev/null) BRK=$((BR / 1000)) # Bitrate mapping if [ "$BRK" -le 128 ]; then T="128k" elif [ "$BRK" -le 160 ]; then T="160k" elif [ "$BRK" -le 192 ]; then T="192k" elif [ "$BRK" -le 256 ]; then T="256k" elif [ "$BRK" -le 320 ]; then T="320k" else T="${BRK}k"; fi # FFmpeg ile yeniden encode (6sn segment, IV ile) /usr/bin/ffmpeg -i "$MP3_PATH" \ -map 0:a -c:a aac -b:a "$T" -profile:a aac_low \ -ar 48000 -ac 2 -vn \ -hls_key_info_file "$dir/enc.keyinfo" \ -start_number 0 -hls_time 6 -hls_list_size 0 \ -hls_segment_filename "$dir/segment-%03d.ts" \ -f hls "$dir/playlist.m3u8" -y 2>/dev/null [ $? -ne 0 ] && { FAIL=$((FAIL+1)); continue; } # Master playlist'i yeniden oluştur (doğru bitrate ile) TD=$(grep '#EXTINF:' "$dir/playlist.m3u8" | \ sed 's/#EXTINF://' | sed 's/,$//' | \ python3 -c "import sys; print(sum(float(l) for l in sys.stdin))") TS=$(du -cb "$dir"/segment-*.ts 2>/dev/null | tail -1 | cut -f1) HBW=$(python3 -c "print(int(round($TS * 8 / $TD)))") M="$dir/master.m3u8" echo "#EXTM3U" > "$M" [ -f "$dir/ultralow/playlist.m3u8" ] && \ printf '#EXT-X-STREAM-INF:BANDWIDTH=32000,CODECS="mp4a.40.2",NAME="ultralow"\nultralow/playlist.m3u8\n' >> "$M" [ -f "$dir/low/playlist.m3u8" ] && \ printf '#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS="mp4a.40.2",NAME="low"\nlow/playlist.m3u8\n' >> "$M" [ -f "$dir/mid/playlist.m3u8" ] && \ printf '#EXT-X-STREAM-INF:BANDWIDTH=128000,CODECS="mp4a.40.2",NAME="mid"\nmid/playlist.m3u8\n' >> "$M" printf "#EXT-X-STREAM-INF:BANDWIDTH=${HBW},CODECS=\"mp4a.40.2\",NAME=\"high\"\nplaylist.m3u8\n" >> "$M" SC=$(ls "$dir"/segment-*.ts 2>/dev/null | wc -l) echo "[$TOTAL] $SONG_ID — $T ${SC}seg BW=${HBW}" SUCCESS=$((SUCCESS + 1)) done echo "" echo "Başarılı: $SUCCESS | Atlandı: $SKIP | Başarısız: $FAIL | Toplam: $TOTAL"
# Arka planda çalıştır (nohup ile)
nohup bash re-encode-high.sh > /tmp/re-encode.log 2>&1 &
# İlerlemeyi takip et
tail -f /tmp/re-encode.log
# Tamamlanınca doğrula (sıfır IV kalmış mı?)
for dir in $HLS_BASE/*/; do
PL="$dir/playlist.m3u8"
[ -f "$PL" ] && grep -q "IV=0x00000000" "$PL" && echo "BOZUK: $(basename $dir)"
done
Paralel çalıştırma: GNU parallel veya xargs ile 4 çekirdek kullanılırsa ~10-16 saat'e düşer. Ama CPU yükünü izlemek lazım (canlı sunucu).
# 1. Sıfır IV kalmış mı?
BROKEN=0; OK=0
for dir in $HLS_BASE/*/; do
PL="$dir/playlist.m3u8"; [ ! -f "$PL" ] && continue
grep -q "IV=0x00000000" "$PL" && BROKEN=$((BROKEN+1)) || OK=$((OK+1))
done
echo "OK: $OK | Bozuk: $BROKEN"
# 2. master.m3u8'de high variant var mı?
grep -l "NAME=\"high\"" $HLS_BASE/*/master.m3u8 | wc -l
# 3. Rastgele bir şarkıyı kontrol et
SAMPLE=$(ls -d $HLS_BASE/*/ | shuf -n 1)
echo "=== $(basename $SAMPLE) ==="
grep "EXT-X-KEY" "$SAMPLE/playlist.m3u8" # IV sıfır değil mi?
cat "$SAMPLE/master.m3u8" # 3 kalite var mı?
ls "$SAMPLE"/segment-*.ts | wc -l # Segment sayısı
| Dosya | Değişiklik |
|---|---|
Modules/Muzibu/App/Jobs/ConvertToHLSJob.php |
IV eklendi (random_bytes) + hls_time 10→6 |
app/Services/Muzibu/HLSService.php |
generateMasterPlaylist() bitrate hesabı: EXTINF sürelerinden |
Modules/.../SongStreamController.php |
High filtre kaldırıldı + query/segment/level=high eklendi |
storage/.../hls/*/ |
66 şarkı yeniden encode (test) — 30.000 şarkı bekliyor (production) |
Encode sonrası doğrulama: IV doğru: 66 | IV sıfır: 0
Bitrate aralığı: 192k-256k (MP3 orijinaline bağlı)
Gerçek bandwidth: 210-285 kbps (master.m3u8'de doğru raporlanıyor)