Muzibu Mobile App

Kapsamlı Analiz Raporu v3

Offline + DRM Flutter
30K+
Şarkı
125K+
Playlist
15 Saat
Günlük Oturum
B2B SaaS
İş Modeli

v3 Yeni Özellikler

Offline İndirme
Şifreli dosya indirme ve offline dinleme
DRM Koruması
Kopyalanamaz dosya formatı
Real-time Premium
Her şarkı geçişinde anlık kontrol

İçindekiler

Mevcut Sistem Analizi

Backend altyapısı ve mevcut koruma mekanizmaları

Basit Anlatım

Muzibu şu an şöyle çalışıyor:

  • Şarkılar sunucuda - Kullanıcı dinlerken anlık olarak sunucudan çekiyor
  • İndirme yok - Şarkı dosyasını telefona kaydetme özelliği mevcut değil
  • HLS şifrelemesi var - Şarkılar parçalara bölünüp şifreleniyor (AES-128)
  • Premium kontrolü - Kullanıcının üyelik bitiş tarihi kontrol ediliyor

Teknik Detaylar

Dosya Saklama Yapısı

/storage/tenant1001/app/public/muzibu/
├── songs/              # MP3 dosyaları
│   ├── mp3_128/        # 128kbps encode
│   ├── mp3_64/         # 64kbps encode
│   └── SONG_*.mp3      # Orijinal dosyalar
├── hls/                # HLS streaming
│   └── {songId}/
│       ├── master.m3u8 # Ana playlist
│       ├── ultralow/   # 32kbps
│       ├── low/        # 64kbps
│       ├── mid/        # 128kbps
│       ├── high/       # Orijinal
│       └── enc.bin     # AES-128 key

Mevcut Premium Kontrolü

// User Model
public function isPremium(): bool {
    // 5 dakika Redis cache
    return Cache::remember("premium:{$this->id}", 300, function () {
        return $this->subscription_expires_at?->isFuture() ?? false;
    });
}

Sorun: 5 dakika cache = Premium bitince 5 dakika daha dinleyebilir

Mevcut Streaming Endpoints

Endpoint Amaç Koruma
/api/muzibu/songs/{id}/stream Stream URL al Auth + Premium
/hls/muzibu/songs/{id}/master.m3u8 HLS playlist Signed URL
/hls-key/muzibu/songs/{id} Şifre anahtarı Signed Token
/api/.../download İndirme YOK!

Offline İndirme Sistemi

Şarkıları telefona indirip internet olmadan dinleme

Basit Anlatım

Ne yapacağız?

  • Kullanıcı beğendiği şarkıyı "İndir" butonuyla telefonuna kaydedecek
  • İnternet olmadan bile indirdiği şarkıları dinleyebilecek
  • İndirilen dosyalar şifreli olacak - başka uygulamalar açamaz
  • Premium bitince indirilen şarkılar kilitlenecek

Teknik Mimari

İndirme Akış Diyagramı

Uygulama İndir butonu
API download-token
Şifreleme AES-256 + User Key
Dosya .mzb formatı
Local DB SQLite + meta

Yeni Backend Endpoint

// POST /api/muzibu/songs/{id}/download-token
public function downloadToken(int $songId): JsonResponse
{
    $user = auth()->user();

    // 1. Premium kontrolü
    if (!$user->isPremium()) {
        return response()->json([
            'status' => 'subscription_required'
        ], 402);
    }

    // 2. User-specific encryption key oluştur
    $userKey = hash('sha256', $user->id . config('app.key') . $songId);

    // 3. Download token (1 saat geçerli)
    $token = Crypt::encryptString(json_encode([
        'user_id' => $user->id,
        'song_id' => $songId,
        'user_key' => $userKey,
        'expires' => now()->addHour()->timestamp,
        'subscription_expires' => $user->subscription_expires_at
    ]));

    // 4. İndirme kaydı oluştur
    UserDownload::create([
        'user_id' => $user->id,
        'song_id' => $songId,
        'downloaded_at' => now(),
        'subscription_expires_at' => $user->subscription_expires_at
    ]);

    return response()->json([
        'status' => 'ready',
        'download_url' => "/api/muzibu/songs/{$songId}/download?token={$token}",
        'user_key' => $userKey, // Mobil uygulama saklar
        'expires_at' => $user->subscription_expires_at
    ]);
}

Flutter İndirme Servisi

// download_service.dart
class DownloadService {
  final _dio = Dio();
  final _db = OfflineDatabase();
  final _crypto = CryptoService();

  Future<void> downloadSong(Song song) async {
    // 1. Token al
    final tokenResponse = await _dio.post(
      '/api/muzibu/songs/${song.id}/download-token'
    );

    final userKey = tokenResponse.data['user_key'];
    final downloadUrl = tokenResponse.data['download_url'];
    final expiresAt = tokenResponse.data['expires_at'];

    // 2. Dosyayı indir (chunk by chunk)
    final tempPath = await _downloadFile(downloadUrl);

    // 3. Şifrele ve .mzb olarak kaydet
    final encryptedPath = await _crypto.encryptFile(
      sourcePath: tempPath,
      userKey: userKey,
      songId: song.id,
    );

    // 4. Local DB'ye kaydet
    await _db.insertDownload(DownloadedSong(
      songId: song.id,
      title: song.title,
      artist: song.artist,
      filePath: encryptedPath,
      userKey: userKey,
      downloadedAt: DateTime.now(),
      subscriptionExpiresAt: DateTime.parse(expiresAt),
    ));

    // 5. Temp dosyayı sil
    await File(tempPath).delete();
  }
}

.mzb Dosya Formatı

Dosya Yapısı

┌─────────────────────────┐
│ Magic Bytes (4 byte)    │ "MZB\x01"
├─────────────────────────┤
│ Header Length (4 byte)  │ uint32
├─────────────────────────┤
│ Header (JSON, encrypted)│
│ - user_id               │
│ - song_id               │
│ - subscription_expires  │
│ - checksum              │
├─────────────────────────┤
│ IV (16 byte)            │ AES-256 IV
├─────────────────────────┤
│ Encrypted Audio Data    │ AES-256-CBC
│ (variable length)       │
└─────────────────────────┘

Güvenlik Özellikleri

  • User-Specific Key
    Her kullanıcı için farklı şifreleme anahtarı
  • Custom Uzantı
    .mzb uzantısı - sistem tanımıyor
  • Embedded User ID
    Dosya içinde kullanıcı kimliği gömülü
  • Expiry Check
    Abonelik tarihi dosya içinde

DRM / Kopyalama Koruması

İndirilen dosyaların PC'ye kopyalanmasını engelleme

Basit Anlatım

Neden önemli?

Kullanıcı şarkıyı indirip USB ile PC'ye kopyalarsa, abonelik bitse bile dinlemeye devam edebilir. Bunu engellemek için:

  • Dosyalar şifreli - Sadece bizim uygulama açabilir
  • Her kullanıcı farklı anahtar - Başkası açamaz
  • Dosya PC'ye kopyalansa bile çalışmaz
  • Premium bitince kilit - Şifreli dosya açılamaz

DRM Seçenekleri Karşılaştırması

Yöntem Güvenlik Zorluk Maliyet Öneri
Widevine + FairPlay
Google + Apple DRM
Çok Yüksek Zor $$$
Custom Encryption
AES-256 + User Key
Yüksek Kolay $ ÖNERİLEN
No Protection
Düz MP3
Yok En Kolay Ücretsiz

Önerilen Çözüm: Custom Encryption

4 Katmanlı Koruma

1
AES-256 Şifreleme
Askeri düzey şifreleme algoritması
2
User-Specific Key
user_id + app_key + song_id hash
3
Device Binding
Cihaz kimliği anahtara eklenir
4
Secure Storage
Key'ler Keychain/Keystore'da

Neden Widevine Değil?

B2B Hedef Kitle: İşletmeler, bireysel korsan değil
Widevine Lisansı: Yıllık $10K+ maliyet
Entegrasyon: 3-6 ay ekstra geliştirme
Custom Yeterli: %99 kullanıcı için engelleyici
Not: İleride ihtiyaç olursa Widevine eklenebilir

Real-time Premium Kontrolü

Her şarkı geçişinde anlık abonelik kontrolü

Basit Anlatım

Sorun: Premium biten kullanıcı hâlâ müzik dinleyebilir mi?

Çözüm: Her şarkı değiştiğinde sistem abonelik durumunu kontrol edecek:

  • Şarkı bitip yenisi başlarken anlık kontrol
  • Premium bittiyse kibar uyarı göster
  • Premium başladıysa anında aktif et
  • Offline modda local kontrol (son bilinen tarih)

Kontrol Akış Diyagramı

1. Şarkı Bitti onTrackEnd() tetiklendi
└── Sonraki şarkı yüklenmeden ÖNCE
2. Online mı?
├── Evet → API: /api/user/subscription-status
└── Hayır → Local: subscription_expires_at kontrol
3. Premium Aktif mi?
├── Evet → Sonraki şarkıyı çal
└── Hayır → Premium bitti modal göster

Backend Endpoint

// GET /api/user/subscription-status
// Hızlı, lightweight endpoint

public function subscriptionStatus(): JsonResponse
{
    $user = auth()->user();

    // Cache bypass - anlık kontrol
    $expiresAt = DB::table('users')
        ->where('id', $user->id)
        ->value('subscription_expires_at');

    $isPremium = $expiresAt &&
        Carbon::parse($expiresAt)->isFuture();

    return response()->json([
        'is_premium' => $isPremium,
        'expires_at' => $expiresAt,
        'server_time' => now()->toIso8601String(),
    ]);
}

Flutter Kontrolü

// player_service.dart

Future<bool> canPlayNext() async {
  // 1. Online kontrol (tercih edilen)
  if (await _connectivity.isOnline) {
    final status = await _api.getSubscriptionStatus();
    _localDb.updateSubscription(status);
    return status.isPremium;
  }

  // 2. Offline kontrol (fallback)
  final localExpiry = await _localDb.getSubscriptionExpiry();
  return localExpiry?.isAfter(DateTime.now()) ?? false;
}

void onTrackEnd() async {
  if (!await canPlayNext()) {
    _showPremiumExpiredModal();
    return;
  }
  playNext();
}

Önemli Notlar

Background Sync: Her 5 dakikada arka planda subscription kontrolü
Offline Grace: Son 24 saat içinde online olmuşsa güven
Push Notification: Subscription değişince anında bildir
UX: Şarkı ortasında kesme, geçişte kontrol et

Neden Flutter?

React Native yerine Flutter tercih etme sebepleri

Flutter vs React Native

Kriter Flutter React Native Kazanan
Audio Kütüphaneleri just_audio (native gapless) react-native-track-player Flutter
Bellek Yönetimi Dart GC + Native ARM JS Bridge overhead Flutter
15 Saat Oturum Stabil Memory leak riski Flutter
Eski Cihaz Desteği iOS 12+, Android 6+ iOS 13+, Android 6+ Flutter
Background Audio audio_service (isolate) 3rd party bağımlı Flutter
Performans 60fps native render Bridge latency Flutter
Öğrenme Eğrisi Dart (yeni dil) JavaScript/TypeScript React Native

Müzik Uygulaması İçin Kritik Avantajlar

Native Gapless Playback
just_audio ConcatenatingAudioSource ile şarkılar arası boşluk yok
HLS Native Desteği
just_audio HLS adaptive streaming built-in
Background İzolasyonu
audio_service ayrı isolate'te çalışır, UI donmaz
Düşük Bellek Tüketimi
15 saat oturum için kritik - JS bridge yok

Social Login

Google, Facebook ve Apple ile giriş

Google Sign-In
google_sign_in: ^6.2.1
  • OAuth 2.0
  • Firebase entegrasyonu
  • ID Token doğrulama
Facebook Login
flutter_facebook_auth: ^6.1.1
  • Facebook SDK
  • Access Token
  • Graph API
Apple Sign-In
sign_in_with_apple: ^5.0.0
  • iOS zorunlu
  • JWT doğrulama
  • Private Email Relay

In-App Purchase

Google Play ve App Store ödeme entegrasyonu

Google Play Billing
in_app_purchase: ^3.1.13
  • Subscription (aylık/yıllık)
  • Real-time Developer Notifications
  • Server-side doğrulama
  • %15-30 komisyon
App Store Connect
in_app_purchase: ^3.1.13
  • Auto-renewable subscriptions
  • StoreKit 2
  • Server-to-Server notifications
  • %15-30 komisyon

Subscription Sync Akışı

Store
Webhook
Backend
DB Update
Push

Kesintisiz Müzik Sistemi

Donma olmadan 15 saat kesintisiz müzik

Fallback Zinciri (Graceful Degradation)

HLS ABR (high) HLS mid (128k) HLS low (64k) HLS ultralow (32k) MP3 128k MP3 64k Sonraki Şarkı

İnternet kötüleşince kalite düşer, donma yerine kalite fedası yapılır

Gapless Playback (Boşluksuz Geçiş)

// Flutter - just_audio ile gapless playback

final playlist = ConcatenatingAudioSource(
  useLazyPreparation: true,  // Sadece sıradaki hazırla
  children: [
    AudioSource.uri(Uri.parse(song1.streamUrl)),
    AudioSource.uri(Uri.parse(song2.streamUrl)),
    AudioSource.uri(Uri.parse(song3.streamUrl)),
  ],
);

// Preload: Şarkı bitmeden 30 saniye önce sonrakini hazırla
player.positionStream.listen((position) {
  final remaining = player.duration - position;
  if (remaining < Duration(seconds: 30)) {
    preloadNextSong();
  }
});

15 Saat İçin Bellek Yönetimi

Playlist Limiti: Bellekte max 100 şarkı metadata
Buffer Yönetimi: Max 5 şarkı buffer, geçenler temizlenir
Image Cache: flutter_cache_manager + LRU eviction
Background Isolate: audio_service ayrı thread

API Endpoints

Mobil uygulama için gerekli API'ler

Endpoint Method Amaç Durum
/api/login POST Email/şifre girişi Mevcut
/api/muzibu/songs/{id}/stream GET Şarkı stream URL Mevcut
/api/muzibu/playlists GET Playlist listesi Mevcut
/api/auth/social POST Social login (Google/Facebook/Apple) Yeni
/api/user/subscription-status GET Anlık premium durumu Yeni
/api/muzibu/songs/{id}/download-token POST İndirme token al Yeni
/api/muzibu/songs/{id}/download GET Şifreli dosya indir Yeni
/api/webhooks/google-play POST Google Play bildirim Yeni
/api/webhooks/app-store POST App Store bildirim Yeni

Yol Haritası (7 Faz)

Adım adım geliştirme planı

1

Temel Altyapı

Flutter proje kurulumu, tema sistemi (dark mode), Dio HTTP client, interceptors, email/şifre login, token storage (secure), GoRouter navigasyon

Flutter 3.x Dio flutter_secure_storage
2

Müzik Player Çekirdeği

just_audio + audio_service entegrasyonu, HLS adaptive streaming, gapless playback, queue yönetimi, background playback, lock screen controls

just_audio audio_service HLS
3

UI/UX Katmanı

Ana sayfa, arama, playlist detay, şarkı detay, mini player, full player, genre/artist sayfaları, animasyonlar

Material 3 Animations cached_network_image
4

Offline + DRM Sistemi (YENİ)

Şarkı indirme, AES-256 şifreleme, .mzb formatı, offline playback, SQLite local DB, indirme yönetimi UI

encrypt sqflite path_provider
5

Premium + Auth Genişletme (YENİ)

Real-time premium kontrolü, social login (Google/Facebook/Apple), subscription status sync, push notifications

google_sign_in firebase_messaging sign_in_with_apple
6

In-App Purchase (YENİ)

Google Play Billing, App Store entegrasyonu, subscription yönetimi, webhook endpoints, receipt validation

in_app_purchase purchases_flutter
7

Test & Yayın

Unit/widget testleri, integration testleri, beta test, store listing hazırlığı, App Store / Google Play yayını

flutter_test Firebase Test Lab

Riskler ve Çözümler

Potansiyel sorunlar ve önlemler

Memory Leak (15 Saat)

Uzun oturumlarda bellek sızıntısı

Çözüm: Playlist limiti, buffer cleanup, isolate kullanımı

Kötü İnternet

Yavaş bağlantıda şarkı takılması

Çözüm: HLS ABR, fallback zinciri, preload sistemi

Premium Gecikmesi

Ödeme sonrası premium aktif olmama

Çözüm: Webhook + push notification + polling fallback

Dosya Kopyalama

İndirilen dosyaların PC'ye kopyalanması

Çözüm: AES-256 + user key + device binding + custom format

Flutter Paketleri

Kullanılacak ana paketler

# pubspec.yaml

dependencies:
  flutter:
    sdk: flutter

  # Audio
  just_audio: ^0.9.36          # Müzik player
  audio_service: ^0.18.12       # Background playback
  audio_session: ^0.1.18        # Audio session yönetimi

  # Network
  dio: ^5.4.0                    # HTTP client
  connectivity_plus: ^5.0.2     # Bağlantı durumu

  # Storage
  flutter_secure_storage: ^9.0.0 # Token saklama
  sqflite: ^2.3.0               # Local database
  path_provider: ^2.1.1         # Dosya yolları

  # Encryption (YENİ)
  encrypt: ^5.0.3               # AES-256 şifreleme
  crypto: ^3.0.3                # Hash fonksiyonları

  # Auth (YENİ)
  google_sign_in: ^6.2.1        # Google login
  flutter_facebook_auth: ^6.1.1 # Facebook login
  sign_in_with_apple: ^5.0.0    # Apple login

  # Payments (YENİ)
  in_app_purchase: ^3.1.13     # IAP unified
  purchases_flutter: ^6.17.0   # RevenueCat (opsiyonel)

  # Push
  firebase_messaging: ^14.7.9  # Push notifications

  # UI
  cached_network_image: ^3.3.0 # Image cache
  go_router: ^12.1.1           # Navigation
  flutter_riverpod: ^2.4.9     # State management