Dinamik Senkronizasyon Global Kapatma 5 Ocak 2026 • v2

Spot Ayarları Dinamik Senkronizasyon v2

Çalma Aralığı, Aktif/Pasif durumu ve Ana Şube Global Kapatma - Otomatik Senkronizasyon

Basit Anlatım (Herkes İçin)

Şu anda ne sorun var?

  • Ana şube "Kaç şarkıda bir anons çalsın?" ayarını değiştiriyor (örn: 10 → 15 şarkı)
  • Ana şube anons sistemini tamamen kapatıyor (spot_enabled = false)
  • Şubeler ve ana şube player'ları eski ayarı kullanmaya devam ediyor
  • Değişiklik için sayfa yenileme (F5) gerekiyor
  • 10 şube var? 10 yerde F5 basmak gerekiyor!

Ne yapacağız?

  • Ana şube ayarı değiştirdiğinde → Tüm şubeler otomatik algılayacak
  • Sayfa yenileme yok! Arka planda sessizce güncellenir
  • 3 dinamik ayar: Çalma Aralığı, Aktif/Pasif, Global Kapatma
  • 30 saniyede bir kontrol eder (hafif, server'ı yormaz)

Örnek Senaryo 1:

Ana şube "10 şarkıda bir" ayarını "15 şarkıda bir" yapar. 30 saniye içinde tüm şubeler yeni ayarı alır. Kimsenin sayfa yenilemesine gerek kalmaz!

Örnek Senaryo 2:

Ana şube "Anons Sistemi"ni kapatır (spot_enabled → false). 30 saniye içinde tüm şubelerdeki player'lar otomatik durur. Şube kendisi açamaz, sadece ana şube kontrol eder!

Üç Seviyeli Kontrol Sistemi

Seviye 1: Ana Şube Global Kontrol (spot_enabled)

Tüm sistemi kapatma/açma yetkisi

  • spot_enabled = true → Sistem açık, şubeler çalabilir
  • spot_enabled = false → Sistem kapalı, hiçbir şube çalamaz!
  • 🔒 Sadece ana şube değiştirebilir

Seviye 2: Şube Lokal Kontrol (spot_is_paused)

Her şube kendi player'ını durdurabilir

  • spot_is_paused = false → Bu şubede çalar (ana şube açıksa)
  • ⏸️ spot_is_paused = true → Bu şubede durur (geçici)
  • 🔓 Her şube kendi durumunu değiştirebilir
  • ⚠️ Ana şube kapatırsa → Şube açamaz!

Seviye 3: Çalma Aralığı (spot_songs_between)

Kaç şarkıda bir anons çalacak?

  • 🎵 spot_songs_between = 10 → Her 10 şarkıda bir
  • 📊 Ana şube değiştirir, tüm şubeler uyar
  • 🔄 Değişince sayaç sıfırlanır

Öncelik Mantığı:

if (!spot_enabled) {
    // ❌ Ana şube kapattı → HİÇBİR ŞUBE ÇALAMAZ!
    return SPOT_DISABLED;
}

if (spot_is_paused) {
    // ⏸️ Bu şube kendi durdurdu → Bu şubede çalmaz
    return SPOT_PAUSED;
}

// ✅ Her şey OK → Normal çalış
playSpotsEvery(spot_songs_between);

Teknik Detaylar (Geliştiriciler İçin)

Mevcut Sistem (Sorunlu)

Player Init (spot-player.js:50-68)

async function init() {
    loadCounter();              // localStorage'dan sayaç yükle
    await fetchSettings();      // API'den ayarları ÇEK (1 kez!)
}

async function fetchSettings() {
    const data = await fetch('/api/spot/settings').json();
    state.songsBetween = data.songs_between || 10;  // ← Bir kez ayarlanıyor!
    state.enabled = data.enabled;                   // ← Bir kez!
    state.isPaused = data.spot_is_paused;           // ← Bir kez!

    // ❌ SORUN: Ayarlar değişse bile player tekrar kontrol etmiyor
}

Sorun: Player init sırasında ayarları 1 kez alıyor, bir daha kontrol etmiyor!

Yeni Sistem (Çözüm)

1. Database: Settings Version Ekle

Dosya: Modules/Muzibu/database/migrations/tenant/YYYY_MM_DD_add_spot_settings_version.php

Schema::table('muzibu_corporate_accounts', function (Blueprint $table) {
    $table->unsignedInteger('spot_settings_version')->default(1)
          ->after('spot_is_paused')
          ->comment('Ayarlar her değiştiğinde artırılır');
});

2. Controller: Version'ı Artır (3 Durum İçin)

Dosya: Modules/Muzibu/app/Http/Controllers/Front/CorporateFrontController.php:1726

public function updateSpotSettings(Request $request)
{
    // ... (mevcut kod)

    // ✅ YENİ: 3 ayar değişimini de izle
    $versionUpdated = false;

    if ($request->has('spot_enabled')) {
        $data['spot_enabled'] = (bool) $request->spot_enabled;
        $versionUpdated = true;  // Ana şube global kapatma!
    }

    if ($request->has('spot_songs_between')) {
        $data['spot_songs_between'] = max(1, min(100, (int) $request->spot_songs_between));
        $versionUpdated = true;  // Çalma aralığı değişti!
    }

    // ⚠️ NOT: spot_is_paused şube bazlı, version artırMAZ! (lokal ayar)

    // Version'ı artır
    if ($versionUpdated) {
        $data['spot_settings_version'] = DB::raw('spot_settings_version + 1');
    }

    $account->update($data);

    return response()->json([
        'success' => true,
        'settings' => [
            'spot_enabled' => $account->spot_enabled,
            'spot_songs_between' => $account->spot_songs_between,
            'spot_settings_version' => $account->fresh()->spot_settings_version  // ✅ Yeni değer!
        ]
    ]);
}

3. API: Version Bilgisini Ekle

Dosya: Modules/Muzibu/app/Http/Controllers/Api/SpotController.php

public function settings(Request $request)
{
    // ... (mevcut kod)

    // ✅ effectiveEnabled: Ana şube + şube kontrolü
    $effectiveEnabled = $parentAccount->spot_enabled && !$account->spot_is_paused;

    return response()->json([
        'enabled' => $effectiveEnabled,                                        // Efektif durum
        'spot_enabled' => $parentAccount->spot_enabled,                        // ✅ YENİ: Ana şube global
        'spot_is_paused' => $account->spot_is_paused,                          // Şube lokal
        'songs_between' => $parentAccount->spot_songs_between ?? 10,
        'corporate_id' => $parentAccount->id,
        'branch_id' => $account->id,
        'spot_settings_version' => $parentAccount->spot_settings_version ?? 1,  // ✅ Version!
    ]);
}

4. JavaScript: Periyodik Kontrol + 3 Seviye

Dosya: public/themes/muzibu/js/player/features/spot-player.js:20-40

const state = {
    enabled: false,              // Efektif durum (global && !paused)
    spotEnabled: true,           // ✅ YENİ: Ana şube global
    isPaused: false,             // Şube lokal
    songsBetween: 10,
    songsPlayed: 0,
    currentVersion: 1,           // ✅ Version
    checkInterval: null,         // Interval ID
};

async function init() {
    await fetchSettings();

    // ✅ Her 30 saniyede bir ayarları kontrol et
    state.checkInterval = setInterval(checkSettingsUpdate, 30000);
}

// ✅ YENİ: Ayarlar değişmiş mi kontrol et
async function checkSettingsUpdate() {
    try {
        const response = await fetch('/api/spot/settings');
        const data = await response.json();

        // Version farklıysa güncelle!
        if (data.spot_settings_version !== state.currentVersion) {
            console.log('🎙️ SpotPlayer: Settings updated! Refreshing...');

            // ✅ 3 seviyeyi güncelle
            state.songsBetween = data.songs_between || 10;
            state.spotEnabled = data.spot_enabled;          // ✅ Ana şube global
            state.isPaused = data.spot_is_paused;           // Şube lokal
            state.enabled = data.enabled;                   // Efektif (global && !paused)
            state.currentVersion = data.spot_settings_version;

            // Sayacı sıfırla (yeni aralık için)
            resetCounter();

            // ✅ UI'ı güncelle (Alpine.js event)
            window.dispatchEvent(new CustomEvent('spot-settings-updated', {
                detail: {
                    songsBetween: state.songsBetween,
                    spotEnabled: state.spotEnabled,     // ✅ Global durum
                    isPaused: state.isPaused,           // Lokal durum
                    enabled: state.enabled              // Efektif durum
                }
            }));
        }
    } catch (e) {
        console.error('🎙️ SpotPlayer: Failed to check settings', e);
    }
}

5. Alpine.js: UI Senkronizasyonu (3 Seviye)

Dosya: Player footer'daki anons toggle component

<div x-data="{
    spotEnabled: true,       // UI state (efektif durum)
    globalDisabled: false,   // ✅ YENİ: Ana şube kapattı mı?

    init() {
        // ✅ YENİ: Spot settings update event'ini dinle
        window.addEventListener('spot-settings-updated', (e) => {
            // Ana şube global kapatma kontrolü
            this.globalDisabled = !e.detail.spotEnabled;

            // Efektif durum (global açık && lokal açık)
            this.spotEnabled = e.detail.enabled;

            console.log('🎨 UI: Spot settings updated!', {
                global: e.detail.spotEnabled,
                paused: e.detail.isPaused,
                effective: e.detail.enabled
            });
        });

        // Başlangıç senkronizasyonu
        const syncState = () => {
            if (window.MuzibuSpotPlayer) {
                this.spotEnabled = window.MuzibuSpotPlayer.isEnabled()
                                && !window.MuzibuSpotPlayer.isPaused();
            }
        };
        setTimeout(syncState, 500);
    },

    toggle() {
        // ✅ Ana şube kapattıysa toggle edemez!
        if (this.globalDisabled) {
            console.warn('🚫 Ana şube spot sistemini kapattı!');
            return;
        }

        // Optimistic UI
        this.spotEnabled = !this.spotEnabled;

        // API çağır (spot_is_paused toggle)
        if (window.MuzibuSpotPlayer) {
            window.MuzibuSpotPlayer.togglePause().then(result => {
                if (!result.success) {
                    this.spotEnabled = !this.spotEnabled;
                }
            }).catch(() => {
                this.spotEnabled = !this.spotEnabled;
            });
        }
    }
}">
    <!-- Anons toggle UI -->
    <div @click="toggle()" class="flex items-center gap-2 cursor-pointer"
         :class="globalDisabled && 'opacity-50 cursor-not-allowed'">
        <i class="fas text-[10px]"
           :class="spotEnabled ? 'fa-bullhorn text-white/50' : 'fa-ban text-red-400/80'"></i>
        <span class="text-xs" x-text="spotEnabled ? 'Anonslar aktif' : 'Anonslar durduruldu'"></span>
        <div class="w-2.5 h-2.5 rounded-sm"
             :class="spotEnabled ? 'bg-green-400/60' : 'bg-red-400/60'"></div>
    </div>

    <!-- ⚠️ Ana şube kapattıysa uyarı -->
    <div x-show="globalDisabled" class="text-[10px] text-amber-400 mt-1">
        <i class="fas fa-lock mr-1"></i>Ana şube tarafından kapatıldı
    </div>
</div>

Çözüm Seçenekleri

Çözüm Avantajlar Dezavantajlar Öneri
Seçildi
Settings Version + Polling
• Basit implementasyon
• Ek teknoloji yok
• Hafif (30s polling)
• Laravel cache entegre
• 3 seviye kontrol destekler
• Gerçek zamanlı değil (30s gecikme)
• Minimal API yükü
WebSocket/Pusher • Anında güncelleme
• Gerçek zamanlı
• Polling yok
• Pusher ücreti
• Server konfigürasyonu
• Karmaşık implementasyon
Redis Pub/Sub • Hafif
• Laravel entegre
• Yine polling gerekir
• Settings version'dan farkı yok

Uygulama Adımları

1

Migration Oluştur ve Çalıştır

Database'e spot_settings_version field ekle

php artisan make:migration add_spot_settings_version_to_corporate_accounts --path=Modules/Muzibu/database/migrations/tenant
php artisan tenants:migrate
2

Controller'ı Güncelle (3 Seviye İçin)

CorporateFrontController::updateSpotSettings() - Version artırma ekle (spot_enabled + spot_songs_between)

3

API'ye Version ve Global Durum Ekle

SpotController::settings() - Version + spot_enabled bilgisi ekle

4

JavaScript Polling + 3 Seviye Ekle

spot-player.js - checkSettingsUpdate() + spotEnabled/isPaused/enabled state'leri

5

Alpine Component Güncelle (Global Kontrol)

Anons toggle component - globalDisabled kontrolü + event listener

6

Test Et (3 Senaryo)

  • • Çalma aralığı değiştir → 30s içinde tüm şubeler güncellenir
  • • Ana şube sistemi kapat → 30s içinde tüm şubeler durur
  • • Şube kendini durdur → Sadece o şube durur (diğerleri çalar)

Önemli Notlar