HLS Optimizasyonları + Bunny CDN

PC Çalışması (P2-10/11/12) + Safari iOS Sorunları + Geçiş Planı

23 Mart 2026 • v2

🐰 İlgili: Bunny.net Hesap Dokümantasyonu (Tam Envanter)

🎯 Amaç ve Karşılaşılan Sorunlar

Ana Amaç: Bunny CDN ile Müzik Sunumu

Müzik dosyalarını (HLS + MP3) Bunny CDN üzerinden sunarak:

  • Sunucu yükünü azaltmak — 40K+ şarkı, yoğun bandwidth
  • Daha hızlı yükleme — CDN edge sunucuları kullanıcıya yakın
  • Maliyet optimizasyonu — Bunny ucuz, bandwidth tasarrufu
  • Güvenlik koruma — AES-128 şifreli HLS, key bizde kalacak

🚨 Karşılaşılan Ana Sorunlar

1. Safari iOS Cross-Origin Cookie Sorunu

Safari iOS'un native HLS player'ı farklı domain'e (bizim sunucu) istek atarken cookie göndermiyor. Playlist Bunny'de, şifreleme anahtarı bizde → 401 Unauthorized hatası.

Durum: ❌ Çözülemedi (blob URL da desteklenmiyor)

2. Safari iOS'ta HLS.js Desteği Yok

Hls.isSupported() = false — Safari iOS kendi native player'ını kullanıyor. CachingKeyLoader devreye giremiyor, inline key cache'i kullanılamıyor.

Çözüm: Safari iOS için MP3 fallback (test edilecek)

3. Tüm MP3'ler Henüz Yüklenmedi

Bunny'ye sadece HLS dosyaları yüklendi. Safari iOS fallback için MP3'ler de lazım. 40K+ şarkının MP3 versiyonları yüklenecek.

Durum: ⏳ Yükleme devam edecek

4. Şifreleme Anahtarı Güvenliği

enc.key dosyaları Bunny'ye yüklenemez (public olur, güvenlik riski). Key her zaman bizim sunucudan gelmeli, auth kontrolü şart.

Karar: Anahtarlar local'da kalacak

✅ Şu Anki Çözüm

Bunny CDN local mod'da — tüm müzikler kendi sunucumuzdan çalıyor. PC ve mobil dahil her yerde sorunsuz çalışıyor.

# .env
BUNNY_STORAGE_MODE=local

Basit Anlatım

Ne yapıldı? HLS müzik çalma sistemi optimize edildi. Şarkı başlatma süresi 4 HTTP isteğinden 1'e düşürüldü.

Nasıl? Master playlist, şifreleme anahtarı ve variant playlist'ler ilk API yanıtına gömüldü (inline).

Bunny CDN? Müzik dosyalarını CDN'den sunmak için entegre edildi ama Safari iOS sorunu çıktı.

Şu an? PC'de her şey çalışıyor. Bunny şu an kapalı (local mod), Safari sorunu çözülünce açılacak.

Bölüm 1: PC HLS Optimizasyonları (Tamamlandı ✅)

Önceki Durum (Yavaş)

┌─────────────────────────────────────────────────────────────┐
│  Şarkı Başlatma (4 HTTP Round-Trip)                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. /api/songs/{id}/stream  ─────►  stream_url al          │
│  2. master.m3u8             ─────►  variant listesi al     │
│  3. playlist.m3u8           ─────►  segment listesi al     │
│  4. /hls-key/{id}           ─────►  şifre anahtarı al      │
│  5. segment-000.ts          ─────►  ilk segment (çalmaya   │
│                                      başla)                │
│                                                             │
│  Toplam: ~400-600ms gecikme                                │
└─────────────────────────────────────────────────────────────┘

Yeni Durum (Hızlı - P2-10/11/12)

┌─────────────────────────────────────────────────────────────┐
│  Şarkı Başlatma (1 HTTP + Inline Data)                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. /api/songs/{id}/stream  ─────►  HER ŞEY BİRDEN:        │
│     {                                                       │
│       "stream_url": "...",                                  │
│       "master_inline": "#EXTM3U...",     ← P2-10           │
│       "key_inline": "base64...",         ← P2-11           │
│       "playlists_inline": {              ← P2-12           │
│         "playlist_32.m3u8": "...",                          │
│         "playlist_64.m3u8": "...",                          │
│         "playlist_128.m3u8": "...",                         │
│         "playlist.m3u8": "..."                              │
│       }                                                     │
│     }                                                       │
│                                                             │
│  2. segment-000.ts          ─────►  ilk segment (çalmaya   │
│                                      başla)                │
│                                                             │
│  Toplam: ~100-150ms gecikme (4x hızlanma!)                 │
└─────────────────────────────────────────────────────────────┘

P2-10: Master Inline

master.m3u8 içeriği API yanıtına gömülü. Blob URL oluşturulup HLS.js'e veriliyor.

master_inline: "#EXTM3U..."

P2-11: Key Inline

AES-128 şifreleme anahtarı base64 olarak gömülü. _hlsKeyCache'e yazılıyor.

key_inline: "base64..."

P2-12: Playlists Inline

Tüm variant playlist'ler (32k, 64k, 128k, orig) gömülü. _hlsPlaylistCache'e yazılıyor.

playlists_inline: {...}

CachingKeyLoader Sistemi

HLS.js'in varsayılan loader'ı override edildi. Key ve playlist istekleri önce cache'e bakıyor:

class CachingKeyLoader extends Hls.DefaultConfig.loader {
    load(context, config, callbacks) {
        // 1. Key isteği mi?
        if (context.type === 'key') {
            var cachedKey = _hlsKeyCache[normalizedUrl];
            if (cachedKey) {
                console.log('🔑 Key served from CACHE (0ms)');
                callbacks.onSuccess(...); // Anında dön
                return;
            }
        }

        // 2. Playlist isteği mi?
        if (context.url.includes('.m3u8')) {
            var cachedPlaylist = _hlsPlaylistCache[url];
            if (cachedPlaylist) {
                console.log('📋 P2-12: Playlist served from cache (0ms)');
                callbacks.onSuccess(...); // Anında dön
                return;
            }
        }

        // 3. Cache'de yoksa normal HTTP isteği
        super.load(context, config, callbacks);
    }
}

Console Çıktısı (Başarılı)

🔑 P2-11 DIRECT: Key inline → cache (16 bytes, song: 23390)
🚀 P2-10: master.m3u8 inline blob kullanıldı
📋 P2-12: 4 playlist inline → cache
🔄 HlsPool.acquire: 4 tid=4 → 3i / 1a
🔍 CachingKeyLoader.load() CALLED: manifest text blob:...
🔍 CachingKeyLoader.load() CALLED: level text playlist.m3u8
📋 P2-12: Playlist served from cache (0ms)
🔑 Key served from CACHE (0ms, 16 bytes)
✅ Şarkı çalıyor!

Bölüm 2: Bunny CDN Entegrasyonu

Hybrid Mode Mimarisi

┌─────────────────────────────────────────────────────────────┐
│  Bunny Hybrid Serving                                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Redis Set: bunny:uploaded_songs                            │
│  ├── 23390 (Bunny'de var)                                   │
│  ├── 22287 (Bunny'de var)                                   │
│  └── ...                                                    │
│                                                             │
│  SignedUrlService.generateHlsUrl($songId):                  │
│  ├── Redis.sismember('bunny:uploaded_songs', $songId)       │
│  ├── TRUE  → https://muzibu-audio-cdn.b-cdn.net/hls/...     │
│  └── FALSE → https://www.muzibu.com/hls/muzibu/songs/...    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

BunnyStorageService.php

  • • uploadFile() → Redis SADD sonrası
  • • deleteFile() → Redis SREM sonrası
  • • HLS + MP3 dosya yükleme

SignedUrlService.php

  • • isSongOnBunny() → Redis SISMEMBER
  • • generateBunnyHlsUrl()
  • • generateBunnyMp3Url()

Bölüm 3: Safari iOS Sorunu

Problem

┌─────────────────────────────────────────────────────────────┐
│  Safari iOS'ta HLS Key Hatası                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Safari iOS: Hls.isSupported() = FALSE                      │
│  → HLS.js çalışmıyor, CachingKeyLoader devrede değil        │
│  → Safari kendi native HLS player'ını kullanıyor            │
│                                                             │
│  Playlist:  muzibu-audio-cdn.b-cdn.net  (Bunny)             │
│  Key:       www.muzibu.com/hls-key/     (Bizim sunucu)      │
│                                                             │
│  Cross-origin istek → Safari cookie göndermiyor → 401       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Console Hatası

[Log] 🔍 HLS PATH DEBUG: Hls.isSupported()= false canPlayType= maybe
[Log] 🍎 HLS PATH: Using Safari NATIVE HLS (not HLS.js!)
[Error] Failed to load resource: 401 Unauthorized
        https://www.muzibu.com/hls-key/muzibu/songs/23390

Denenen Çözümler

BAŞARISIZ

1. Session Middleware

Safari cross-origin'de cookie göndermiyor, session işe yaramaz.

BAŞARISIZ

2. Blob URL + Data URI Key

Safari native HLS blob:// URL desteklemiyor. "NotSupportedError"

TEST EDİLMEDİ

3. Safari iOS için MP3 Fallback

Safari iOS tespit → ?force_mp3=1 → MP3 URL dön. Muhtemelen çalışır.

REDDEDİLDİ

4. Bunny'ye enc.key Yükleme

Güvenlik riski - anahtarlar public olmamalı.

REDDEDİLDİ

5. Playlist'leri Yeniden Encode

40K+ şarkı var, çok uzun sürer.

Mevcut Durum (23 Mart 2026)

.env Ayarları

BUNNY_STORAGE_ENABLED=true
BUNNY_STORAGE_MODE=local      ← Şu an LOCAL
BUNNY_FALLBACK_TO_LOCAL=true

Çalışma Durumu

  • ✅ Chrome / Firefox / Edge → HLS (P2-10/11/12)
  • ✅ Safari macOS → HLS çalışıyor
  • ✅ Safari iOS → HLS çalışıyor (local)
  • ✅ Android → HLS çalışıyor
  • ⏸️ Bunny CDN → Kapalı (local mod)

Bölüm 5: Bunny CDN Geçiş Planı

Ön Koşullar (Checklist)

Geçiş Adımları

  1. 1

    MP3 Yükleme Tamamla

    php artisan bunny:migrate --type=mp3 --batch=100
  2. 2

    Safari iOS MP3 Fallback Ekle

    // player-core.js
    var isSafariIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) &&
                      /Safari/.test(navigator.userAgent) &&
                      !navigator.userAgent.includes('CriOS');
    
    if (isSafariIOS) _streamUrl += '?force_mp3=1';
    
    // SongController.php
    if ($request->has('force_mp3')) {
        return MP3_URL;
    }
  3. 3

    Local Mode'da Test

    Safari iOS'ta MP3 fallback çalışıyor mu kontrol et

  4. 4

    Hybrid Moda Geç

    # .env
    BUNNY_STORAGE_MODE=hybrid
    
    # Cache temizle
    php artisan config:cache && php artisan cache:clear
  5. 5

    Tüm Cihazlarda Test

    Chrome/Firefox/Edge→ HLS from Bunny
    Safari macOS→ HLS from Bunny + Local Key
    Safari iOS→ MP3 from Bunny (fallback)
    Android→ HLS from Bunny

Sorun Olursa Geri Al

# .env
BUNNY_STORAGE_MODE=local

# Cache temizle
php artisan config:cache

Git Geçmişi

eb0acb8ff  23 Mar 05:XX  🔙 Safari iOS denemelerini geri al + Bunny local moda
83d1043bd  23 Mar 05:XX  🍎 Safari iOS HLS Denemeleri (HATALI)
f2ef4bcbd  22 Mar 22:56  🐰 Bunny Hybrid Serving + Redis Tracking
b489ec4fa  22 Mar 22:41  🐰 Bunny Migration optimizasyonları
55ff250f8  21 Mar 00:27  🐰 Bunny Storage Zone entegrasyonu
6b7340e26  01 Mar 21:19  🚀 P2-10/11/12 inline + HLS iyileştirmeleri
9af2f34e4  01 Mar 01:42  🔧 Fast-start + Hover preload + Rebuffer fix

İlgili Dosyalar

PC HLS (P2-10/11/12)

  • 📁 player-core.js (CachingKeyLoader)
  • 📁 SongStreamController.php (inline data)
  • 📁 HLSService.php

Bunny CDN

  • 📁 BunnyStorageService.php
  • 📁 SignedUrlService.php
  • 📁 .env (BUNNY_STORAGE_*)