Files
root 0c6c558a59 Add radio embed widget, SSE real-time, song history, moderation panel, and Auto DJ
- Embed widget: standalone iframe player with dark/light/transparent themes, copy-paste embed code admin page
- Real-time SSE: streaming now-playing/listeners/dj events, replaces polling in radio-player and embed
- Song history: auto-records song changes to radio_song_plays table, Filament resource to view
- DJ moderation: unified panel for shouts approval, song request queue, DJ applications
- Auto DJ: playlist management with round-robin playback when no DJ is live
- Refactored radio-player Alpine component to use EventSource API with auto-reconnect
2026-05-24 14:07:32 +02:00

1668 lines
73 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace App\Filament\Pages\Radio;
use App\Models\Miscellaneous\WebsiteSetting;
use Filament\Actions\Action;
use Filament\Forms\Components\ColorPicker;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\TimePicker;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
final class RadioSettings extends Page implements HasForms
{
use InteractsWithForms;
#[\Override]
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-radio';
#[\Override]
protected static string|\UnitEnum|null $navigationGroup = 'Radio';
#[\Override]
protected static ?string $navigationLabel = null;
#[\Override]
protected static ?string $title = 'Radio Settings';
#[\Override]
public static function getNavigationLabel(): string
{
return __('Radio Settings');
}
#[\Override]
public function getTitle(): string
{
return __('radio.title');
}
#[\Override]
protected string $view = 'filament.pages.radio.radio-settings';
/** @var array<string, mixed> */
public array $data = [];
public ?string $currentDjId = null;
private const string CACHE_KEY = 'website_settings';
public function mount(): void
{
$this->fillForm();
}
protected function fillForm(): void
{
$this->data = [
'radio_enabled' => $this->getSettingBool('radio_enabled'),
'radio_stream_url' => $this->getSetting('radio_stream_url', ''),
'radio_stream_backup_url' => $this->getSetting('radio_stream_backup_url', ''),
'radio_style' => $this->getSetting('radio_style', 'dark'),
'radio_player_type' => $this->getSetting('radio_player_type', 'default'),
'radio_auto_play' => $this->getSettingBool('radio_auto_play'),
'radio_auto_play_delay' => (int) $this->getSetting('radio_auto_play_delay', '0'),
'radio_mute_on_start' => $this->getSettingBool('radio_mute_on_start'),
'radio_volume' => (int) $this->getSetting('radio_volume', '80'),
'radio_applications_enabled' => $this->getSettingBool('radio_applications_enabled'),
'radio_applications_max_per_day' => (int) $this->getSetting('radio_applications_max_per_day', '1'),
'radio_applications_require_approval' => $this->getSettingBool('radio_applications_require_approval'),
'radio_widget_enabled' => $this->getSettingBool('radio_widget_enabled'),
'radio_widget_show_globally' => $this->getSettingBool('radio_widget_show_globally'),
'radio_widget_position' => $this->getSetting('radio_widget_position', 'bottom-right'),
'radio_embed_enabled' => $this->getSettingBool('radio_embed_enabled'),
'radio_embed_allowed_domains' => $this->getSetting('radio_embed_allowed_domains', ''),
'radio_embed_theme' => $this->getSetting('radio_embed_theme', 'dark'),
'radio_embed_height' => (int) $this->getSetting('radio_embed_height', '400'),
'radio_embed_width' => $this->getSetting('radio_embed_width', '100%'),
'radio_embed_auto_play' => $this->getSettingBool('radio_embed_auto_play'),
'radio_shouts_enabled' => $this->getSettingBool('radio_shouts_enabled'),
'radio_shouts_max_length' => (int) $this->getSetting('radio_shouts_max_length', '280'),
'radio_shouts_cooldown' => (int) $this->getSetting('radio_shouts_cooldown', '30'),
'radio_shouts_require_moderation' => $this->getSettingBool('radio_shouts_require_moderation'),
'radio_chat_enabled' => $this->getSettingBool('radio_chat_enabled'),
'radio_chat_position' => $this->getSetting('radio_chat_position', 'right'),
'radio_chat_width' => (int) $this->getSetting('radio_chat_width', '350'),
'radio_chat_height' => (int) $this->getSetting('radio_chat_height', '500'),
'radio_chat_messages_count' => (int) $this->getSetting('radio_chat_messages_count', '50'),
'radio_chat_show_timestamps' => $this->getSettingBool('radio_chat_show_timestamps'),
'radio_chat_allow_links' => $this->getSettingBool('radio_chat_allow_links'),
'radio_chat_allow_emojis' => $this->getSettingBool('radio_chat_allow_emojis'),
'radio_now_playing_enabled' => $this->getSettingBool('radio_now_playing_enabled'),
'radio_now_playing_api_url' => $this->getSetting('radio_now_playing_api_url', ''),
'radio_now_playing_cache_duration' => (int) $this->getSetting('radio_now_playing_cache_duration', '30'),
'radio_show_song_history' => $this->getSettingBool('radio_show_song_history'),
'radio_song_history_count' => (int) $this->getSetting('radio_song_history_count', '10'),
'radio_listeners_enabled' => $this->getSettingBool('radio_listeners_enabled'),
'radio_listeners_api_url' => $this->getSetting('radio_listeners_api_url', ''),
'radio_show_listener_peak' => $this->getSettingBool('radio_show_listener_peak'),
'radio_peak_reset_time' => $this->getSetting('radio_peak_reset_time', '00:00'),
'radio_listeners_update_interval' => (int) $this->getSetting('radio_listeners_update_interval', '30'),
'radio_store_listener_stats' => $this->getSettingBool('radio_store_listener_stats'),
'radio_stats_retention_days' => (int) $this->getSetting('radio_stats_retention_days', '30'),
'radio_min_listeners_threshold' => (int) $this->getSetting('radio_min_listeners_threshold', '0'),
'radio_listener_alerts_enabled' => $this->getSettingBool('radio_listener_alerts_enabled'),
'radio_listener_alert_threshold_high' => (int) $this->getSetting('radio_listener_alert_threshold_high', '100'),
'radio_listener_alert_threshold_low' => (int) $this->getSetting('radio_listener_alert_threshold_low', '5'),
'radio_show_listener_graph' => $this->getSettingBool('radio_show_listener_graph'),
'radio_listener_graph_period' => $this->getSetting('radio_listener_graph_period', '24h'),
'radio_show_average_listeners' => $this->getSettingBool('radio_show_average_listeners'),
'radio_show_unique_listeners' => $this->getSettingBool('radio_show_unique_listeners'),
'radio_show_current_dj' => $this->getSettingBool('radio_show_current_dj'),
'current_dj_id' => $this->getSetting('radio_current_dj_id', ''),
'radio_dj_avatar_size' => (int) $this->getSetting('radio_dj_avatar_size', '64'),
'radio_default_avatar_figure' => $this->getSetting('radio_default_avatar_figure', 'hr-893-45.hd-180-2.ch-210-62.lg-285-62.sh-295-62.ha-1012-62.wa-2007-0'),
'radio_default_dj_name' => $this->getSetting('radio_default_dj_name', 'Frank'),
'radio_show_dj_avatar_popup' => $this->getSettingBool('radio_show_dj_avatar_popup'),
'radio_dj_avatar_popup_duration' => (int) $this->getSetting('radio_dj_avatar_popup_duration', '5'),
'radio_show_dj_schedule' => $this->getSettingBool('radio_show_dj_schedule'),
'radio_auto_dj_detection' => $this->getSettingBool('radio_auto_dj_detection'),
'radio_dj_detection_interval' => (int) $this->getSetting('radio_dj_detection_interval', '60'),
'auto_dj_detected_action' => $this->getSetting('auto_dj_detected_action', 'set_dj'),
'radio_sambroadcaster_api_url' => $this->getSetting('radio_sambroadcaster_api_url', ''),
'radio_sambroadcaster_password' => $this->getSetting('radio_sambroadcaster_password', ''),
'radio_virtual_dj_url' => $this->getSetting('radio_virtual_dj_url', ''),
'radio_virtual_dj_password' => $this->getSetting('radio_virtual_dj_password', ''),
'radio_djs_api_url' => $this->getSetting('radio_djs_api_url', ''),
'radio_request_form_enabled' => $this->getSettingBool('radio_request_form_enabled'),
'radio_request_max_per_user' => (int) $this->getSetting('radio_request_max_per_user', '3'),
'radio_request_cooldown' => (int) $this->getSetting('radio_request_cooldown', '300'),
'radio_voting_enabled' => $this->getSettingBool('radio_voting_enabled'),
'radio_voting_show_song_requests' => $this->getSettingBool('radio_voting_show_song_requests'),
'radio_stats_show_daily' => $this->getSettingBool('radio_stats_show_daily'),
'radio_stats_show_weekly' => $this->getSettingBool('radio_stats_show_weekly'),
'radio_stats_show_monthly' => $this->getSettingBool('radio_stats_show_monthly'),
'radio_stats_show_top_djs' => $this->getSettingBool('radio_stats_show_top_djs'),
'radio_stats_show_top_songs' => $this->getSettingBool('radio_stats_show_top_songs'),
'radio_show_offline_message' => $this->getSettingBool('radio_show_offline_message'),
'radio_offline_message' => $this->getSetting('radio_offline_message', 'We zijn momenteel offline. Check onze socials voor updates!'),
'radio_offline_image' => $this->getSetting('radio_offline_image', ''),
'radio_background_image' => $this->getSetting('radio_background_image', ''),
'radio_logo_url' => $this->getSetting('radio_logo_url', ''),
'radio_player_color_primary' => $this->getSetting('radio_player_color_primary', '#eeb425'),
'radio_player_color_secondary' => $this->getSetting('radio_player_color_secondary', '#1a1a2e'),
'radio_player_color_text' => $this->getSetting('radio_player_color_text', '#ffffff'),
'radio_player_color_accent' => $this->getSetting('radio_player_color_accent', '#eeb425'),
'radio_custom_css' => $this->getSetting('radio_custom_css', ''),
'radio_custom_js' => $this->getSetting('radio_custom_js', ''),
'radio_social_facebook' => $this->getSetting('radio_social_facebook', ''),
'radio_social_twitter' => $this->getSetting('radio_social_twitter', ''),
'radio_social_instagram' => $this->getSetting('radio_social_instagram', ''),
'radio_social_discord' => $this->getSetting('radio_social_discord', ''),
'radio_social_youtube' => $this->getSetting('radio_social_youtube', ''),
'radio_social_twitch' => $this->getSetting('radio_social_twitch', ''),
'radio_contact_email' => $this->getSetting('radio_contact_email', ''),
'radio_contact_phone' => $this->getSetting('radio_contact_phone', ''),
'radio_about_text' => $this->getSetting('radio_about_text', ''),
'radio_azurecast_base_url' => $this->getSetting('radio_azurecast_base_url', ''),
'radio_azurecast_station_id' => (int) $this->getSetting('radio_azurecast_station_id', '1'),
'radio_azurecast_api_key' => $this->getSetting('radio_azurecast_api_key', ''),
'radio_azurecast_port' => (int) $this->getSetting('radio_azurecast_port', '8443'),
'radio_azurecast_protocol' => $this->getSetting('radio_azurecast_protocol', 'https'),
'radio_azurecast_use_proxy' => $this->getSettingBool('radio_azurecast_use_proxy'),
'radio_language' => $this->getSetting('radio_language', 'nl'),
'radio_multi_language_enabled' => $this->getSettingBool('radio_multi_language_enabled'),
'radio_discord_webhook_url' => $this->getSetting('radio_discord_webhook_url', ''),
'radio_discord_enabled' => $this->getSettingBool('radio_discord_enabled'),
'radio_discord_dj_live' => $this->getSettingBool('radio_discord_dj_live'),
'radio_discord_song_changes' => $this->getSettingBool('radio_discord_song_changes'),
'radio_discord_custom_message' => $this->getSetting('radio_discord_custom_message', 'DJ {dj_name} is nu live!'),
];
$this->currentDjId = $this->data['current_dj_id'] ?? null;
}
public function form(Schema $schema): Schema
{
return $schema
->components([
Section::make('Radio Auto-Setup')
->description('In 1 klik je radio installeren en configureren')
->columns(2)
->afterHeader([
Action::make('install_azurecast')
->label('Installeer AzureCast')
->action('installAndSetupAzurecast')
->color('success'),
Action::make('auto_setup')
->label('Auto-Setup')
->action('fullAutoSetup')
->color('primary'),
])
->schema([
TextInput::make('radio_stream_url')
->label('Stream URL')
->placeholder('https://jouw-radio.nl/radio.mp3')
->url()
->columnSpan(2)
->hint('Voer je AzureCast stream URL in'),
Toggle::make('radio_enabled')
->label('Radio Inschakelen')
->columnSpanFull(),
]),
Section::make('AzureCast Configuratie')
->description('Handmatige AzureCast instellingen')
->collapsed()
->columns(2)
->afterHeader([
Action::make('save_azurecast')
->label('Opslaan')
->action('saveAzurecast')
->color('primary'),
])
->schema([
TextInput::make('radio_azurecast_base_url')
->label('AzureCast Basis URL')
->placeholder('https://radio.jouwdomein.nl')
->url()
->columnSpan(2),
TextInput::make('radio_azurecast_station_id')
->label('Station ID')
->numeric()
->default(1),
TextInput::make('radio_azurecast_api_key')
->label('API Key')
->password(),
TextInput::make('radio_azurecast_port')
->label('Poort')
->numeric()
->default(8443),
Select::make('radio_azurecast_protocol')
->label('Protocol')
->options([
'http' => 'HTTP',
'https' => 'HTTPS',
])
->default('https'),
Toggle::make('radio_azurecast_use_proxy')
->label('Proxy Gebruiken'),
]),
Section::make('Sambroadcaster & Virtual DJ')
->description('Verbinding met Sambroadcaster en Virtual DJ software')
->columns(2)
->afterHeader([
Action::make('check_dj')
->label('Check DJ Verbinding')
->action('checkDjConnection')
->color('info'),
Action::make('save_sambroadcaster')
->label('Opslaan')
->action('saveSambroadcaster')
->color('primary'),
])
->schema([
TextInput::make('radio_sambroadcaster_api_url')
->label('Sambroadcaster API URL')
->url(),
TextInput::make('radio_sambroadcaster_password')
->label('Sambroadcaster Wachtwoord')
->password(),
TextInput::make('radio_virtual_dj_url')
->label('Virtual DJ URL')
->url(),
TextInput::make('radio_virtual_dj_password')
->label('Virtual DJ Wachtwoord')
->password(),
TextInput::make('radio_djs_api_url')
->label('DJs API URL')
->url(),
]),
Section::make('Player Configuratie')
->description('Basis player instellingen')
->columns(2)
->afterHeader([
Action::make('save_player')
->label('Opslaan')
->action('savePlayer')
->color('primary'),
])
->schema([
Select::make('radio_style')
->label('Player Stijl')
->options([
'dark' => 'Donker',
'light' => 'Licht',
'blue' => 'Blauw',
'purple' => 'Paars',
'pink' => 'Roze',
'gold' => 'Goud',
])
->default('dark'),
Select::make('radio_player_type')
->label('Player Type')
->options([
'default' => 'Standaard',
'minimal' => 'Minimaal',
'embedded' => 'Ingebed',
])
->default('default'),
Toggle::make('radio_auto_play')
->label('Auto-Play'),
TextInput::make('radio_auto_play_delay')
->label('Auto-Play Vertraging (seconden)')
->numeric()
->default(0),
Toggle::make('radio_mute_on_start')
->label('Gedempt Starten'),
TextInput::make('radio_volume')
->label('Standaard Volume')
->numeric()
->minValue(0)
->maxValue(100)
->default(80),
Select::make('radio_language')
->label('Systeem Taal')
->options([
'nl' => 'Nederlands',
'en' => 'English',
'de' => 'Deutsch',
'fr' => 'Français',
'es' => 'Español',
'pt' => 'Português',
'it' => 'Italiano',
'br' => 'Português Brasil',
])
->default('nl'),
Toggle::make('radio_multi_language_enabled')
->label('Multi-taal Ondersteuning'),
]),
Section::make('Player Kleuren')
->description('Player kleuren aanpassen')
->columns(3)
->afterHeader([
Action::make('save_colors')
->label('Opslaan')
->action('saveColors')
->color('primary'),
])
->schema([
ColorPicker::make('radio_player_color_primary')
->label('Primaire Kleur')
->default('#eeb425'),
ColorPicker::make('radio_player_color_secondary')
->label('Secundaire Kleur')
->default('#1a1a2e'),
ColorPicker::make('radio_player_color_text')
->label('Tekst Kleur')
->default('#ffffff'),
ColorPicker::make('radio_player_color_accent')
->label('Accent Kleur')
->default('#eeb425'),
]),
Section::make('Nu Afspelen')
->description('Nu afspelen informatie')
->columns(2)
->afterHeader([
Action::make('save_now_playing')
->label('Opslaan')
->action('saveNowPlaying')
->color('primary'),
])
->schema([
Toggle::make('radio_now_playing_enabled')
->label('Nu Afspelen Inschakelen'),
TextInput::make('radio_now_playing_api_url')
->label('API URL')
->url(),
TextInput::make('radio_now_playing_cache_duration')
->label('Cache Duur (seconden)')
->numeric()
->default(30),
Toggle::make('radio_show_song_history')
->label('Song Geschiedenis Tonen'),
TextInput::make('radio_song_history_count')
->label('Aantal Songs in Geschiedenis')
->numeric()
->default(10),
]),
Section::make('Luisteraars Statistieken')
->description('Luisteraars informatie en statistieken')
->columns(2)
->afterHeader([
Action::make('save_listeners')
->label('Opslaan')
->action('saveListeners')
->color('primary'),
])
->schema([
Toggle::make('radio_listeners_enabled')
->label('Luisteraars Aantal Inschakelen'),
TextInput::make('radio_listeners_api_url')
->label('API URL')
->url(),
TextInput::make('radio_listeners_update_interval')
->label('Update Interval (seconden)')
->numeric()
->default(30),
Toggle::make('radio_show_listener_peak')
->label('Piek Luisteraars Tonen'),
TimePicker::make('radio_peak_reset_time')
->label('Piektijd Reset')
->seconds(false)
->format('H:i')
->default('00:00'),
Toggle::make('radio_store_listener_stats')
->label('Statistieken Opslaan'),
TextInput::make('radio_stats_retention_days')
->label('Retentie Dagen')
->numeric()
->default(30),
Toggle::make('radio_show_listener_graph')
->label('Luisteraars Grafiek Tonen'),
Select::make('radio_listener_graph_period')
->label('Grafiek Periode')
->options([
'1h' => '1 Uur',
'6h' => '6 Uur',
'12h' => '12 Uur',
'24h' => '24 Uur',
'7d' => '7 Dagen',
])
->default('24h'),
Toggle::make('radio_show_average_listeners')
->label('Gemiddelde Luisteraars Tonen'),
Toggle::make('radio_show_unique_listeners')
->label('Unieke Luisteraars Tonen'),
]),
Section::make('Luisteraars Meldingen')
->description('Meldingen bij luisteraars changes')
->columns(2)
->schema([
Toggle::make('radio_listener_alerts_enabled')
->label('Luisteraars Meldingen Inschakelen'),
TextInput::make('radio_min_listeners_threshold')
->label('Minimale Luisteraars Drempel')
->numeric()
->default(0),
TextInput::make('radio_listener_alert_threshold_high')
->label('Hoge Luisteraars Melding')
->numeric()
->default(100),
TextInput::make('radio_listener_alert_threshold_low')
->label('Lage Luisteraars Melding')
->numeric()
->default(5),
]),
Section::make('DJ Instellingen')
->description('Huidige DJ en schedule')
->columns(2)
->afterHeader([
Action::make('save_dj')
->label('Opslaan')
->action('saveDj')
->color('primary'),
])
->schema([
Toggle::make('radio_show_current_dj')
->label('Huidige DJ Tonen'),
TextInput::make('current_dj_id')
->label('Handmatige DJ (User ID)')
->numeric(),
TextInput::make('radio_dj_avatar_size')
->label('Avatar Grootte (px)')
->numeric()
->default(64),
TextInput::make('radio_default_avatar_figure')
->label('Default Avatar Figure (geen DJ)')
->helperText('Habbo figure code voor wanneer er geen DJ live is (bijv. hr-893-45.hd-180-2...)')
->placeholder('hr-893-45.hd-180-2.ch-210-62.lg-285-62.sh-295-62.ha-1012-62.wa-2007-0'),
TextInput::make('radio_default_dj_name')
->label('Default DJ Naam (geen DJ)')
->default('Frank'),
Toggle::make('radio_show_dj_avatar_popup')
->label('DJ Avatar Popup Tonen')
->helperText('Toon een popup met DJ avatar wanneer een DJ live gaat'),
TextInput::make('radio_dj_avatar_popup_duration')
->label('Popup Duur (seconden)')
->numeric()
->default(5)
->helperText('Hoe lang de popup zichtbaar blijft'),
Toggle::make('radio_show_dj_schedule')
->label('DJ Schedule Tonen'),
]),
Section::make('Auto DJ Detectie')
->description('Automatisch DJ detecteren')
->columns(2)
->afterHeader([
Action::make('check_connection')
->label('Check Verbinding')
->action('checkDjConnection')
->color('info'),
Action::make('save_auto_dj')
->label('Opslaan')
->action('saveAutoDj')
->color('primary'),
])
->schema([
Toggle::make('radio_auto_dj_detection')
->label('Auto DJ Detectie Inschakelen'),
TextInput::make('radio_dj_detection_interval')
->label('Detectie Interval (seconden)')
->numeric()
->default(60),
Select::make('auto_dj_detected_action')
->label('Bij Detectie')
->options([
'set_dj' => 'DJ Automatisch Instellen',
'notify' => 'Alleen Melding Sturen',
'both' => 'Instellen en Melden',
])
->default('set_dj'),
]),
Section::make('Shouts')
->description('Shoutbox instellingen')
->columns(2)
->afterHeader([
Action::make('save_shouts')
->label('Opslaan')
->action('saveShouts')
->color('primary'),
])
->schema([
Toggle::make('radio_shouts_enabled')
->label('Shouts Inschakelen'),
TextInput::make('radio_shouts_max_length')
->label('Max. Karakters')
->numeric()
->default(280),
TextInput::make('radio_shouts_cooldown')
->label('Cooldown (seconden)')
->numeric()
->default(30),
Toggle::make('radio_shouts_require_moderation')
->label('Moderatie Vereist'),
]),
Section::make('Live Chat')
->description('Live chat naast de player')
->columns(2)
->afterHeader([
Action::make('save_chat')
->label('Opslaan')
->action('saveChat')
->color('primary'),
])
->schema([
Toggle::make('radio_chat_enabled')
->label('Chat Inschakelen'),
Select::make('radio_chat_position')
->label('Chat Positie')
->options([
'left' => 'Links',
'right' => 'Rechts',
'bottom' => 'Onder',
])
->default('right'),
TextInput::make('radio_chat_width')
->label('Chat Breedte (px)')
->numeric()
->default(350),
TextInput::make('radio_chat_height')
->label('Chat Hoogte (px)')
->numeric()
->default(500),
TextInput::make('radio_chat_messages_count')
->label('Aantal Berichten')
->numeric()
->default(50),
Toggle::make('radio_chat_show_timestamps')
->label('Tijden Tonen'),
Toggle::make('radio_chat_allow_links')
->label('Links Toestaan'),
Toggle::make('radio_chat_allow_emojis')
->label('Emojis Toestaan'),
]),
Section::make('Song Verzoeken')
->description('Song request formulier instellingen')
->columns(2)
->afterHeader([
Action::make('save_requests')
->label('Opslaan')
->action('saveRequests')
->color('primary'),
])
->schema([
Toggle::make('radio_request_form_enabled')
->label('Verzoekformulier Inschakelen'),
TextInput::make('radio_request_max_per_user')
->label('Max. Verzoeken per Gebruiker')
->numeric()
->default(3),
TextInput::make('radio_request_cooldown')
->label('Cooldown (seconden)')
->numeric()
->default(300),
Toggle::make('radio_voting_enabled')
->label('Stemmen Inschakelen'),
Toggle::make('radio_voting_show_song_requests')
->label('Verzoeken met Stemmen Tonen'),
]),
Section::make('Radio Widget')
->description('Kleine radio widget voor op de site')
->columns(2)
->afterHeader([
Action::make('save_widget')
->label('Opslaan')
->action('saveWidget')
->color('primary'),
])
->schema([
Toggle::make('radio_widget_enabled')
->label('Widget Inschakelen'),
Toggle::make('radio_widget_show_globally')
->label('Widget Overal Tonen'),
Select::make('radio_widget_position')
->label('Widget Positie')
->options([
'bottom-right' => 'Rechts Onder',
'bottom-left' => 'Links Onder',
'top-right' => 'Rechts Boven',
'top-left' => 'Links Boven',
])
->default('bottom-right'),
Toggle::make('radio_embed_enabled')
->label('Externe Embed Inschakelen')
->columnSpanFull()
->helperText('Sta toe dat de radio als iframe op externe sites wordt geplaatst.'),
TextInput::make('radio_embed_allowed_domains')
->label('Toegestane Domeinen')
->placeholder('voorbeeld.nl, anderedomein.com')
->helperText('Komma-gescheiden lijst. Leeg = alle domeinen toegestaan.'),
Select::make('radio_embed_theme')
->label('Embed Thema')
->options([
'dark' => 'Donker',
'light' => 'Licht',
'transparent' => 'Transparant',
])
->default('dark'),
TextInput::make('radio_embed_height')
->label('Embed Hoogte (px)')
->numeric()
->default(400),
TextInput::make('radio_embed_width')
->label('Embed Breedte')
->placeholder('100% of 400px')
->default('100%'),
Toggle::make('radio_embed_auto_play')
->label('Auto-Play in Embed'),
]),
Section::make('Offline Pagina')
->description('Instellingen voor wanneer de radio offline is')
->columns(2)
->afterHeader([
Action::make('save_offline')
->label('Opslaan')
->action('saveOffline')
->color('primary'),
])
->schema([
Toggle::make('radio_show_offline_message')
->label('Offline Bericht Tonen'),
Textarea::make('radio_offline_message')
->label('Offline Bericht')
->rows(3),
TextInput::make('radio_offline_image')
->label('Offline Afbeelding URL')
->url(),
TextInput::make('radio_background_image')
->label('Achtergrond Afbeelding URL')
->url(),
TextInput::make('radio_logo_url')
->label('Logo URL')
->url(),
]),
Section::make('DJ Aanmeldingen')
->description('Instellingen voor DJ sollicitaties')
->columns(2)
->afterHeader([
Action::make('save_applications')
->label('Opslaan')
->action('saveApplications')
->color('primary'),
])
->schema([
Toggle::make('radio_applications_enabled')
->label('Aanmeldingen Inschakelen'),
TextInput::make('radio_applications_max_per_day')
->label('Max. Aanmeldingen per Dag')
->numeric()
->default(1),
Toggle::make('radio_applications_require_approval')
->label('Goedkeuring Vereist'),
]),
Section::make('Statistieken Weergave')
->description('Welke statistieken tonen op de site')
->columns(2)
->afterHeader([
Action::make('save_stats')
->label('Opslaan')
->action('saveStats')
->color('primary'),
])
->schema([
Toggle::make('radio_stats_show_daily')
->label('Dagelijkse Statistieken'),
Toggle::make('radio_stats_show_weekly')
->label('Wekelijkse Statistieken'),
Toggle::make('radio_stats_show_monthly')
->label('Maandelijkse Statistieken'),
Toggle::make('radio_stats_show_top_djs')
->label('Top DJs'),
Toggle::make('radio_stats_show_top_songs')
->label('Top Songs'),
]),
Section::make('Social Media Links')
->description('Links naar je social media')
->columns(2)
->afterHeader([
Action::make('save_social')
->label('Opslaan')
->action('saveSocial')
->color('primary'),
])
->schema([
TextInput::make('radio_social_facebook')
->label('Facebook URL')
->url(),
TextInput::make('radio_social_twitter')
->label('Twitter URL')
->url(),
TextInput::make('radio_social_instagram')
->label('Instagram URL')
->url(),
TextInput::make('radio_social_discord')
->label('Discord URL')
->url(),
TextInput::make('radio_social_youtube')
->label('YouTube URL')
->url(),
TextInput::make('radio_social_twitch')
->label('Twitch URL')
->url(),
]),
Section::make('Contact Informatie')
->description('Contact informatie')
->columns(2)
->schema([
TextInput::make('radio_contact_email')
->label('E-mail')
->email(),
TextInput::make('radio_contact_phone')
->label('Telefoon'),
Textarea::make('radio_about_text')
->label('Over Ons Tekst')
->rows(4),
]),
Section::make('Discord Webhook')
->description('Discord notificaties voor DJ live, nummers, etc.')
->columns(2)
->afterHeader([
Action::make('test_discord')
->label('Test Webhook')
->action('testDiscordWebhook')
->color('warning'),
Action::make('save_discord')
->label('Opslaan')
->action('saveDiscord')
->color('primary'),
])
->schema([
Toggle::make('radio_discord_enabled')
->label('Discord Inschakelen')
->columnSpanFull(),
TextInput::make('radio_discord_webhook_url')
->label('Webhook URL')
->url()
->columnSpanFull(),
Toggle::make('radio_discord_dj_live')
->label('DJ Live Melding'),
Toggle::make('radio_discord_song_changes')
->label('Nummer Wijziging Melding'),
Textarea::make('radio_discord_custom_message')
->label('Custom Bericht')
->rows(3)
->columnSpanFull()
->helperText('Gebruik {dj_name}, {song}, {listeners}'),
]),
Section::make('Custom Code')
->description('Aangepaste CSS en JavaScript')
->columns(1)
->afterHeader([
Action::make('save_custom')
->label('Opslaan')
->action('saveCustom')
->color('primary'),
])
->schema([
Textarea::make('radio_custom_css')
->label('Custom CSS')
->rows(10),
Textarea::make('radio_custom_js')
->label('Custom JavaScript')
->rows(10),
]),
])
->statePath('data');
}
public function savePlayer(): void
{
$this->saveSettings([
'radio_style', 'radio_player_type', 'radio_auto_play',
'radio_auto_play_delay', 'radio_mute_on_start', 'radio_volume',
'radio_language', 'radio_multi_language_enabled',
]);
}
public function saveAzurecast(): void
{
$this->saveSettings([
'radio_enabled', 'radio_azurecast_base_url', 'radio_azurecast_station_id',
'radio_azurecast_api_key', 'radio_azurecast_port', 'radio_azurecast_protocol',
'radio_azurecast_use_proxy', 'radio_stream_url', 'radio_stream_backup_url',
]);
}
public function saveSambroadcaster(): void
{
$this->saveSettings([
'radio_sambroadcaster_api_url', 'radio_sambroadcaster_password',
'radio_virtual_dj_url', 'radio_virtual_dj_password', 'radio_djs_api_url',
]);
}
public function saveAutoDj(): void
{
$this->saveSettings([
'radio_auto_dj_detection', 'radio_dj_detection_interval',
'auto_dj_detected_action',
]);
}
public function saveChat(): void
{
$this->saveSettings([
'radio_chat_enabled', 'radio_chat_position', 'radio_chat_width',
'radio_chat_height', 'radio_chat_messages_count', 'radio_chat_show_timestamps',
'radio_chat_allow_links', 'radio_chat_allow_emojis',
]);
}
public function saveColors(): void
{
$this->saveSettings([
'radio_player_color_primary', 'radio_player_color_secondary',
'radio_player_color_text', 'radio_player_color_accent',
]);
}
public function saveNowPlaying(): void
{
$this->saveSettings([
'radio_now_playing_enabled', 'radio_now_playing_api_url',
'radio_now_playing_cache_duration', 'radio_show_song_history',
'radio_song_history_count',
]);
}
public function saveListeners(): void
{
$this->saveSettings([
'radio_listeners_enabled', 'radio_listeners_api_url',
'radio_listeners_update_interval', 'radio_show_listener_peak',
'radio_peak_reset_time', 'radio_store_listener_stats',
'radio_stats_retention_days', 'radio_show_listener_graph',
'radio_listener_graph_period', 'radio_show_average_listeners',
'radio_show_unique_listeners',
]);
}
public function saveDj(): void
{
$this->saveSettings([
'radio_show_current_dj', 'current_dj_id', 'radio_dj_avatar_size',
'radio_default_avatar_figure', 'radio_default_dj_name',
'radio_show_dj_avatar_popup', 'radio_dj_avatar_popup_duration',
'radio_show_dj_schedule',
]);
}
public function saveShouts(): void
{
$this->saveSettings([
'radio_shouts_enabled', 'radio_shouts_max_length',
'radio_shouts_cooldown', 'radio_shouts_require_moderation',
]);
}
public function saveRequests(): void
{
$this->saveSettings([
'radio_request_form_enabled', 'radio_request_max_per_user',
'radio_request_cooldown', 'radio_voting_enabled',
'radio_voting_show_song_requests',
]);
}
public function saveWidget(): void
{
$this->saveSettings([
'radio_widget_enabled', 'radio_widget_show_globally',
'radio_widget_position',
'radio_embed_enabled', 'radio_embed_allowed_domains',
'radio_embed_theme', 'radio_embed_height',
'radio_embed_width', 'radio_embed_auto_play',
]);
}
public function saveOffline(): void
{
$this->saveSettings([
'radio_show_offline_message', 'radio_offline_message',
'radio_offline_image', 'radio_background_image',
'radio_logo_url',
]);
}
public function saveApplications(): void
{
$this->saveSettings([
'radio_applications_enabled', 'radio_applications_max_per_day',
'radio_applications_require_approval',
]);
}
public function saveStats(): void
{
$this->saveSettings([
'radio_stats_show_daily', 'radio_stats_show_weekly',
'radio_stats_show_monthly', 'radio_stats_show_top_djs',
'radio_stats_show_top_songs',
]);
}
public function saveSocial(): void
{
$this->saveSettings([
'radio_social_facebook', 'radio_social_twitter',
'radio_social_instagram', 'radio_social_discord',
'radio_social_youtube', 'radio_social_twitch',
'radio_contact_email', 'radio_contact_phone',
'radio_about_text',
]);
}
public function saveCustom(): void
{
$this->saveSettings([
'radio_custom_css', 'radio_custom_js',
]);
}
public function saveDiscord(): void
{
$this->saveSettings([
'radio_discord_enabled', 'radio_discord_webhook_url',
'radio_discord_dj_live', 'radio_discord_song_changes',
'radio_discord_custom_message',
]);
}
public function autoSetupAzurecast(): void
{
$streamUrl = $this->data['radio_stream_url'] ?? '';
$baseUrl = $this->data['radio_azurecast_base_url'] ?? '';
if (empty($streamUrl) && empty($baseUrl)) {
Notification::make()
->warning()
->title('URL Vereist')
->body('Vul de Stream URL of AzureCast Basis URL in.')
->send();
return;
}
try {
if (! empty($streamUrl)) {
$parsed = parse_url((string) $streamUrl);
if ($parsed) {
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
$detectedBaseUrl = $scheme . '://' . $host;
$testUrl = $detectedBaseUrl . '/api/nowplaying';
$response = Http::timeout(5)->get($testUrl);
if ($response->ok()) {
$data = $response->json();
if (is_array($data)) {
$stationId = $data[0]['station']['id'] ?? $data['station']['id'] ?? 1;
$this->saveSettings([
'radio_enabled' => true,
'radio_azurecast_base_url' => $detectedBaseUrl,
'radio_azurecast_station_id' => $stationId,
'radio_now_playing_enabled' => true,
'radio_listeners_enabled' => true,
'radio_show_current_dj' => true,
'radio_widget_enabled' => true,
'radio_widget_show_globally' => true,
]);
Notification::make()
->success()
->title('Success!')
->body("AzureCast gedetecteerd! Station ID: {$stationId}")
->send();
return;
}
}
}
}
if (! empty($baseUrl)) {
$baseUrl = rtrim((string) $baseUrl, '/');
$response = Http::timeout(10)->get($baseUrl . '/api/stations');
if ($response->ok()) {
$stations = $response->json();
if (! empty($stations) && is_array($stations)) {
$station = $stations[0];
$stationId = $station['id'] ?? 1;
$stationName = $station['name'] ?? 'Radio';
$streamFromStation = $station['url'] ?? $station['mounts'][0]['url'] ?? $streamUrl ?? '';
$this->saveSettings([
'radio_enabled' => true,
'radio_azurecast_base_url' => $baseUrl,
'radio_azurecast_station_id' => $stationId,
'radio_stream_url' => $streamFromStation,
'radio_now_playing_enabled' => true,
'radio_listeners_enabled' => true,
'radio_show_current_dj' => true,
'radio_widget_enabled' => true,
'radio_widget_show_globally' => true,
]);
Notification::make()
->success()
->title('Success!')
->body("Station '{$stationName}' (ID: {$stationId}) is geconfigureerd!")
->send();
return;
}
}
}
Notification::make()
->danger()
->title('Niet Gevonden')
->body('Kon AzureCast niet detecteren. Controleer de URL.')
->send();
} catch (\Exception $e) {
Notification::make()
->danger()
->title('Verbindingsfout')
->body('Kon geen verbinding maken: ' . $e->getMessage())
->send();
}
}
public function fullAutoSetup(): void
{
$streamUrl = $this->data['radio_stream_url'] ?? '';
if (empty($streamUrl)) {
Notification::make()
->warning()
->title('Stream URL Vereist')
->body('Voer je AzureCast stream URL in (bijv. https://jouw-radio.nl/radio.mp3)')
->send();
return;
}
Notification::make()
->info()
->title('Auto-Setup')
->body('Configuratie wordt gestart...')
->send();
$detectedBaseUrl = null;
$stationId = 1;
$apiKey = '';
$parsed = parse_url($streamUrl);
if ($parsed) {
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
$detectedBaseUrl = $scheme . '://' . $host;
$apiTestUrl = $detectedBaseUrl . '/api/nowplaying/1';
try {
$response = Http::timeout(10)->get($apiTestUrl);
if ($response->ok()) {
$npData = $response->json();
if (isset($npData['station']['id'])) {
$stationId = $npData['station']['id'];
}
}
} catch (\Exception) {
try {
$response = Http::timeout(10)->get($detectedBaseUrl . '/api/stations');
if ($response->ok()) {
$stations = $response->json();
if (! empty($stations)) {
$stationId = $stations[0]['id'] ?? 1;
if (! empty($stations[0]['url'])) {
$streamUrl = $stations[0]['url'];
}
}
}
} catch (\Exception) {
// Continue with defaults
}
}
}
if (empty($detectedBaseUrl)) {
Notification::make()
->danger()
->title('AzureCast Niet Gevonden')
->body('Kon AzureCast server niet bereiken. Controleer de stream URL.')
->send();
return;
}
$settings = [
'radio_enabled' => '1',
'radio_azurecast_base_url' => $detectedBaseUrl,
'radio_azurecast_station_id' => (string) $stationId,
'radio_stream_url' => $streamUrl,
'radio_now_playing_enabled' => '1',
'radio_listeners_enabled' => '1',
'radio_show_current_dj' => '1',
'radio_widget_enabled' => '1',
'radio_widget_show_globally' => '1',
'radio_azurecast_use_proxy' => '1',
'radio_shouts_enabled' => '1',
'radio_applications_enabled' => '1',
];
foreach ($settings as $key => $value) {
WebsiteSetting::updateOrCreate(
['key' => $key],
['value' => $value],
);
}
Cache::flush();
Artisan::call('config:clear');
Artisan::call('cache:clear');
Notification::make()
->success()
->title('🎉 Radio Klaar!')
->body("Alles is geconfigureerd! Stream: {$streamUrl}")
->send();
}
public function installAndSetupAzurecast(): void
{
Notification::make()
->info()
->title('Zoeken naar AzureCast...')
->body('Ik zoek naar een bestaande AzureCast server.')
->send();
$foundPort = null;
$commonPorts = [2000, 2250, 8000, 8080, 8443, 9000];
foreach ($commonPorts as $testPort) {
try {
$response = Http::timeout(2)->get("http://localhost:{$testPort}/api/nowplaying/1");
if ($response->ok()) {
$foundPort = $testPort;
break;
}
} catch (\Exception) {
// Try next port
}
}
if ($foundPort) {
Notification::make()
->success()
->title('AzureCast Gevonden!')
->body("AzureCast draait al op poort {$foundPort}")
->send();
$this->configureExistingAzureCast($foundPort);
return;
}
Notification::make()
->info()
->title('Geen AzureCast Gevonden')
->body('Voer je stream URL in en klik op Auto-Setup')
->send();
$this->fullAutoSetup();
}
private function configureExistingAzureCast(int $port): void
{
$baseUrl = "http://localhost:{$port}";
try {
$response = Http::timeout(5)->get($baseUrl . '/api/stations');
$stations = $response->ok() ? $response->json() : [];
$stationId = $stations[0]['id'] ?? 1;
$streamUrl = $stations[0]['url'] ?? $baseUrl . '/radio.mp3';
} catch (\Exception $e) {
$stationId = 1;
$streamUrl = $baseUrl . '/radio.mp3';
}
$settings = [
'radio_enabled' => '1',
'radio_azurecast_base_url' => $baseUrl,
'radio_azurecast_station_id' => (string) $stationId,
'radio_stream_url' => $streamUrl,
'radio_now_playing_enabled' => '1',
'radio_listeners_enabled' => '1',
'radio_show_current_dj' => '1',
'radio_widget_enabled' => '1',
'radio_widget_show_globally' => '1',
'radio_shouts_enabled' => '1',
];
foreach ($settings as $key => $value) {
WebsiteSetting::updateOrCreate(
['key' => $key],
['value' => $value],
);
}
Cache::flush();
Artisan::call('config:clear');
Artisan::call('cache:clear');
Notification::make()
->success()
->title('🎉 Alles Klaar!')
->body("Radio werkt op http://localhost:{$port}")
->send();
}
private function autoConfigureFromUrl(string $streamUrl): void
{
$parsed = parse_url($streamUrl);
if (! $parsed) {
Notification::make()
->danger()
->title('Ongeldige URL')
->body(' Voer een geldige stream URL in.')
->send();
return;
}
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
$baseUrl = $scheme . '://' . $host;
try {
$response = Http::timeout(10)->get($baseUrl . '/api/nowplaying/1');
if (! $response->ok()) {
$response = Http::timeout(10)->get($baseUrl . '/api/stations');
}
} catch (\Exception $e) {
Notification::make()
->danger()
->title('Verbindingsfout')
->body('Kon AzureCast niet bereiken.')
->send();
return;
}
$stationId = 1;
$actualStreamUrl = $streamUrl;
try {
$npResponse = Http::timeout(5)->get($baseUrl . '/api/nowplaying/1');
if ($npResponse->ok()) {
$npData = $npResponse->json();
$stationId = $npData['station']['id'] ?? 1;
}
$stationsResponse = Http::timeout(5)->get($baseUrl . '/api/stations');
if ($stationsResponse->ok()) {
$stations = $stationsResponse->json();
if (! empty($stations)) {
$stationId = $stations[0]['id'] ?? 1;
$actualStreamUrl = $stations[0]['url'] ?? $streamUrl;
}
}
} catch (\Exception) {
// Use defaults
}
$settings = [
'radio_enabled' => '1',
'radio_azurecast_base_url' => $baseUrl,
'radio_azurecast_station_id' => (string) $stationId,
'radio_stream_url' => $actualStreamUrl,
'radio_now_playing_enabled' => '1',
'radio_listeners_enabled' => '1',
'radio_show_current_dj' => '1',
'radio_widget_enabled' => '1',
'radio_widget_show_globally' => '1',
'radio_shouts_enabled' => '1',
];
foreach ($settings as $key => $value) {
WebsiteSetting::updateOrCreate(
['key' => $key],
['value' => $value],
);
}
Cache::flush();
Artisan::call('config:clear');
Artisan::call('cache:clear');
Notification::make()
->success()
->title('🎉 Radio Klaar!')
->body("Alles geconfigureerd op {$baseUrl}")
->send();
}
private function findFreePort(int $start = 2000, int $end = 9000): ?int
{
for ($port = $start; $port <= $end; $port++) {
$connection = @fsockopen('127.0.0.1', $port);
if ($connection === false) {
return $port;
}
fclose($connection);
}
return null;
}
public function testDiscordWebhook(): void
{
$webhookUrl = $this->data['radio_discord_webhook_url'] ?? '';
if (empty($webhookUrl)) {
Notification::make()
->warning()
->title('Webhook URL Vereist')
->body('Vul eerst de Discord Webhook URL in.')
->send();
return;
}
try {
$payload = ['content' => '🎵 Radio Discord integration test - Success!'];
$response = Http::post($webhookUrl, $payload);
if ($response->ok()) {
Notification::make()
->success()
->title('Test Verzonden!')
->body('Testbericht is succesvol naar Discord gestuurd.')
->send();
} else {
Notification::make()
->danger()
->title(__('Error'))
->body('Kon bericht niet versturen. Status: ' . $response->status())
->send();
}
} catch (\Exception $e) {
Notification::make()
->danger()
->title(__('Error'))
->body('Verbindingsfout: ' . $e->getMessage())
->send();
}
}
public function checkDjConnection(): void
{
$detectedDj = null;
$source = null;
$sambroadcasterUrl = $this->data['radio_sambroadcaster_api_url'] ?? '';
$virtualDjUrl = $this->data['radio_virtual_dj_url'] ?? '';
if (! empty($sambroadcasterUrl)) {
try {
$response = Http::timeout(5)->get($sambroadcasterUrl . '/dj');
if ($response->ok()) {
$data = $response->json();
$detectedDj = $data['dj_name'] ?? $data['dj'] ?? $data['current_dj'] ?? null;
$source = 'Sambroadcaster';
}
} catch (\Exception $e) {
Notification::make()
->warning()
->title('Sambroadcaster Fout')
->body('Kon geen verbinding maken met Sambroadcaster: ' . $e->getMessage())
->send();
}
}
if ($detectedDj === null && ! empty($virtualDjUrl)) {
try {
$response = Http::timeout(5)->withBasicAuth('admin', $this->data['radio_virtual_dj_password'] ?? '')
->get($virtualDjUrl . '/api/v1/status');
if ($response->ok()) {
$data = $response->json();
$detectedDj = $data['current_dj'] ?? $data['dj'] ?? null;
$source = 'Virtual DJ';
}
} catch (\Exception $e) {
Notification::make()
->warning()
->title('Virtual DJ Fout')
->body('Kon geen verbinding maken met Virtual DJ: ' . $e->getMessage())
->send();
}
}
if ($detectedDj !== null) {
$action = $this->data['auto_dj_detected_action'] ?? 'set_dj';
if (in_array($action, ['set_dj', 'both'])) {
Notification::make()
->success()
->title('DJ Gedetecteerd!')
->body("{$detectedDj} is verbonden via {$source}.")
->send();
} else {
Notification::make()
->info()
->title('DJ Gedetecteerd')
->body("{$detectedDj} is actief via {$source}.")
->send();
}
} else {
Notification::make()
->warning()
->title('Geen DJ Gevonden')
->body('Er kon geen actieve DJ worden gedetecteerd.')
->send();
}
}
private function saveSettings(array $keys): void
{
foreach ($keys as $key => $value) {
if (is_numeric($key)) {
$settingKey = $value;
$settingValue = $this->data[$value] ?? '';
} else {
$settingKey = $key;
$settingValue = $value;
}
$boolSettings = [
'radio_enabled', 'radio_auto_play', 'radio_mute_on_start',
'radio_multi_language_enabled', 'radio_show_current_dj',
'radio_now_playing_enabled', 'radio_show_song_history',
'radio_listeners_enabled', 'radio_show_listener_peak',
'radio_store_listener_stats', 'radio_show_listener_graph',
'radio_show_average_listeners', 'radio_show_unique_listeners',
'radio_show_dj_schedule', 'radio_auto_dj_detection',
'radio_shouts_enabled', 'radio_shouts_require_moderation',
'radio_chat_enabled', 'radio_chat_show_timestamps',
'radio_chat_allow_links', 'radio_chat_allow_emojis',
'radio_request_form_enabled', 'radio_voting_enabled',
'radio_voting_show_song_requests', 'radio_stats_show_daily',
'radio_stats_show_weekly', 'radio_stats_show_monthly',
'radio_stats_show_top_djs', 'radio_stats_show_top_songs',
'radio_show_offline_message', 'radio_widget_enabled',
'radio_widget_show_globally', 'radio_embed_enabled',
'radio_embed_auto_play', 'radio_applications_enabled',
'radio_applications_require_approval', 'radio_discord_enabled',
'radio_discord_dj_live', 'radio_discord_song_changes',
'radio_listener_alerts_enabled', 'radio_azurecast_use_proxy',
];
$intSettings = [
'radio_auto_play_delay', 'radio_volume',
'radio_now_playing_cache_duration', 'radio_song_history_count',
'radio_listeners_update_interval', 'radio_stats_retention_days',
'radio_dj_avatar_size', 'radio_dj_detection_interval',
'radio_shouts_max_length', 'radio_shouts_cooldown',
'radio_chat_width', 'radio_chat_height', 'radio_chat_messages_count',
'radio_embed_height',
'radio_request_max_per_user', 'radio_request_cooldown',
'radio_min_listeners_threshold', 'radio_listener_alert_threshold_high',
'radio_listener_alert_threshold_low', 'radio_applications_max_per_day',
'radio_azurecast_station_id',
];
if (in_array($settingKey, $boolSettings)) {
$dbValue = $settingValue ? '1' : '0';
} elseif (in_array($settingKey, $intSettings)) {
$dbValue = (string) ($settingValue ?? 0);
} else {
$dbValue = (string) ($settingValue ?? '');
}
WebsiteSetting::updateOrCreate(
['key' => $settingKey],
['value' => $dbValue],
);
}
$this->clearCache();
Notification::make()
->success()
->title(__('Saved'))
->body('Alle instellingen zijn succesvol opgeslagen!')
->send();
}
private function clearCache(): void
{
Cache::forget(self::CACHE_KEY);
}
private function getSettingBool(string $key): bool
{
return (bool) WebsiteSetting::where('key', $key)->first()?->value;
}
private function getSetting(string $key, string $default = ''): string
{
return WebsiteSetting::where('key', $key)->first()?->value ?? $default;
}
#[\Override]
protected function getHeaderActions(): array
{
return [
Action::make('setup')
->label('🚀 ' . __('radio.setup.button_label'))
->icon('heroicon-o-rocket-launch')
->color('warning')
->size('lg')
->requiresConfirmation()
->modalHeading(__('radio.setup.modal_heading'))
->modalDescription(__('radio.setup.modal_description'))
->modalSubmitActionLabel(__('radio.setup.modal_submit'))
->action(function () {
$this->runAllInOneSetup();
})
->tooltip(__('radio.setup.tooltip')),
];
}
public function runAllInOneSetup(): void
{
try {
// Standard radio settings
$settings = [
'radio_enabled' => '1',
'radio_stream_url' => 'https://stream.radioking.com/radio/83232/radio.mp3',
'radio_style' => 'dark',
'radio_auto_play' => '0',
'points_enabled' => '1',
'points_per_minute' => '2',
'max_points_per_day' => '100',
'points_for_request' => '5',
'points_for_vote' => '2',
'points_for_giveaway_win' => '50',
'points_for_contest_win' => '100',
'radio_shouts_enabled' => '1',
'radio_now_playing_enabled' => '1',
'radio_listeners_enabled' => '1',
'radio_show_current_dj' => '1',
'radio_widget_enabled' => '1',
'radio_widget_show_globally' => '1',
'radio_widget_position' => 'bottom-right',
'radio_applications_enabled' => '1',
'radio_auto_dj_detection' => '1',
'radio_monitoring_enabled' => '1',
'radio_monitoring_timeout' => '5',
'radio_stream_fallback_url' => '',
'radio_stream_backup_enabled' => '0',
'radio_show_song_history' => '1',
'radio_show_schedule_preview' => '1',
'radio_max_history_items' => '10',
'radio_social_links_enabled' => '1',
'radio_facebook_url' => '',
'radio_twitter_url' => '',
'radio_discord_url' => '',
'radio_instagram_url' => '',
'radio_youtube_url' => '',
'radio_twitch_url' => '',
'radio_word_filter_enabled' => '1',
'radio_max_shout_length' => '280',
'radio_shout_cooldown' => '30',
'radio_contests_enabled' => '1',
'radio_giveaways_enabled' => '1',
'radio_auto_contest_creation' => '0',
];
// Update all settings
foreach ($settings as $key => $value) {
WebsiteSetting::updateOrCreate(
['key' => $key],
['value' => $value, 'comment' => 'Auto-configured by All-in-One Setup'],
);
}
// Clear cache
$this->clearCache();
// Reload form data
$this->fillForm();
Notification::make()
->success()
->title(__('radio.setup.success_title'))
->body(__('radio.setup.success_body'))
->send();
} catch (\Exception $e) {
Notification::make()
->danger()
->title(__('radio.setup.error_title'))
->body(__('radio.setup.error_body', ['message' => $e->getMessage()]))
->send();
}
}
}