You've already forked Atomcms-edit
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
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Pages\Radio;
|
||||
|
||||
use App\Models\RadioApiKey;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Tabs;
|
||||
use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Concerns\InteractsWithTable;
|
||||
use Filament\Tables\Contracts\HasTable;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
final class ApiKeys extends Page implements HasTable
|
||||
{
|
||||
use InteractsWithTable;
|
||||
|
||||
#[\Override]
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-key';
|
||||
|
||||
#[\Override]
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Radio';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $navigationLabel = 'API Sleutels';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $title = 'Radio API Sleutels';
|
||||
|
||||
#[\Override]
|
||||
protected string $view = 'filament.pages.radio.api-keys';
|
||||
|
||||
public ?string $newKey = null;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(RadioApiKey::query())
|
||||
->defaultSort('created_at', 'desc')
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->label('Naam')
|
||||
->searchable(),
|
||||
TextColumn::make('key')
|
||||
->label('API Key')
|
||||
->formatStateUsing(fn ($state) => substr($state, 0, 16) . '...')
|
||||
->copyable()
|
||||
->copyMessage('Gekopieerd!'),
|
||||
TextColumn::make('rate_limit')
|
||||
->label('Rate Limit')
|
||||
->badge()
|
||||
->color('info'),
|
||||
IconColumn::make('is_active')
|
||||
->label('Actief')
|
||||
->boolean()
|
||||
->trueIcon('heroicon-o-check-circle')
|
||||
->falseIcon('heroicon-o-x-circle')
|
||||
->trueColor('success')
|
||||
->falseColor('danger'),
|
||||
TextColumn::make('last_used_at')
|
||||
->label('Laatst gebruikt')
|
||||
->dateTime('d-m-Y H:i')
|
||||
->placeholder('Nooit'),
|
||||
TextColumn::make('expires_at')
|
||||
->label('Verloopt')
|
||||
->dateTime('d-m-Y')
|
||||
->placeholder('Nooit'),
|
||||
TextColumn::make('created_at')
|
||||
->label('Aangemaakt')
|
||||
->dateTime('d-m-Y H:i'),
|
||||
])
|
||||
->recordActions(function ($record) {
|
||||
return [
|
||||
ActionGroup::make([
|
||||
Action::make('toggle')
|
||||
->label($record->is_active ? 'Deactiveren' : 'Activeren')
|
||||
->icon($record->is_active ? 'heroicon-o-pause' : 'heroicon-o-play')
|
||||
->action(fn () => $this->toggleKey($record)),
|
||||
Action::make('delete')
|
||||
->label('Verwijderen')
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(fn () => $record->delete()),
|
||||
]),
|
||||
];
|
||||
})
|
||||
->toolbarActions([
|
||||
Action::make('create')
|
||||
->label('Nieuwe API Sleutel')
|
||||
->icon('heroicon-o-plus')
|
||||
->color('primary')
|
||||
->form([
|
||||
TextInput::make('name')
|
||||
->label('Naam')
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->placeholder('Bijv. Discord Bot, Mobiele App'),
|
||||
TextInput::make('allowed_ips')
|
||||
->label('Toegestane IP-adressen')
|
||||
->placeholder('Leeg laten voor alle IPs (bijv. 1.2.3.4, 5.6.7.8)')
|
||||
->helperText('Komma-gescheiden lijst van IP-adressen. Leeg = alle IPs toegestaan.'),
|
||||
Select::make('rate_limit')
|
||||
->label('Rate Limit (verzoeken per minuut)')
|
||||
->options([
|
||||
60 => '60/min',
|
||||
100 => '100/min',
|
||||
300 => '300/min',
|
||||
500 => '500/min',
|
||||
1000 => '1000/min',
|
||||
])
|
||||
->default(300),
|
||||
Select::make('permissions')
|
||||
->label('Permissies')
|
||||
->multiple()
|
||||
->options([
|
||||
'*' => 'Alle permissies',
|
||||
'now-playing' => 'Nu Afspelen',
|
||||
'listeners' => 'Luisteraars',
|
||||
'current-dj' => 'Huidige DJ',
|
||||
'config' => 'Configuratie',
|
||||
'shouts' => 'Shouts',
|
||||
'points' => 'Punten',
|
||||
'requests' => 'Song Requests',
|
||||
])
|
||||
->default(['*']),
|
||||
Toggle::make('set_expiration')
|
||||
->label('Vervaldatum instellen')
|
||||
->live(),
|
||||
TextInput::make('expires_at')
|
||||
->label('Vervaldatum')
|
||||
->type('date')
|
||||
->visible(fn ($get) => $get('set_expiration')),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
$this->createKey($data);
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createKey(array $data): void
|
||||
{
|
||||
$key = RadioApiKey::generate($data['name'], [
|
||||
'allowed_ips' => $data['allowed_ips'] ?? null,
|
||||
'permissions' => $data['permissions'] ?? ['*'],
|
||||
'rate_limit' => $data['rate_limit'] ?? 300,
|
||||
'expires_at' => ! empty($data['set_expiration']) && ! empty($data['expires_at'])
|
||||
? $data['expires_at'] : null,
|
||||
]);
|
||||
|
||||
$this->newKey = $key->key;
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('API Sleutel Aangemaakt!')
|
||||
->body('Kopieer de sleutel nu - deze wordt niet meer getoond.')
|
||||
->send();
|
||||
}
|
||||
|
||||
private function toggleKey(RadioApiKey $key): void
|
||||
{
|
||||
$key->update(['is_active' => ! $key->is_active]);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title($key->is_active ? 'API Sleutel geactiveerd' : 'API Sleutel gedeactiveerd')
|
||||
->send();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getNewKey(): ?string
|
||||
{
|
||||
$key = $this->newKey;
|
||||
$this->newKey = null;
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Pages\Radio;
|
||||
|
||||
use App\Models\RadioAutoDjTrack;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Concerns\InteractsWithTable;
|
||||
use Filament\Tables\Contracts\HasTable;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
final class AutoDjPlaylist extends Page implements HasTable
|
||||
{
|
||||
use InteractsWithTable;
|
||||
|
||||
#[\Override]
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-play-circle';
|
||||
|
||||
#[\Override]
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Radio';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $navigationLabel = 'Auto DJ Playlist';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $title = 'Auto DJ Playlist';
|
||||
|
||||
#[\Override]
|
||||
protected string $view = 'filament.pages.radio.auto-dj-playlist';
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(RadioAutoDjTrack::query())
|
||||
->defaultSort('sort_order')
|
||||
->columns([
|
||||
TextColumn::make('sort_order')
|
||||
->label('#')
|
||||
->sortable()
|
||||
->width(50),
|
||||
TextColumn::make('title')
|
||||
->label('Titel')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('artist')
|
||||
->label('Artiest')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('duration')
|
||||
->label('Duur')
|
||||
->formatStateUsing(fn ($state) => $state ? gmdate('i:s', $state) : '-'),
|
||||
TextColumn::make('play_count')
|
||||
->label('Gespeeld')
|
||||
->sortable(),
|
||||
TextColumn::make('last_played_at')
|
||||
->label('Laatst gespeeld')
|
||||
->dateTime('d-m-Y H:i')
|
||||
->placeholder('Nooit'),
|
||||
IconColumn::make('is_active')
|
||||
->label('Actief')
|
||||
->boolean()
|
||||
->trueIcon('heroicon-o-check-circle')
|
||||
->falseIcon('heroicon-o-x-circle')
|
||||
->trueColor('success')
|
||||
->falseColor('danger'),
|
||||
])
|
||||
->recordActions(function ($record) {
|
||||
return [
|
||||
ActionGroup::make([
|
||||
Action::make('toggle_active')
|
||||
->label($record->is_active ? 'Deactiveren' : 'Activeren')
|
||||
->icon($record->is_active ? 'heroicon-o-pause' : 'heroicon-o-play')
|
||||
->action(fn () => $this->toggleActive($record)),
|
||||
Action::make('move_up')
|
||||
->label('Omhoog')
|
||||
->icon('heroicon-o-chevron-up')
|
||||
->action(fn () => $this->moveUp($record)),
|
||||
Action::make('move_down')
|
||||
->label('Omlaag')
|
||||
->icon('heroicon-o-chevron-down')
|
||||
->action(fn () => $this->moveDown($record)),
|
||||
Action::make('delete')
|
||||
->label('Verwijderen')
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(fn () => $record->delete()),
|
||||
]),
|
||||
];
|
||||
})
|
||||
->toolbarActions([
|
||||
Action::make('create')
|
||||
->label('Track Toevoegen')
|
||||
->icon('heroicon-o-plus')
|
||||
->color('primary')
|
||||
->form([
|
||||
TextInput::make('title')
|
||||
->label('Titel')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
TextInput::make('artist')
|
||||
->label('Artiest')
|
||||
->maxLength(255),
|
||||
TextInput::make('album')
|
||||
->label('Album')
|
||||
->maxLength(255),
|
||||
TextInput::make('artwork_url')
|
||||
->label('Afbeelding URL')
|
||||
->url(),
|
||||
TextInput::make('duration')
|
||||
->label('Duur (seconden)')
|
||||
->numeric()
|
||||
->minValue(0),
|
||||
Toggle::make('is_active')
|
||||
->label('Actief')
|
||||
->default(true),
|
||||
])
|
||||
->action(function (array $data) {
|
||||
$this->createTrack($data);
|
||||
}),
|
||||
])
|
||||
->poll('30s');
|
||||
}
|
||||
|
||||
public function getStatusBadge(): string
|
||||
{
|
||||
$autoDj = cache('radio_auto_dj_active');
|
||||
|
||||
if ($autoDj) {
|
||||
return 'Auto DJ is actief - speelt: ' . ($autoDj['title'] ?? 'onbekend');
|
||||
}
|
||||
|
||||
return 'Auto DJ is niet actief';
|
||||
}
|
||||
|
||||
private function createTrack(array $data): void
|
||||
{
|
||||
$maxOrder = RadioAutoDjTrack::max('sort_order') ?? 0;
|
||||
|
||||
RadioAutoDjTrack::create([
|
||||
'title' => $data['title'],
|
||||
'artist' => $data['artist'] ?? null,
|
||||
'album' => $data['album'] ?? null,
|
||||
'artwork_url' => $data['artwork_url'] ?? null,
|
||||
'duration' => $data['duration'] ? (int) $data['duration'] : null,
|
||||
'is_active' => $data['is_active'] ?? true,
|
||||
'sort_order' => $maxOrder + 1,
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Track toegevoegd aan playlist')
|
||||
->send();
|
||||
}
|
||||
|
||||
private function toggleActive(RadioAutoDjTrack $track): void
|
||||
{
|
||||
$track->update(['is_active' => ! $track->is_active]);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title($track->is_active ? 'Track geactiveerd' : 'Track gedeactiveerd')
|
||||
->send();
|
||||
}
|
||||
|
||||
private function moveUp(RadioAutoDjTrack $track): void
|
||||
{
|
||||
$prev = RadioAutoDjTrack::where('sort_order', '<', $track->sort_order)
|
||||
->orderBy('sort_order', 'desc')
|
||||
->first();
|
||||
|
||||
if ($prev) {
|
||||
$temp = $track->sort_order;
|
||||
$track->update(['sort_order' => $prev->sort_order]);
|
||||
$prev->update(['sort_order' => $temp]);
|
||||
}
|
||||
}
|
||||
|
||||
private function moveDown(RadioAutoDjTrack $track): void
|
||||
{
|
||||
$next = RadioAutoDjTrack::where('sort_order', '>', $track->sort_order)
|
||||
->orderBy('sort_order')
|
||||
->first();
|
||||
|
||||
if ($next) {
|
||||
$temp = $track->sort_order;
|
||||
$track->update(['sort_order' => $next->sort_order]);
|
||||
$next->update(['sort_order' => $temp]);
|
||||
}
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Pages\Radio;
|
||||
|
||||
use App\Models\RadioApplication;
|
||||
use App\Models\RadioShout;
|
||||
use App\Models\RadioSongRequest;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Page;
|
||||
|
||||
final class DjModeration extends Page
|
||||
{
|
||||
#[\Override]
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';
|
||||
|
||||
#[\Override]
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Radio';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $navigationLabel = 'Moderatie';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $title = 'DJ Moderatie';
|
||||
|
||||
#[\Override]
|
||||
protected string $view = 'filament.pages.radio.dj-moderation';
|
||||
|
||||
public function getShouts()
|
||||
{
|
||||
return RadioShout::with('user')->latest()->paginate(20);
|
||||
}
|
||||
|
||||
public function getPendingShouts()
|
||||
{
|
||||
return RadioShout::with('user')->where('approved', false)->latest()->paginate(20);
|
||||
}
|
||||
|
||||
public function getReportedShouts()
|
||||
{
|
||||
return RadioShout::with('user')->where('reported', true)->latest()->paginate(20);
|
||||
}
|
||||
|
||||
public function getRequests()
|
||||
{
|
||||
return RadioSongRequest::with('user')->latest('submitted_at')->paginate(20);
|
||||
}
|
||||
|
||||
public function getPendingRequests()
|
||||
{
|
||||
return RadioSongRequest::with('user')->where('is_approved', false)->where('is_played', false)->latest('submitted_at')->paginate(20);
|
||||
}
|
||||
|
||||
public function getApplications()
|
||||
{
|
||||
return RadioApplication::with(['user', 'rank'])->latest()->paginate(20);
|
||||
}
|
||||
|
||||
public function getPendingApplications()
|
||||
{
|
||||
return RadioApplication::with(['user', 'rank'])->where('status', 'pending')->latest()->paginate(20);
|
||||
}
|
||||
|
||||
public function approveShout(int $id): void
|
||||
{
|
||||
$shout = RadioShout::findOrFail($id);
|
||||
$shout->update([
|
||||
'approved' => true,
|
||||
'approved_by' => auth()->id(),
|
||||
'approved_at' => now(),
|
||||
]);
|
||||
|
||||
Notification::make()->success()->title('Shout goedgekeurd')->send();
|
||||
}
|
||||
|
||||
public function dismissShoutReport(int $id): void
|
||||
{
|
||||
$shout = RadioShout::findOrFail($id);
|
||||
$shout->update(['reported' => false, 'reported_by' => null]);
|
||||
|
||||
Notification::make()->success()->title('Melding genegeerd')->send();
|
||||
}
|
||||
|
||||
public function deleteShout(int $id): void
|
||||
{
|
||||
RadioShout::findOrFail($id)->delete();
|
||||
|
||||
Notification::make()->success()->title('Shout verwijderd')->send();
|
||||
}
|
||||
|
||||
public function approveRequest(int $id): void
|
||||
{
|
||||
$request = RadioSongRequest::findOrFail($id);
|
||||
$request->approve();
|
||||
|
||||
Notification::make()->success()->title('Verzoek goedgekeurd')->send();
|
||||
}
|
||||
|
||||
public function markRequestPlayed(int $id): void
|
||||
{
|
||||
$request = RadioSongRequest::findOrFail($id);
|
||||
$request->markAsPlayed();
|
||||
|
||||
Notification::make()->success()->title('Verzoek gemarkeerd als gespeeld')->send();
|
||||
}
|
||||
|
||||
public function rejectRequest(int $id): void
|
||||
{
|
||||
$request = RadioSongRequest::findOrFail($id);
|
||||
$request->update(['is_approved' => false]);
|
||||
|
||||
Notification::make()->success()->title('Verzoek afgewezen')->send();
|
||||
}
|
||||
|
||||
public function approveApplication(int $id): void
|
||||
{
|
||||
$application = RadioApplication::findOrFail($id);
|
||||
$application->update([
|
||||
'status' => 'approved',
|
||||
'approved_by' => auth()->id(),
|
||||
'approved_at' => now(),
|
||||
]);
|
||||
|
||||
Notification::make()->success()->title('Aanmelding goedgekeurd')->send();
|
||||
}
|
||||
|
||||
public function rejectApplication(int $id): void
|
||||
{
|
||||
$application = RadioApplication::findOrFail($id);
|
||||
$application->update([
|
||||
'status' => 'rejected',
|
||||
'rejected_by' => auth()->id(),
|
||||
'rejected_at' => now(),
|
||||
]);
|
||||
|
||||
Notification::make()->success()->title('Aanmelding afgewezen')->send();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Pages\Radio;
|
||||
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
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;
|
||||
|
||||
final class EmbedCode extends Page implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
|
||||
#[\Override]
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-code-bracket';
|
||||
|
||||
#[\Override]
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'Radio';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $navigationLabel = 'Embed Code';
|
||||
|
||||
#[\Override]
|
||||
protected static ?string $title = 'Radio Embed Code';
|
||||
|
||||
#[\Override]
|
||||
protected string $view = 'filament.pages.radio.embed-code';
|
||||
|
||||
public array $data = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->form->fill([
|
||||
'embed_theme' => $this->getSetting('radio_embed_theme', 'dark'),
|
||||
'embed_height' => $this->getSetting('radio_embed_height', '400'),
|
||||
'embed_width' => $this->getSetting('radio_embed_width', '100%'),
|
||||
'embed_auto_play' => $this->getSetting('radio_embed_auto_play', '0'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make('Embed Configuratie')
|
||||
->description('Pas het uiterlijk van de embed player aan')
|
||||
->schema([
|
||||
Select::make('embed_theme')
|
||||
->label('Thema')
|
||||
->options([
|
||||
'dark' => 'Donker',
|
||||
'light' => 'Licht',
|
||||
'transparent' => 'Transparant',
|
||||
])
|
||||
->default('dark'),
|
||||
TextInput::make('embed_height')
|
||||
->label('Hoogte (px)')
|
||||
->numeric()
|
||||
->default(400),
|
||||
TextInput::make('embed_width')
|
||||
->label('Breedte')
|
||||
->placeholder('100% of 400px')
|
||||
->default('100%'),
|
||||
Toggle::make('embed_auto_play')
|
||||
->label('Auto-Play'),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getIframeCode(): string
|
||||
{
|
||||
$theme = $this->data['embed_theme'] ?? 'dark';
|
||||
$height = $this->data['embed_height'] ?? '400';
|
||||
$width = $this->data['embed_width'] ?? '100%';
|
||||
$autoPlay = ($this->data['embed_auto_play'] ?? false) ? '&autoplay=1' : '';
|
||||
$url = route('radio.embed', ['theme' => $theme]) . $autoPlay;
|
||||
|
||||
return '<iframe src="' . e($url) . '" width="' . e($width) . '" height="' . e($height) . '" frameborder="0" allow="autoplay" style="border-radius: 12px;"></iframe>';
|
||||
}
|
||||
|
||||
public function getJsSnippet(): string
|
||||
{
|
||||
$theme = $this->data['embed_theme'] ?? 'dark';
|
||||
$autoPlay = ($this->data['embed_auto_play'] ?? false) ? '&autoplay=1' : '';
|
||||
$url = route('radio.embed', ['theme' => $theme]) . $autoPlay;
|
||||
|
||||
return '<div id="radio-embed"></div>
|
||||
<script>
|
||||
(function() {
|
||||
var iframe = document.createElement(\'iframe\');
|
||||
iframe.src = \'' . e($url) . '\';
|
||||
iframe.width = \'100%\';
|
||||
iframe.height = \'400\';
|
||||
iframe.frameBorder = \'0\';
|
||||
iframe.style.borderRadius = \'12px\';
|
||||
iframe.allow = \'autoplay\';
|
||||
document.getElementById(\'radio-embed\').appendChild(iframe);
|
||||
})();
|
||||
</script>';
|
||||
}
|
||||
|
||||
public function getDirectUrl(): string
|
||||
{
|
||||
$theme = $this->data['embed_theme'] ?? 'dark';
|
||||
$autoPlay = ($this->data['embed_auto_play'] ?? false) ? '&autoplay=1' : '';
|
||||
|
||||
return route('radio.embed', ['theme' => $theme]) . $autoPlay;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getSetting(string $key, mixed $default = null): mixed
|
||||
{
|
||||
$setting = WebsiteSetting::where('key', $key)->first();
|
||||
|
||||
return $setting?->value ?? $default;
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,12 @@ final class RadioSettings extends Page implements HasForms
|
||||
'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'),
|
||||
@@ -626,6 +632,32 @@ final class RadioSettings extends Page implements HasForms
|
||||
'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')
|
||||
@@ -890,6 +922,9 @@ final class RadioSettings extends Page implements HasForms
|
||||
$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',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1475,7 +1510,8 @@ final class RadioSettings extends Page implements HasForms
|
||||
'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_applications_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',
|
||||
@@ -1488,6 +1524,7 @@ final class RadioSettings extends Page implements HasForms
|
||||
'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',
|
||||
|
||||
Reference in New Issue
Block a user