v2 - Kapsamlı Plan 31 Aralık 2025

Muzibu Anons Sistemi

Kurumsal Şarkı Arası Anons

Sistem Özeti

Basit Anlatım (Herkes İçin)

Ne yapıyoruz? Kurumsal müşterilerin (restoranlar, kafeler, mağazalar, ofisler) müzik dinlerken araya kendi duyurularını ekleyebilmesini sağlıyoruz.

Nasıl çalışacak? Örneğin bir restoran müzik çalarken, her 10 şarkıda bir "Değerli müşterilerimiz, happy hour başlamıştır" gibi bir anons otomatik çalacak.

Kim kontrol edecek?

  • Muzibu Admin: Anons dosyalarını yükler, sıralar
  • Ana Şube: Kaç şarkıda bir çalacağını ayarlar
  • Her Şube: Kendi için durdurabilir/devam ettirebilir

Teknik Özet (Geliştiriciler İçin)

4 yeni veritabanı tablosu (tenant DB'de)
Admin Livewire component (CRUD + drag-drop)
Kurumsal ayarlar API (settings GET/PUT)
Player JS entegrasyonu (Alpine.js store)
Şube bazlı state yönetimi (localStorage + DB sync)
MP3 dosya yükleme (Spatie Media Library)

Sistemdeki Aktörler

Muzibu Admin

Anons dosyalarını yükler, yönetir, sıralar

Ana Şube

Anons ayarlarını yapar, tüm şubeleri görür

Alt Şube

Sadece kendi için durdur/devam yapabilir

Dinleyici

Müzik dinler, anonsları duyar

Admin Panel (Muzibu Yönetimi)

Menü Konumu

Admin Sidebar → Muzibu Anonslar

Route: /admin/muzibu/announcements

Anons Listesi Sayfası

Anons Yönetimi

Kurumsal hesaplarda çalacak anonsları yönetin

5

Toplam Anons

3

Aktif

2

Pasif

1,245

Toplam Çalınma

Sıra Anons Süre Çalınma Durum İşlemler
1

Yılbaşı Kampanyası

Yüklenme: 30 Ara 2025

0:45 523 Aktif
2

Happy Hour Duyurusu

Yüklenme: 28 Ara 2025

0:30 412 Aktif
3

Eski Kampanya

Yüklenme: 15 Ara 2025

0:35 310 Pasif
Sıralama (Drag & Drop)
  • • Sol taraftaki tutma ikonundan sürükle-bırak
  • • Sıra numarası otomatik güncellenir
  • • Anonslar bu sırayla çalar (rotation)
  • • Livewire + SortableJS kullanılacak
Durum Yönetimi
  • Aktif: Rotation'a dahil, çalabilir
  • Pasif: Rotation dışı, çalmaz
  • • Pasif anonslar listede soluk görünür
  • • Tek tıkla durum değiştirilebilir

Anons Yükleme / Düzenleme Formu

Admin panelde görünecek isim

Dosya sürükle veya tıkla

MP3, WAV • Max 10MB • Max 2 dakika

Yılbaşı Kampanyası

anons-yilbasi.mp3

0:00 0:45

Süre

0:45

Boyut

1.2 MB

Format

MP3

Dosya Kuralları

Format

MP3, WAV, M4A kabul edilir

Boyut

Maksimum 10 MB

Süre

Maksimum 2 dakika (120 saniye)

Admin İstatistikleri

Anons Bazlı İstatistik
Yılbaşı Kampanyası
523
Happy Hour
412
Eski Kampanya
310
Genel Metrikler

Bugün

127

Bu Hafta

843

Atlama Oranı

12%

Ort. Dinleme

38sn

Kurumsal Panel (Ana Şube)

Erişim Noktası

Kurumsal Dashboard → Anons Ayarları

Route: /corporate/announcement-settings

Sadece ana şube (parent_id = null) bu sayfayı görebilir

Anons Ayarları Sayfası

Anons Sistemi Ayarları

Tüm şubeleriniz için anons ayarlarını buradan yönetin

Anons Sistemi

Tüm şubelerde anons çalmasını aç/kapat

Aktif
Anons Aralığı

Kaç şarkıdan sonra anons çalsın?

10
5 şarkı 20 şarkı

Şarkılar en az 30 saniye dinlendiğinde sayılır

10

Şarkıda Bir Anons

3

Aktif Anons

1,245

Toplam Çalınma

Şube Durumları (Ana Şube Görünümü)

Şube Anons Durumu Şarkı Sayacı Son Anons
Ana Şube (Merkez)
Aktif 7 / 10 5 dk önce
Ankara Şubesi
Aktif 3 / 10 12 dk önce
İzmir Şubesi
Durduruldu - 2 saat önce

Şubeler kendi panellerinden anonsu durdurabilir. Ana şube sadece görüntüleyebilir, müdahale edemez.

Şube Paneli (Durdur/Devam)

Anons Kontrol Butonu Konumu

Seçenek 1: Sol Sidebar (Önerilen)
Ana Sayfa
Müzikler
Playlistler

Anonslar

3 şarkı sonra

Her zaman görünür, kolay erişim

Seçenek 2: Player İçinde

Şarkı Adı

Sanatçı

Player ile entegre, kompakt

Durdur / Devam Arayüzü

Anons Aktif

Anons Sistemi

Şu an aktif

AKTİF
Sonraki anons: 3 şarkı sonra

7 / 10 şarkı

Anons Durdurulmuş

Anons Sistemi

Durduruldu

DURDU

Anonslar çalmıyor

Not: Durdurma sadece bu şubeyi etkiler. Diğer şubeler ve ana şube etkilenmez. Ana şube ayarları (kaç şarkıda bir) değişmez, sadece bu şubede anons çalmaz.

Player Entegrasyonu

Çalma Akışı

Normal Akış (Anons Yok)
Şarkı 1
Şarkı 2
Şarkı 3
...
Anonslu Akış (Kurumsal)
1
2
...
9
10
ANONS
11
12
...

Anons Çalarken Player Görünümü

Normal Şarkı Çalarken

Hayal Kahvesi

Sezen Aksu

1:23 3:45
Anons Çalarken
ANONS

Yılbaşı Kampanyası

Şirket Duyurusu

0:27 0:45

Anons Sırasında Davranışlar

İzin Verilenler
  • • Ses açma/kapama (volume)
  • • Mute/unmute
  • • Tam ekran görünümü
Engellenenler
  • • İleri/geri atlama (skip) - Opsiyonel
  • • Progress bar ile atlama (seek)
  • • Şarkı değiştirme
Karar: Anons Atlanabilir mi?

JavaScript Akış Mantığı

// 1. Sayfa yüklendiğinde kurumsal kontrolü
async initAnnouncementSystem() {
    const corporate = await this.getCorporateAccount();
    if (!corporate) return; // Kurumsal değilse çık

    this.announcement = {
        enabled: corporate.announcement_enabled,
        paused: await this.getBranchPauseStatus(),
        counter: await this.getBranchCounter(),
        triggerAt: corporate.songs_between_announcements, // default: 10
        currentIndex: 0
    };
}

// 2. Şarkı 30 saniye dinlendiğinde (trackHit)
onTrackHit() {
    // Normal tracking...

    // Anons sayacı
    if (this.announcement.enabled && !this.announcement.paused) {
        this.announcement.counter++;
        this.syncCounterToServer(); // Backend'e kaydet
    }
}

// 3. Şarkı bittiğinde
onSongEnded() {
    if (this.shouldPlayAnnouncement()) {
        this.playAnnouncement();
    } else {
        this.playNextSong();
    }
}

// 4. Anons gerekli mi?
shouldPlayAnnouncement() {
    return this.announcement.enabled
        && !this.announcement.paused
        && this.announcement.counter >= this.announcement.triggerAt;
}

// 5. Anons çal
async playAnnouncement() {
    // Sıradaki anonsu al (rotation)
    const next = await fetch('/api/announcement/next');

    // Player state'i değiştir
    this.isPlayingAnnouncement = true;
    this.currentTrack = {
        type: 'announcement',
        ...next.data
    };

    // Audio src değiştir ve çal
    this.audio.src = next.data.audio_url;
    this.audio.play();

    // Anons bitince
    this.audio.onended = () => {
        this.trackAnnouncementPlay(next.data.id);
        this.announcement.counter = 0; // Sayaç sıfırla
        this.isPlayingAnnouncement = false;
        this.playNextSong(); // Normal akışa dön
    };
}

Dosya Yönetimi

Storage Yapısı

storage/
└── tenant1001/              # Muzibu tenant
    └── app/
        └── public/
            └── announcements/    # Anons dosyaları
                ├── 1/
                │   └── yilbasi-kampanyasi.mp3
                ├── 2/
                │   └── happy-hour.mp3
                └── 3/
                    └── genel-duyuru.mp3
Dosya Adlandırma
  • • Orijinal isim slug'lanır
  • • Türkçe karakterler dönüştürülür
  • • Örn: "Yılbaşı Kampanyası.mp3" → "yilbasi-kampanyasi.mp3"
Erişim URL
/storage/announcements/1/yilbasi-kampanyasi.mp3

Dosya Validasyonu

Kural Değer Açıklama
Format mp3, wav, m4a, ogg MIME type kontrolü yapılır
Max Boyut 10 MB Büyük dosyalar reddedilir
Max Süre 120 saniye FFprobe ile kontrol
Min Süre 5 saniye Çok kısa dosyalar reddedilir

Veritabanı Tasarımı

Tablo İlişkileri

announcements
  • id
  • title
  • audio_path
  • duration
  • position
  • is_active
corporate_announcement_settings
  • id
  • corporate_account_id (FK)
  • songs_between
  • is_enabled
  • current_index
branch_announcement_status
  • id
  • corporate_account_id (FK)
  • user_id (FK)
  • is_paused
  • song_counter
announcement_plays
  • id
  • announcement_id (FK)
  • corporate_account_id (FK)
  • user_id (FK)
  • listened_duration
  • was_skipped

muzibu_announcements

Kolon Tip
idBIGINT PK
titleVARCHAR(255)
audio_pathVARCHAR(500)
durationINT (saniye)
file_sizeINT (bytes)
positionINT
is_activeBOOLEAN
play_countINT default 0
created_atTIMESTAMP
updated_atTIMESTAMP

muzibu_corporate_announcement_settings

Kolon Tip
idBIGINT PK
corporate_account_idBIGINT FK UNIQUE
songs_between_announcementsINT default 10
min_listen_durationINT default 30
is_enabledBOOLEAN default true
current_announcement_indexINT default 0
created_atTIMESTAMP
updated_atTIMESTAMP

muzibu_branch_announcement_status

Kolon Tip
idBIGINT PK
corporate_account_idBIGINT FK
user_idBIGINT FK
is_pausedBOOLEAN default false
song_counterINT default 0
last_announcement_atTIMESTAMP NULL
created_atTIMESTAMP
updated_atTIMESTAMP

UNIQUE: (corporate_account_id, user_id)

muzibu_announcement_plays

Kolon Tip
idBIGINT PK
announcement_idBIGINT FK
corporate_account_idBIGINT FK
user_idBIGINT FK NULL
listened_durationINT
was_skippedBOOLEAN
device_typeVARCHAR(50)
played_atTIMESTAMP

INDEX: (announcement_id, played_at)

API Endpoints

Admin API

GET
/api/admin/announcements

Tüm anonsları listele (pagination)

POST
/api/admin/announcements

Yeni anons yükle (multipart/form-data)

PUT
/api/admin/announcements/{id}

Anons güncelle (başlık, durum)

DEL
/api/admin/announcements/{id}

Anons sil (soft delete)

POST
/api/admin/announcements/reorder

Sıralama güncelle: {"ids": [3,1,2]}

Corporate API (Ana Şube)

GET
/api/corporate/announcement-settings

Mevcut ayarları getir

Response: {
  "is_enabled": true,
  "songs_between": 10,
  "total_announcements": 3,
  "total_plays": 1245
}
PUT
/api/corporate/announcement-settings

Ayarları güncelle

Body: {
  "is_enabled": true,
  "songs_between": 10
}
GET
/api/corporate/branch-announcement-statuses

Tüm şubelerin anons durumlarını getir

Player API (Frontend)

GET
/api/announcement/init

Anons sistemi başlangıç bilgileri

Response: {
  "enabled": true,
  "paused": false,
  "counter": 7,
  "trigger_at": 10,
  "announcements_count": 3
}
GET
/api/announcement/next

Sıradaki anonsu getir (rotation)

Response: {
  "id": 2,
  "title": "Happy Hour",
  "audio_url": "/storage/announcements/2/happy-hour.mp3",
  "duration": 30
}
POST
/api/announcement/increment

Şarkı sayacını artır (30sn sonra)

POST
/api/announcement/track-play

Anons çalındı bildir

Body: {
  "announcement_id": 2,
  "listened_duration": 30,
  "was_skipped": false
}
POST
/api/announcement/pause

Bu şube için anonsu durdur

POST
/api/announcement/resume

Bu şube için anonsu devam ettir

Kullanım Senaryoları

Yeni Anons Ekleme (Admin)

  1. 1 Admin panele giriş yap → Muzibu → Anonslar
  2. 2 "Yeni Anons Yükle" butonuna tıkla
  3. 3 Başlık gir: "Yılbaşı Kampanyası"
  4. 4 MP3 dosyasını sürükle-bırak ile yükle
  5. 5 Sistem otomatik: süre kontrolü, format kontrolü yapar
  6. 6 Önizleme ile dinle, "Kaydet" tıkla
  7. Anons listede görünür, rotation'a dahil olur

Kurumsal Ayarları Değiştirme (Ana Şube)

  1. 1 Kurumsal hesapla giriş yap (ana şube)
  2. 2 Kurumsal Dashboard → "Anons Ayarları"
  3. 3 Slider ile "15 şarkıda bir" olarak değiştir
  4. 4 "Ayarları Kaydet" tıkla
  5. Tüm şubelerde artık 15 şarkıda bir anons çalar

Şube Anons Durdurma

  1. 1 Şube hesabıyla giriş yap
  2. 2 Sol sidebar'da "Anonslar" panelini gör
  3. 3 "Durdur" butonuna tıkla
  4. ! Sadece bu şubede anonslar durur
  5. 5 Diğer şubeler etkilenmez, normal çalmaya devam

Normal Dinleme Akışı (Kullanıcı)

  1. Şarkı 1 çalar → 30sn+ dinlenir → Sayaç: 1
  2. Şarkı 2 çalar → 30sn+ dinlenir → Sayaç: 2
  3. ... ... (şarkılar devam eder)
  4. Şarkı 10 çalar → 30sn+ dinlenir → Sayaç: 10
  5. 📢 Şarkı 10 biter → ANONS ÇALAR (45sn)
  6. Anons biter → Sayaç sıfırlanır → Şarkı 11 başlar
  7. Döngü devam eder...

Edge Cases (Özel Durumlar)

Şarkı 30sn'den az dinlenirse?

Sayaç artmaz. Skip edilen şarkılar sayılmaz.

Aktif anons yoksa?

Anons sistemi devre dışı kalır, normal çalma devam eder.

Sayfa yenilenirse?

Sayaç backend'den çekilir, kaldığı yerden devam.

Kullanıcı çıkış yaparsa?

Sayaç DB'de kalır, tekrar girişte devam.

Aynı anda birden fazla cihaz?

Her cihaz (session) ayrı sayaç tutar.

Kurumsal değilse?

Anons sistemi hiç aktif olmaz, JS yüklenmez.

Geliştirme Adımları

1. Veritabanı

  • • 4 migration oluştur
  • • 4 model oluştur
  • • İlişkileri tanımla

2. Admin Panel

  • • Livewire component
  • • Liste + Form view
  • • Drag-drop sıralama
  • • Dosya yükleme

3. Kurumsal Panel

  • • Ayarlar sayfası
  • • Şube durumları
  • • API endpoints

4. Şube Kontrolü

  • • Sidebar widget
  • • Durdur/devam UI
  • • API endpoints

5. Player

  • • Alpine store güncelle
  • • Sayaç mantığı
  • • Anons çalma UI
  • • API entegrasyonu

6. Test & Deploy

  • • Migration çalıştır
  • • Manuel test
  • • Cache temizle
  • • npm run prod