| Kullanıcı | Kural | Kontrol |
|---|---|---|
| 🚫 Guest | Her şarkının 30 saniyesi | Frontend fade-out (güvenlik gereksiz) |
| 👤 Normal Üye | Günde 5 şarkı (30+ saniye dinlenen) | Backend API: muzibu_song_plays tablosundan count |
| ⭐ Premium/Deneme | Sınırsız | Backend: user.isPremium() check |
muzibu_song_plays (ZATEN VAR!) ├── id ├── song_id ├── user_id ├── ip_address ├── user_agent ├── device_type ├── created_at └── updated_at
Migration ekle:
// 2025_11_26_add_duration_to_song_plays.php
Schema::table('muzibu_song_plays', function (Blueprint $table) {
$table->integer('duration_listened')->default(0)
->after('user_id')
->comment('Kaç saniye dinlendi (30+ = tam dinleme)');
// Index ekle (performans için)
$table->index(['user_id', 'created_at', 'duration_listened'],
'user_daily_plays_idx');
});
- ❌ Yeni tablo YOK
- ❌ users tablosuna alan ekleme YOK
- ✅ Sadece 1 kolon ekle: duration_listened
- ✅ Mevcut song_plays mekanizması çalışıyor zaten
// app/Models/User.php
/**
* Kullanıcı bugün kaç şarkı dinledi? (30+ saniye)
*/
public function getTodayPlayedCount(): int
{
return DB::table('muzibu_song_plays')
->where('user_id', $this->id)
->where('duration_listened', '>=', 30)
->whereDate('created_at', today())
->count();
}
/**
* Şarkı çalabilir mi?
*/
public function canPlaySong(): bool
{
// Premium/Trial → Sınırsız
if ($this->isPremium() || $this->isTrialActive()) {
return true;
}
// Normal üye → Günde 5 şarkı
return $this->getTodayPlayedCount() < 5;
}
/**
* Kalan şarkı hakkı
*/
public function getRemainingPlays(): int
{
if ($this->isPremium() || $this->isTrialActive()) {
return -1; // Sınırsız
}
return max(0, 5 - $this->getTodayPlayedCount());
}
// Modules/Muzibu/app/Http/Controllers/Api/SongStreamController.php
public function stream(int $songId): JsonResponse
{
$user = auth()->user();
// Guest → 30 saniye preview (frontend'de fade-out)
if (!$user) {
return response()->json([
'status' => 'preview',
'stream_url' => $song->getStreamUrl(),
'preview_duration' => 30,
'message' => 'Kayıt olun, tam dinleyin'
]);
}
// Limit kontrolü (BACKEND'DE!)
if (!$user->canPlaySong()) {
return response()->json([
'status' => 'limit_exceeded',
'played_today' => $user->getTodayPlayedCount(),
'limit' => 5,
'message' => 'Günlük 5 şarkı limitiniz doldu'
], 403);
}
// OK → Stream ver
return response()->json([
'status' => 'ok',
'stream_url' => $song->getStreamUrl(),
'remaining' => $user->getRemainingPlays()
]);
}
// Frontend her 5 saniyede progress rapor eder
// Backend son raporu saklar
public function trackPlay(Request $request, int $songId): JsonResponse
{
$durationListened = $request->input('duration', 0);
// Güvenlik: Maximum şarkı süresi
$song = Song::findOrFail($songId);
if ($durationListened > $song->duration_seconds + 10) {
return response()->json(['error' => 'invalid_duration'], 400);
}
// Insert/Update
DB::table('muzibu_song_plays')->updateOrInsert(
[
'user_id' => auth()->id(),
'song_id' => $songId,
'created_at' => now() // Aynı gün, aynı şarkı
],
[
'duration_listened' => $durationListened,
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent(),
'device_type' => $this->detectDevice(),
'updated_at' => now()
]
);
return response()->json([
'success' => true,
'remaining' => auth()->user()->getRemainingPlays()
]);
}
Frontend: API.stream(songId) çağrısı yapar
Query:
SELECT COUNT(*) FROM muzibu_song_plays WHERE user_id = ? AND created_at >= TODAY() AND duration_listened >= 30
Response 403:
{ "error": "limit_exceeded", "played_today": 5 }
Frontend modal gösterir, şarkı çalmaz
Response 200:
{ "stream_url": "...", "remaining": 2 }
Frontend şarkıyı çalar
Her 5 saniyede: API.trackPlay(songId, currentTime)
Örnek: 10s, 15s, 20s, 25s, 30s, 35s...
duration_listened güncellenir
30+ saniye olduğunda → "Tam dinleme" sayılır
Bir sonraki API call'da limit'e sayılır
Son progress raporu: duration_listened = son_pozisyon
Frontend sidebar'ı günceller: "4/5 şarkı"