Unify feature checks; simplify station routes.

This commit is contained in:
Buster "Silver Eagle" Neece 2022-06-29 23:38:46 -05:00
parent 6529a92e58
commit 6e9af6f1f4
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
15 changed files with 666 additions and 426 deletions

View File

@ -3,6 +3,7 @@
* Administrative dashboard configuration. * Administrative dashboard configuration.
*/ */
use App\Enums\StationFeatures;
use App\Enums\StationPermissions; use App\Enums\StationPermissions;
use App\Radio\Enums\AudioProcessingMethods; use App\Radio\Enums\AudioProcessingMethods;
@ -13,8 +14,6 @@ return function (App\Event\BuildStationMenu $e) {
$backendConfig = $station->getBackendConfig(); $backendConfig = $station->getBackendConfig();
$router = $request->getRouter(); $router = $request->getRouter();
$backendEnum = $station->getBackendTypeEnum();
$frontendEnum = $station->getFrontendTypeEnum(); $frontendEnum = $station->getFrontendTypeEnum();
$willDisconnectMessage = __('Restart broadcasting? This will disconnect any current listeners.'); $willDisconnectMessage = __('Restart broadcasting? This will disconnect any current listeners.');
@ -63,33 +62,30 @@ return function (App\Event\BuildStationMenu $e) {
'media' => [ 'media' => [
'label' => __('Media'), 'label' => __('Media'),
'icon' => 'library_music', 'icon' => 'library_music',
'visible' => StationFeatures::Media->supportedForStation($station),
'items' => [ 'items' => [
'files' => [ 'files' => [
'label' => __('Music Files'), 'label' => __('Music Files'),
'icon' => 'library_music', 'icon' => 'library_music',
'url' => (string)$router->fromHere('stations:files:index'), 'url' => (string)$router->fromHere('stations:files:index'),
'visible' => $backendEnum->isEnabled(),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'reports_duplicates' => [ 'reports_duplicates' => [
'label' => __('Duplicate Songs'), 'label' => __('Duplicate Songs'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:files:index') . '#special:duplicates', 'url' => (string)$router->fromHere('stations:files:index') . '#special:duplicates',
'visible' => $backendEnum->isEnabled(),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'reports_unprocessable' => [ 'reports_unprocessable' => [
'label' => __('Unprocessable Files'), 'label' => __('Unprocessable Files'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:files:index') . '#special:unprocessable', 'url' => (string)$router->fromHere('stations:files:index') . '#special:unprocessable',
'visible' => $backendEnum->isEnabled(),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'reports_unassigned' => [ 'reports_unassigned' => [
'label' => __('Unassigned Files'), 'label' => __('Unassigned Files'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:files:index') . '#special:unassigned', 'url' => (string)$router->fromHere('stations:files:index') . '#special:unassigned',
'visible' => $backendEnum->isEnabled(),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
'ondemand' => [ 'ondemand' => [
@ -111,7 +107,6 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Bulk Media Import/Export'), 'label' => __('Bulk Media Import/Export'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:bulk-media'), 'url' => (string)$router->fromHere('stations:bulk-media'),
'visible' => $backendEnum->isEnabled(),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
], ],
@ -121,7 +116,7 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Playlists'), 'label' => __('Playlists'),
'icon' => 'queue_music', 'icon' => 'queue_music',
'url' => (string)$router->fromHere('stations:playlists:index'), 'url' => (string)$router->fromHere('stations:playlists:index'),
'visible' => $backendEnum->isEnabled(), 'visible' => StationFeatures::Media->supportedForStation($station),
'permission' => StationPermissions::Media, 'permission' => StationPermissions::Media,
], ],
@ -129,18 +124,19 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Podcasts'), 'label' => __('Podcasts'),
'icon' => 'cast', 'icon' => 'cast',
'url' => (string)$router->fromHere('stations:podcasts:index'), 'url' => (string)$router->fromHere('stations:podcasts:index'),
'visible' => StationFeatures::Podcasts->supportedForStation($station),
'permission' => StationPermissions::Podcasts, 'permission' => StationPermissions::Podcasts,
], ],
'live_streaming' => [ 'live_streaming' => [
'label' => __('Live Streaming'), 'label' => __('Live Streaming'),
'icon' => 'mic', 'icon' => 'mic',
'visible' => StationFeatures::Streamers->supportedForStation($station),
'items' => [ 'items' => [
'streamers' => [ 'streamers' => [
'label' => __('Streamer/DJ Accounts'), 'label' => __('Streamer/DJ Accounts'),
'icon' => 'mic', 'icon' => 'mic',
'url' => (string)$router->fromHere('stations:streamers:index'), 'url' => (string)$router->fromHere('stations:streamers:index'),
'visible' => $backendEnum->isEnabled() && $station->getEnableStreamers(),
'permission' => StationPermissions::Streamers, 'permission' => StationPermissions::Streamers,
], ],
@ -154,7 +150,7 @@ return function (App\Event\BuildStationMenu $e) {
true true
) )
->withScheme('https'), ->withScheme('https'),
'visible' => $station->getEnablePublicPage() && $station->getEnableStreamers(), 'visible' => $station->getEnablePublicPage(),
'external' => true, 'external' => true,
], ],
], ],
@ -164,6 +160,7 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Web Hooks'), 'label' => __('Web Hooks'),
'icon' => 'code', 'icon' => 'code',
'url' => (string)$router->fromHere('stations:webhooks:index'), 'url' => (string)$router->fromHere('stations:webhooks:index'),
'visible' => StationFeatures::Webhooks->supportedForStation($station),
'permission' => StationPermissions::WebHooks, 'permission' => StationPermissions::WebHooks,
], ],
@ -204,32 +201,34 @@ return function (App\Event\BuildStationMenu $e) {
'label' => __('Mount Points'), 'label' => __('Mount Points'),
'icon' => 'wifi_tethering', 'icon' => 'wifi_tethering',
'url' => (string)$router->fromHere('stations:mounts:index'), 'url' => (string)$router->fromHere('stations:mounts:index'),
'visible' => $frontendEnum->supportsMounts(), 'visible' => StationFeatures::MountPoints->supportedForStation($station),
'permission' => StationPermissions::MountPoints, 'permission' => StationPermissions::MountPoints,
], ],
'hls_streams' => [ 'hls_streams' => [
'label' => __('HLS Streams'), 'label' => __('HLS Streams'),
'url' => (string)$router->fromHere('stations:hls_streams:index'), 'url' => (string)$router->fromHere('stations:hls_streams:index'),
'visible' => $backendEnum->isEnabled() && $station->getEnableHls(), 'visible' => StationFeatures::HlsStreams->supportedForStation($station),
'permission' => StationPermissions::MountPoints, 'permission' => StationPermissions::MountPoints,
], ],
'remotes' => [ 'remotes' => [
'label' => __('Remote Relays'), 'label' => __('Remote Relays'),
'icon' => 'router', 'icon' => 'router',
'url' => (string)$router->fromHere('stations:remotes:index'), 'url' => (string)$router->fromHere('stations:remotes:index'),
'visible' => StationFeatures::RemoteRelays->supportedForStation($station),
'permission' => StationPermissions::RemoteRelays, 'permission' => StationPermissions::RemoteRelays,
], ],
'fallback' => [ 'fallback' => [
'label' => __('Custom Fallback File'), 'label' => __('Custom Fallback File'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:fallback'), 'url' => (string)$router->fromHere('stations:fallback'),
'visible' => StationFeatures::Media->supportedForStation($station),
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
], ],
'ls_config' => [ 'ls_config' => [
'label' => __('Edit Liquidsoap Configuration'), 'label' => __('Edit Liquidsoap Configuration'),
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:util:ls_config'), 'url' => (string)$router->fromHere('stations:util:ls_config'),
'visible' => $settings->getEnableAdvancedFeatures() && $backendEnum->isEnabled(), 'visible' => StationFeatures::CustomLiquidsoapConfig->supportedForStation($station),
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
], ],
'stations:stereo_tool_config' => [ 'stations:stereo_tool_config' => [
@ -237,7 +236,7 @@ return function (App\Event\BuildStationMenu $e) {
'class' => 'text-muted', 'class' => 'text-muted',
'url' => (string)$router->fromHere('stations:stereo_tool_config'), 'url' => (string)$router->fromHere('stations:stereo_tool_config'),
'visible' => $settings->getEnableAdvancedFeatures() 'visible' => $settings->getEnableAdvancedFeatures()
&& App\Radio\Enums\BackendAdapters::Liquidsoap === $backendEnum && StationFeatures::Media->supportedForStation($station)
&& AudioProcessingMethods::StereoTool === $backendConfig->getAudioProcessingMethodEnum(), && AudioProcessingMethods::StereoTool === $backendConfig->getAudioProcessingMethodEnum(),
'permission' => StationPermissions::Broadcasting, 'permission' => StationPermissions::Broadcasting,
], ],

View File

@ -1,6 +1,7 @@
<?php <?php
use App\Controller; use App\Controller;
use App\Enums\StationFeatures;
use App\Enums\StationPermissions; use App\Enums\StationPermissions;
use App\Middleware; use App\Middleware;
use Slim\Routing\RouteCollectorProxy; use Slim\Routing\RouteCollectorProxy;
@ -87,8 +88,7 @@ return static function (RouteCollectorProxy $group) {
$group->get( $group->get(
'/waveform/{media_id:[a-zA-Z0-9\-]+}.json', '/waveform/{media_id:[a-zA-Z0-9\-]+}.json',
Controller\Api\Stations\Waveform\GetWaveformAction::class Controller\Api\Stations\Waveform\GetWaveformAction::class
) )->setName('api:stations:media:waveform');
->setName('api:stations:media:waveform');
$group->get('/art/{media_id:[a-zA-Z0-9\-]+}.jpg', Controller\Api\Stations\Art\GetArtAction::class) $group->get('/art/{media_id:[a-zA-Z0-9\-]+}.jpg', Controller\Api\Stations\Art\GetArtAction::class)
->setName('api:stations:media:art'); ->setName('api:stations:media:art');
@ -102,42 +102,9 @@ return static function (RouteCollectorProxy $group) {
$group->delete('/art/{media_id:[a-zA-Z0-9]+}', Controller\Api\Stations\Art\DeleteArtAction::class) $group->delete('/art/{media_id:[a-zA-Z0-9]+}', Controller\Api\Stations\Art\DeleteArtAction::class)
->add(new Middleware\Permissions(StationPermissions::Media, true)); ->add(new Middleware\Permissions(StationPermissions::Media, true));
$group->group( /*
'/liquidsoap-config', * Podcast Public Pages
function (RouteCollectorProxy $group) { */
$group->get(
'',
Controller\Api\Stations\LiquidsoapConfig\GetAction::class
)->setName('api:stations:liquidsoap-config');
$group->put(
'',
Controller\Api\Stations\LiquidsoapConfig\PutAction::class
);
}
)->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
$group->group(
'/stereo_tool_config',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\StereoTool\GetStereoToolConfigurationAction::class
)->setName('api:stations:stereo_tool_config');
$group->post(
'',
Controller\Api\Stations\StereoTool\PostStereoToolConfigurationAction::class
);
$group->delete(
'',
Controller\Api\Stations\StereoTool\DeleteStereoToolConfigurationAction::class
);
}
)->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
// Public and private podcast pages
$group->group( $group->group(
'/podcast/{podcast_id}', '/podcast/{podcast_id}',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
@ -176,304 +143,424 @@ return static function (RouteCollectorProxy $group) {
} }
)->add(Middleware\RequirePublishedPodcastEpisodeMiddleware::class); )->add(Middleware\RequirePublishedPodcastEpisodeMiddleware::class);
// Private-only podcast pages /*
* Podcast Private Pates
*/
$group->group( $group->group(
'/podcasts', '',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
$group->get('', Controller\Api\Stations\PodcastsController::class . ':listAction') $group->get('/podcasts', Controller\Api\Stations\PodcastsController::class . ':listAction')
->setName('api:stations:podcasts'); ->setName('api:stations:podcasts');
$group->post('', Controller\Api\Stations\PodcastsController::class . ':createAction'); $group->post('/podcasts', Controller\Api\Stations\PodcastsController::class . ':createAction');
$group->post('/art', Controller\Api\Stations\Podcasts\Art\PostArtAction::class) $group->post('/podcasts/art', Controller\Api\Stations\Podcasts\Art\PostArtAction::class)
->setName('api:stations:podcasts:new-art'); ->setName('api:stations:podcasts:new-art');
}
)->add(new Middleware\Permissions(StationPermissions::Podcasts, true));
$group->group(
'/podcast/{podcast_id}',
function (RouteCollectorProxy $group) {
$group->put('', Controller\Api\Stations\PodcastsController::class . ':editAction');
$group->delete('', Controller\Api\Stations\PodcastsController::class . ':deleteAction');
$group->post(
'/art',
Controller\Api\Stations\Podcasts\Art\PostArtAction::class
)->setName('api:stations:podcast:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Art\DeleteArtAction::class
);
$group->post(
'/episodes',
Controller\Api\Stations\PodcastEpisodesController::class . ':createAction'
);
$group->post(
'/episodes/art',
Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class
)->setName('api:stations:podcast:episodes:new-art');
$group->post(
'/episodes/media',
Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class
)->setName('api:stations:podcast:episodes:new-media');
$group->group( $group->group(
'/episode/{episode_id}', '/podcast/{podcast_id}',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
$group->put( $group->put('', Controller\Api\Stations\PodcastsController::class . ':editAction');
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':editAction'
);
$group->delete( $group->delete('', Controller\Api\Stations\PodcastsController::class . ':deleteAction');
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':deleteAction'
);
$group->post( $group->post(
'/art', '/art',
Controller\Api\Stations\Podcasts\Art\PostArtAction::class
)->setName('api:stations:podcast:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Art\DeleteArtAction::class
);
$group->post(
'/episodes',
Controller\Api\Stations\PodcastEpisodesController::class . ':createAction'
);
$group->post(
'/episodes/art',
Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class
)->setName('api:stations:podcast:episode:art-internal'); )->setName('api:stations:podcast:episodes:new-art');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\DeleteArtAction::class
);
$group->post( $group->post(
'/media', '/episodes/media',
Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class
)->setName('api:stations:podcast:episode:media-internal'); )->setName('api:stations:podcast:episodes:new-media');
$group->delete( $group->group(
'/media', '/episode/{episode_id}',
Controller\Api\Stations\Podcasts\Episodes\Media\DeleteMediaAction::class function (RouteCollectorProxy $group) {
$group->put(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\PodcastEpisodesController::class . ':deleteAction'
);
$group->post(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\PostArtAction::class
)->setName('api:stations:podcast:episode:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Podcasts\Episodes\Art\DeleteArtAction::class
);
$group->post(
'/media',
Controller\Api\Stations\Podcasts\Episodes\Media\PostMediaAction::class
)->setName('api:stations:podcast:episode:media-internal');
$group->delete(
'/media',
Controller\Api\Stations\Podcasts\Episodes\Media\DeleteMediaAction::class
);
}
); );
} }
); );
} }
)->add(new Middleware\Permissions(StationPermissions::Podcasts, true)); )->add(new Middleware\Permissions(StationPermissions::Podcasts, true));
// Streamers public pages /*
$group->get( * Files/Media
'/streamer/{streamer_id}/art', */
Controller\Api\Stations\Streamers\Art\GetArtAction::class
)->setName('api:stations:streamer:art');
// Streamers internal pages
$group->post('/streamers/art', Controller\Api\Stations\Streamers\Art\PostArtAction::class)
->setName('api:stations:streamers:new-art')
->add(new Middleware\Permissions(StationPermissions::Streamers, true));
$group->group( $group->group(
'/streamer/{streamer_id}', '',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
$group->post( $group->group(
'/art', '/files',
Controller\Api\Stations\Streamers\Art\PostArtAction::class function (RouteCollectorProxy $group) {
)->setName('api:stations:streamer:art-internal'); $group->get(
'',
Controller\Api\Stations\FilesController::class . ':listAction'
)->setName('api:stations:files');
$group->delete( $group->post(
'/art', '',
Controller\Api\Stations\Streamers\Art\DeleteArtAction::class Controller\Api\Stations\FilesController::class . ':createAction'
);
$group->get('/list', Controller\Api\Stations\Files\ListAction::class)
->setName('api:stations:files:list');
$group->get('/directories', Controller\Api\Stations\Files\ListDirectoriesAction::class)
->setName('api:stations:files:directories');
$group->put('/rename', Controller\Api\Stations\Files\RenameAction::class)
->setName('api:stations:files:rename');
$group->put('/batch', Controller\Api\Stations\Files\BatchAction::class)
->setName('api:stations:files:batch');
$group->post('/mkdir', Controller\Api\Stations\Files\MakeDirectoryAction::class)
->setName('api:stations:files:mkdir');
$group->get('/bulk', Controller\Api\Stations\BulkMedia\DownloadAction::class)
->setName('api:stations:files:bulk');
$group->post('/bulk', Controller\Api\Stations\BulkMedia\UploadAction::class);
$group->get('/download', Controller\Api\Stations\Files\DownloadAction::class)
->setName('api:stations:files:download');
$group->map(
['GET', 'POST'],
'/upload',
Controller\Api\Stations\Files\FlowUploadAction::class
)->setName('api:stations:files:upload');
}
);
$group->group(
'/file/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\FilesController::class . ':getAction'
)->setName('api:stations:file');
$group->put(
'',
Controller\Api\Stations\FilesController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\FilesController::class . ':deleteAction'
);
$group->get('/play', Controller\Api\Stations\Files\PlayAction::class)
->setName('api:stations:files:play');
}
); );
} }
)->add(new Middleware\Permissions(StationPermissions::Streamers, true)); )->add(new Middleware\StationSupportsFeature(StationFeatures::Media))
$station_api_endpoints = [
[
'file',
'files',
Controller\Api\Stations\FilesController::class,
StationPermissions::Media,
],
[
'hls_stream',
'hls_streams',
Controller\Api\Stations\HlsStreamsController::class,
StationPermissions::MountPoints,
],
[
'mount',
'mounts',
Controller\Api\Stations\MountsController::class,
StationPermissions::MountPoints,
],
[
'playlist',
'playlists',
Controller\Api\Stations\PlaylistsController::class,
StationPermissions::Media,
],
[
'remote',
'remotes',
Controller\Api\Stations\RemotesController::class,
StationPermissions::RemoteRelays,
],
[
'sftp-user',
'sftp-users',
Controller\Api\Stations\SftpUsersController::class,
StationPermissions::Media,
],
[
'streamer',
'streamers',
Controller\Api\Stations\StreamersController::class,
StationPermissions::Streamers,
],
[
'webhook',
'webhooks',
Controller\Api\Stations\WebhooksController::class,
StationPermissions::WebHooks,
],
];
foreach ($station_api_endpoints as [$singular, $plural, $class, $permission]) {
$group->group(
'',
function (RouteCollectorProxy $group) use ($singular, $plural, $class) {
$group->get('/' . $plural, $class . ':listAction')
->setName('api:stations:' . $plural);
$group->post('/' . $plural, $class . ':createAction');
$group->get('/' . $singular . '/{id}', $class . ':getAction')
->setName('api:stations:' . $singular);
$group->put('/' . $singular . '/{id}', $class . ':editAction');
$group->delete('/' . $singular . '/{id}', $class . ':deleteAction');
}
)->add(new Middleware\Permissions($permission, true));
}
$group->group(
'/files',
function (RouteCollectorProxy $group) {
$group->get('/list', Controller\Api\Stations\Files\ListAction::class)
->setName('api:stations:files:list');
$group->get('/directories', Controller\Api\Stations\Files\ListDirectoriesAction::class)
->setName('api:stations:files:directories');
$group->put('/rename', Controller\Api\Stations\Files\RenameAction::class)
->setName('api:stations:files:rename');
$group->put('/batch', Controller\Api\Stations\Files\BatchAction::class)
->setName('api:stations:files:batch');
$group->post('/mkdir', Controller\Api\Stations\Files\MakeDirectoryAction::class)
->setName('api:stations:files:mkdir');
$group->get('/play/{id}', Controller\Api\Stations\Files\PlayAction::class)
->setName('api:stations:files:play');
$group->get('/download', Controller\Api\Stations\Files\DownloadAction::class)
->setName('api:stations:files:download');
$group->get('/bulk', Controller\Api\Stations\BulkMedia\DownloadAction::class)
->setName('api:stations:files:bulk');
$group->post('/bulk', Controller\Api\Stations\BulkMedia\UploadAction::class);
$group->map(
['GET', 'POST'],
'/upload',
Controller\Api\Stations\Files\FlowUploadAction::class
)->setName('api:stations:files:upload');
}
)
->add(Middleware\Module\StationFiles::class)
->add(new Middleware\Permissions(StationPermissions::Media, true)); ->add(new Middleware\Permissions(StationPermissions::Media, true));
$group->post( /*
'/mounts/intro', * SFTP Users
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class */
)->setName('api:stations:mounts:new-intro') $group->group(
'',
function (RouteCollectorProxy $group) {
$group->get(
'/sftp-users',
Controller\Api\Stations\SftpUsersController::class . ':listAction'
)->setName('api:stations:sftp-users');
$group->post(
'/sftp-users',
Controller\Api\Stations\SftpUsersController::class . ':createAction'
);
$group->get(
'/sftp-user/{id}',
Controller\Api\Stations\SftpUsersController::class . ':getAction'
)->setName('api:stations:sftp-user');
$group->put(
'/sftp-user/{id}',
Controller\Api\Stations\SftpUsersController::class . ':editAction'
);
$group->delete(
'/sftp-user/{id}',
Controller\Api\Stations\SftpUsersController::class . ':deleteAction'
);
}
)->add(new Middleware\StationSupportsFeature(StationFeatures::Sftp))
->add(new Middleware\Permissions(StationPermissions::Media, true));
/*
* Mount Points
*/
$group->group(
'',
function (RouteCollectorProxy $group) {
$group->group(
'/mounts',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\MountsController::class . ':listAction'
)->setName('api:stations:mounts');
$group->post(
'',
Controller\Api\Stations\MountsController::class . ':createAction'
);
$group->post(
'/intro',
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class
)->setName('api:stations:mounts:new-intro');
}
);
$group->group(
'/mount/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\MountsController::class . ':getAction'
)->setName('api:stations:mount');
$group->put(
'',
Controller\Api\Stations\MountsController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\MountsController::class . ':deleteAction'
);
$group->get(
'/intro',
Controller\Api\Stations\Mounts\Intro\GetIntroAction::class
)->setName('api:stations:mounts:intro');
$group->post(
'/intro',
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class
);
$group->delete(
'/intro',
Controller\Api\Stations\Mounts\Intro\DeleteIntroAction::class
);
}
);
}
)->add(new Middleware\StationSupportsFeature(StationFeatures::MountPoints))
->add(new Middleware\Permissions(StationPermissions::MountPoints, true)); ->add(new Middleware\Permissions(StationPermissions::MountPoints, true));
/*
* Remote Relays
*/
$group->group( $group->group(
'/mount/{id}', '',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
$group->get( $group->get(
'/intro', '/remotes',
Controller\Api\Stations\Mounts\Intro\GetIntroAction::class Controller\Api\Stations\RemotesController::class . ':listAction'
)->setName('api:stations:mounts:intro'); )->setName('api:stations:remotes');
$group->post( $group->post(
'/intro', '/remotes',
Controller\Api\Stations\Mounts\Intro\PostIntroAction::class Controller\Api\Stations\RemotesController::class . ':createAction'
);
$group->get(
'/remote/{id}',
Controller\Api\Stations\RemotesController::class . ':getAction'
)->setName('api:stations:remote');
$group->put(
'/remote/{id}',
Controller\Api\Stations\RemotesController::class . ':editAction'
); );
$group->delete( $group->delete(
'/intro', '/remote/{id}',
Controller\Api\Stations\Mounts\Intro\DeleteIntroAction::class Controller\Api\Stations\RemotesController::class . ':deleteAction'
); );
} }
)->add(new Middleware\Permissions(StationPermissions::MountPoints, true)); )->add(new Middleware\StationSupportsFeature(StationFeatures::RemoteRelays))
->add(new Middleware\Permissions(StationPermissions::RemoteRelays, true));
$group->get( /*
'/playlists/schedule', * HLS Streams
Controller\Api\Stations\PlaylistsController::class . ':scheduleAction' */
) $group->group(
->setName('api:stations:playlists:schedule') '',
function (RouteCollectorProxy $group) {
$group->get(
'/hls_streams',
Controller\Api\Stations\HlsStreamsController::class . ':listAction'
)->setName('api:stations:hls_streams');
$group->post(
'/hls_streams',
Controller\Api\Stations\HlsStreamsController::class . ':createAction'
);
$group->get(
'/hls_stream/{id}',
Controller\Api\Stations\HlsStreamsController::class . ':getAction'
)->setName('api:stations:hls_stream');
$group->put(
'/hls_stream/{id}',
Controller\Api\Stations\HlsStreamsController::class . ':editAction'
);
$group->delete(
'/hls_stream/{id}',
Controller\Api\Stations\HlsStreamsController::class . ':deleteAction'
);
}
)->add(new Middleware\StationSupportsFeature(StationFeatures::HlsStreams))
->add(new Middleware\Permissions(StationPermissions::MountPoints, true));
/*
* Playlist
*/
$group->group(
'',
function (RouteCollectorProxy $group) {
$group->get(
'/playlists',
Controller\Api\Stations\PlaylistsController::class . ':listAction'
)->setName('api:stations:playlists');
$group->post(
'/playlists',
Controller\Api\Stations\PlaylistsController::class . ':createAction'
);
$group->get(
'/playlists/schedule',
Controller\Api\Stations\PlaylistsController::class . ':scheduleAction'
)->setName('api:stations:playlists:schedule');
$group->group(
'/playlist/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\PlaylistsController::class . ':getAction'
)->setName('api:stations:playlist');
$group->put(
'',
Controller\Api\Stations\PlaylistsController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\PlaylistsController::class . ':deleteAction'
);
$group->put(
'/toggle',
Controller\Api\Stations\Playlists\ToggleAction::class
)->setName('api:stations:playlist:toggle');
$group->put(
'/reshuffle',
Controller\Api\Stations\Playlists\ReshuffleAction::class
)->setName('api:stations:playlist:reshuffle');
$group->get(
'/order',
Controller\Api\Stations\Playlists\GetOrderAction::class
)->setName('api:stations:playlist:order');
$group->put(
'/order',
Controller\Api\Stations\Playlists\PutOrderAction::class
);
$group->get(
'/queue',
Controller\Api\Stations\Playlists\GetQueueAction::class
)->setName('api:stations:playlist:queue');
$group->delete(
'/queue',
Controller\Api\Stations\Playlists\DeleteQueueAction::class
);
$group->post(
'/clone',
Controller\Api\Stations\Playlists\CloneAction::class
)->setName('api:stations:playlist:clone');
$group->post(
'/import',
Controller\Api\Stations\Playlists\ImportAction::class
)->setName('api:stations:playlist:import');
$group->get(
'/export[/{format}]',
Controller\Api\Stations\Playlists\ExportAction::class
)->setName('api:stations:playlist:export');
}
);
}
)->add(new Middleware\StationSupportsFeature(StationFeatures::Media))
->add(new Middleware\Permissions(StationPermissions::Media, true)); ->add(new Middleware\Permissions(StationPermissions::Media, true));
$group->group( /*
'/playlist/{id}', * Reports
function (RouteCollectorProxy $group) { */
$group->put(
'/toggle',
Controller\Api\Stations\Playlists\ToggleAction::class
)->setName('api:stations:playlist:toggle');
$group->put(
'/reshuffle',
Controller\Api\Stations\Playlists\ReshuffleAction::class
)->setName('api:stations:playlist:reshuffle');
$group->get(
'/order',
Controller\Api\Stations\Playlists\GetOrderAction::class
)->setName('api:stations:playlist:order');
$group->put(
'/order',
Controller\Api\Stations\Playlists\PutOrderAction::class
);
$group->get(
'/queue',
Controller\Api\Stations\Playlists\GetQueueAction::class
)->setName('api:stations:playlist:queue');
$group->delete(
'/queue',
Controller\Api\Stations\Playlists\DeleteQueueAction::class
);
$group->post(
'/clone',
Controller\Api\Stations\Playlists\CloneAction::class
)->setName('api:stations:playlist:clone');
$group->post(
'/import',
Controller\Api\Stations\Playlists\ImportAction::class
)->setName('api:stations:playlist:import');
$group->get(
'/export[/{format}]',
Controller\Api\Stations\Playlists\ExportAction::class
)->setName('api:stations:playlist:export');
}
)->add(new Middleware\Permissions(StationPermissions::Media, true));
$group->group( $group->group(
'/reports', '/reports',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
@ -539,40 +626,94 @@ return static function (RouteCollectorProxy $group) {
} }
)->add(new Middleware\Permissions(StationPermissions::Reports, true)); )->add(new Middleware\Permissions(StationPermissions::Reports, true));
/*
* Streamers Extras
*/
$group->get( $group->get(
'/streamers/schedule', '/streamer/{id}/art',
Controller\Api\Stations\StreamersController::class . ':scheduleAction' Controller\Api\Stations\Streamers\Art\GetArtAction::class
) )->setName('api:stations:streamer:art');
->setName('api:stations:streamers:schedule')
->add(new Middleware\Permissions(StationPermissions::Streamers, true));
$group->get(
'/streamers/broadcasts',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction'
)
->setName('api:stations:streamers:broadcasts')
->add(new Middleware\Permissions(StationPermissions::Streamers, true));
$group->group( $group->group(
'/streamer/{streamer_id}', '',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
$group->get( $group->group(
'/broadcasts', '/streamers',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction' function (RouteCollectorProxy $group) {
)->setName('api:stations:streamer:broadcasts'); $group->get(
'',
Controller\Api\Stations\StreamersController::class . ':listAction'
)->setName('api:stations:streamers');
$group->get( $group->post(
'/broadcast/{broadcast_id}/download', '',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':downloadAction' Controller\Api\Stations\StreamersController::class . ':createAction'
)->setName('api:stations:streamer:broadcast:download'); );
$group->delete( $group->get(
'/broadcast/{broadcast_id}', '/schedule',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':deleteAction' Controller\Api\Stations\StreamersController::class . ':scheduleAction'
) )->setName('api:stations:streamers:schedule');
->setName('api:stations:streamer:broadcast:delete');
$group->get(
'/broadcasts',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction'
)->setName('api:stations:streamers:broadcasts');
$group->post(
'/art',
Controller\Api\Stations\Streamers\Art\PostArtAction::class
)->setName('api:stations:streamers:new-art');
}
);
$group->group(
'/streamer/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\StreamersController::class . ':getAction'
)->setName('api:stations:streamer');
$group->put(
'',
Controller\Api\Stations\StreamersController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\StreamersController::class . ':deleteAction'
);
$group->get(
'/broadcasts',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':listAction'
)->setName('api:stations:streamer:broadcasts');
$group->get(
'/broadcast/{broadcast_id}/download',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':downloadAction'
)->setName('api:stations:streamer:broadcast:download');
$group->delete(
'/broadcast/{broadcast_id}',
Controller\Api\Stations\Streamers\BroadcastsController::class . ':deleteAction'
)->setName('api:stations:streamer:broadcast:delete');
$group->post(
'/art',
Controller\Api\Stations\Streamers\Art\PostArtAction::class
)->setName('api:stations:streamer:art-internal');
$group->delete(
'/art',
Controller\Api\Stations\Streamers\Art\DeleteArtAction::class
);
}
);
} }
)->add(new Middleware\Permissions(StationPermissions::Streamers, true)); )->add(new Middleware\StationSupportsFeature(StationFeatures::Streamers))
->add(new Middleware\Permissions(StationPermissions::Streamers, true));
$group->get('/restart-status', Controller\Api\Stations\GetRestartStatusAction::class) $group->get('/restart-status', Controller\Api\Stations\GetRestartStatusAction::class)
->setName('api:stations:restart-status') ->setName('api:stations:restart-status')
@ -589,8 +730,7 @@ return static function (RouteCollectorProxy $group) {
$group->post( $group->post(
'/frontend/{do}', '/frontend/{do}',
Controller\Api\Stations\ServicesController::class . ':frontendAction' Controller\Api\Stations\ServicesController::class . ':frontendAction'
) )->setName('api:stations:frontend')
->setName('api:stations:frontend')
->add(new Middleware\Permissions(StationPermissions::Broadcasting, true)); ->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
$group->post('/reload', Controller\Api\Stations\ServicesController::class . ':reloadAction') $group->post('/reload', Controller\Api\Stations\ServicesController::class . ':reloadAction')
@ -601,6 +741,9 @@ return static function (RouteCollectorProxy $group) {
->setName('api:stations:restart') ->setName('api:stations:restart')
->add(new Middleware\Permissions(StationPermissions::Broadcasting, true)); ->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
/*
* Custom Fallback File
*/
$group->group( $group->group(
'/fallback', '/fallback',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
@ -621,26 +764,104 @@ return static function (RouteCollectorProxy $group) {
} }
)->add(new Middleware\Permissions(StationPermissions::Broadcasting, true)); )->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
/*
* Webhook Extras
*/
$group->group( $group->group(
'/webhook/{id}', '',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {
$group->put(
'/toggle',
Controller\Api\Stations\Webhooks\ToggleAction::class
)->setName('api:stations:webhook:toggle');
$group->put(
'/test',
Controller\Api\Stations\Webhooks\TestAction::class
)->setName('api:stations:webhook:test');
$group->get( $group->get(
'/test-log/{path}', '/webhooks',
Controller\Api\Stations\Webhooks\TestLogAction::class Controller\Api\Stations\WebhooksController::class . ':listAction'
)->setName('api:stations:webhook:test-log'); )->setName('api:stations:webhooks');
}
)->add(new Middleware\Permissions(StationPermissions::WebHooks, true));
$group->post(
'/webhooks',
Controller\Api\Stations\WebhooksController::class . ':createAction'
);
$group->group(
'/webhook/{id}',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\WebhooksController::class . ':getAction'
)->setName('api:stations:webhook');
$group->put(
'',
Controller\Api\Stations\WebhooksController::class . ':editAction'
);
$group->delete(
'',
Controller\Api\Stations\WebhooksController::class . ':deleteAction'
);
$group->put(
'/toggle',
Controller\Api\Stations\Webhooks\ToggleAction::class
)->setName('api:stations:webhook:toggle');
$group->put(
'/test',
Controller\Api\Stations\Webhooks\TestAction::class
)->setName('api:stations:webhook:test');
$group->get(
'/test-log/{path}',
Controller\Api\Stations\Webhooks\TestLogAction::class
)->setName('api:stations:webhook:test-log');
}
);
}
)->add(new Middleware\StationSupportsFeature(StationFeatures::Webhooks))
->add(new Middleware\Permissions(StationPermissions::WebHooks, true));
/*
* Custom Liquidsoap Configuration
*/
$group->group(
'/liquidsoap-config',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\LiquidsoapConfig\GetAction::class
)->setName('api:stations:liquidsoap-config');
$group->put(
'',
Controller\Api\Stations\LiquidsoapConfig\PutAction::class
);
}
)->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
/*
* StereoTool Configuration
*/
$group->group(
'/stereo_tool_config',
function (RouteCollectorProxy $group) {
$group->get(
'',
Controller\Api\Stations\StereoTool\GetStereoToolConfigurationAction::class
)->setName('api:stations:stereo_tool_config');
$group->post(
'',
Controller\Api\Stations\StereoTool\PostStereoToolConfigurationAction::class
);
$group->delete(
'',
Controller\Api\Stations\StereoTool\DeleteStereoToolConfigurationAction::class
);
}
)->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
/*
* Logs
*/
$group->group( $group->group(
'', '',
function (RouteCollectorProxy $group) { function (RouteCollectorProxy $group) {

View File

@ -1,6 +1,7 @@
<?php <?php
use App\Controller; use App\Controller;
use App\Enums\StationFeatures;
use App\Enums\StationPermissions; use App\Enums\StationPermissions;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
@ -30,14 +31,17 @@ return static function (RouteCollectorProxy $app) {
$group->get('/files', Controller\Stations\FilesAction::class) $group->get('/files', Controller\Stations\FilesAction::class)
->setName('stations:files:index') ->setName('stations:files:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::Media))
->add(new Middleware\Permissions(StationPermissions::Media, true)); ->add(new Middleware\Permissions(StationPermissions::Media, true));
$group->get('/hls_streams', Controller\Stations\HlsStreamsAction::class) $group->get('/hls_streams', Controller\Stations\HlsStreamsAction::class)
->setName('stations:hls_streams:index') ->setName('stations:hls_streams:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::HlsStreams))
->add(new Middleware\Permissions(StationPermissions::MountPoints, true)); ->add(new Middleware\Permissions(StationPermissions::MountPoints, true));
$group->get('/ls_config', Controller\Stations\EditLiquidsoapConfigAction::class) $group->get('/ls_config', Controller\Stations\EditLiquidsoapConfigAction::class)
->setName('stations:util:ls_config') ->setName('stations:util:ls_config')
->add(new Middleware\StationSupportsFeature(StationFeatures::CustomLiquidsoapConfig))
->add(new Middleware\Permissions(StationPermissions::Broadcasting, true)); ->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
$group->get('/stereo_tool_config', Controller\Stations\UploadStereoToolConfigAction::class) $group->get('/stereo_tool_config', Controller\Stations\UploadStereoToolConfigAction::class)
@ -50,14 +54,17 @@ return static function (RouteCollectorProxy $app) {
$group->get('/playlists', Controller\Stations\PlaylistsAction::class) $group->get('/playlists', Controller\Stations\PlaylistsAction::class)
->setName('stations:playlists:index') ->setName('stations:playlists:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::Media))
->add(new Middleware\Permissions(StationPermissions::Media, true)); ->add(new Middleware\Permissions(StationPermissions::Media, true));
$group->get('/podcasts', Controller\Stations\PodcastsAction::class) $group->get('/podcasts', Controller\Stations\PodcastsAction::class)
->setName('stations:podcasts:index') ->setName('stations:podcasts:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::Podcasts))
->add(new Middleware\Permissions(StationPermissions::Podcasts, true)); ->add(new Middleware\Permissions(StationPermissions::Podcasts, true));
$group->get('/mounts', Controller\Stations\MountsAction::class) $group->get('/mounts', Controller\Stations\MountsAction::class)
->setName('stations:mounts:index') ->setName('stations:mounts:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::MountPoints))
->add(new Middleware\Permissions(StationPermissions::MountPoints, true)); ->add(new Middleware\Permissions(StationPermissions::MountPoints, true));
$group->get('/profile', Controller\Stations\ProfileController::class) $group->get('/profile', Controller\Stations\ProfileController::class)
@ -76,10 +83,12 @@ return static function (RouteCollectorProxy $app) {
$group->get('/queue', Controller\Stations\QueueAction::class) $group->get('/queue', Controller\Stations\QueueAction::class)
->setName('stations:queue:index') ->setName('stations:queue:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::Media))
->add(new Middleware\Permissions(StationPermissions::Broadcasting, true)); ->add(new Middleware\Permissions(StationPermissions::Broadcasting, true));
$group->get('/remotes', Controller\Stations\RemotesAction::class) $group->get('/remotes', Controller\Stations\RemotesAction::class)
->setName('stations:remotes:index') ->setName('stations:remotes:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::RemoteRelays))
->add(new Middleware\Permissions(StationPermissions::RemoteRelays, true)); ->add(new Middleware\Permissions(StationPermissions::RemoteRelays, true));
$group->group( $group->group(
@ -108,14 +117,17 @@ return static function (RouteCollectorProxy $app) {
$group->get('/sftp_users', Controller\Stations\SftpUsersAction::class) $group->get('/sftp_users', Controller\Stations\SftpUsersAction::class)
->setName('stations:sftp_users:index') ->setName('stations:sftp_users:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::Sftp))
->add(new Middleware\Permissions(StationPermissions::Media, true)); ->add(new Middleware\Permissions(StationPermissions::Media, true));
$group->get('/streamers', Controller\Stations\StreamersAction::class) $group->get('/streamers', Controller\Stations\StreamersAction::class)
->setName('stations:streamers:index') ->setName('stations:streamers:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::Streamers))
->add(new Middleware\Permissions(StationPermissions::Streamers, true)); ->add(new Middleware\Permissions(StationPermissions::Streamers, true));
$group->get('/webhooks', Controller\Stations\WebhooksAction::class) $group->get('/webhooks', Controller\Stations\WebhooksAction::class)
->setName('stations:webhooks:index') ->setName('stations:webhooks:index')
->add(new Middleware\StationSupportsFeature(StationFeatures::Webhooks))
->add(new Middleware\Permissions(StationPermissions::WebHooks, true)); ->add(new Middleware\Permissions(StationPermissions::WebHooks, true));
} }
) )

View File

@ -5,8 +5,6 @@ declare(strict_types=1);
namespace App\Controller\Api\Stations; namespace App\Controller\Api\Stations;
use App\Entity; use App\Entity;
use App\Exception\StationUnsupportedException;
use App\Http\ServerRequest;
use App\OpenApi; use App\OpenApi;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
@ -137,18 +135,4 @@ final class HlsStreamsController extends AbstractStationApiCrudController
{ {
protected string $entityClass = Entity\StationHlsStream::class; protected string $entityClass = Entity\StationHlsStream::class;
protected string $resourceRouteName = 'api:stations:hls_stream'; protected string $resourceRouteName = 'api:stations:hls_stream';
/**
* @inheritDoc
*/
protected function getStation(ServerRequest $request): Entity\Station
{
$station = parent::getStation($request);
if (!$station->getBackendTypeEnum()->isEnabled()) {
throw new StationUnsupportedException();
}
return $station;
}
} }

View File

@ -7,7 +7,6 @@ namespace App\Controller\Api\Stations;
use App\Controller\Api\Traits\CanSortResults; use App\Controller\Api\Traits\CanSortResults;
use App\Doctrine\ReloadableEntityManagerInterface; use App\Doctrine\ReloadableEntityManagerInterface;
use App\Entity; use App\Entity;
use App\Exception\StationUnsupportedException;
use App\Http\Response; use App\Http\Response;
use App\Http\Router; use App\Http\Router;
use App\Http\ServerRequest; use App\Http\ServerRequest;
@ -256,18 +255,4 @@ final class MountsController extends AbstractStationApiCrudController
return $response->withJson(Entity\Api\Status::deleted()); return $response->withJson(Entity\Api\Status::deleted());
} }
/**
* @inheritDoc
*/
protected function getStation(ServerRequest $request): Entity\Station
{
$station = parent::getStation($request);
if (!$station->getFrontendTypeEnum()->supportsMounts()) {
throw new StationUnsupportedException();
}
return $station;
}
} }

View File

@ -21,11 +21,11 @@ final class DeleteArtAction
ServerRequest $request, ServerRequest $request,
Response $response, Response $response,
string $station_id, string $station_id,
string $streamer_id string $id
): ResponseInterface { ): ResponseInterface {
$station = $request->getStation(); $station = $request->getStation();
$streamer = $this->streamerRepo->requireForStation($streamer_id, $station); $streamer = $this->streamerRepo->requireForStation($id, $station);
$this->streamerRepo->removeArtwork($streamer); $this->streamerRepo->removeArtwork($streamer);
$this->streamerRepo->getEntityManager() $this->streamerRepo->getEntityManager()

View File

@ -21,14 +21,14 @@ final class GetArtAction
ServerRequest $request, ServerRequest $request,
Response $response, Response $response,
string $station_id, string $station_id,
string $streamer_id string $id
): ResponseInterface { ): ResponseInterface {
// If a timestamp delimiter is added, strip it automatically. // If a timestamp delimiter is added, strip it automatically.
$streamer_id = explode('|', $streamer_id, 2)[0]; $id = explode('|', $id, 2)[0];
$station = $request->getStation(); $station = $request->getStation();
$artworkPath = Entity\StationStreamer::getArtworkPath($streamer_id); $artworkPath = Entity\StationStreamer::getArtworkPath($id);
$fsConfig = (new StationFilesystems($station))->getConfigFilesystem(); $fsConfig = (new StationFilesystems($station))->getConfigFilesystem();
if ($fsConfig->fileExists($artworkPath)) { if ($fsConfig->fileExists($artworkPath)) {

View File

@ -21,7 +21,7 @@ final class PostArtAction
ServerRequest $request, ServerRequest $request,
Response $response, Response $response,
string $station_id, string $station_id,
?string $streamer_id = null ?string $id = null
): ResponseInterface { ): ResponseInterface {
$station = $request->getStation(); $station = $request->getStation();
@ -30,8 +30,8 @@ final class PostArtAction
return $flowResponse; return $flowResponse;
} }
if (null !== $streamer_id) { if (null !== $id) {
$streamer = $this->streamerRepo->requireForStation($streamer_id, $station); $streamer = $this->streamerRepo->requireForStation($id, $station);
$this->streamerRepo->writeArtwork( $this->streamerRepo->writeArtwork(
$streamer, $streamer,

View File

@ -25,12 +25,12 @@ final class BroadcastsController extends AbstractApiCrudController
ServerRequest $request, ServerRequest $request,
Response $response, Response $response,
string $station_id, string $station_id,
?string $streamer_id = null ?string $id = null
): ResponseInterface { ): ResponseInterface {
$station = $request->getStation(); $station = $request->getStation();
if (null !== $streamer_id) { if (null !== $id) {
$streamer = $this->getStreamer($station, $streamer_id); $streamer = $this->getStreamer($station, $id);
if (null === $streamer) { if (null === $streamer) {
return $response->withStatus(404) return $response->withStatus(404)
@ -66,13 +66,13 @@ final class BroadcastsController extends AbstractApiCrudController
$fsRecordings = (new StationFilesystems($station))->getRecordingsFilesystem(); $fsRecordings = (new StationFilesystems($station))->getRecordingsFilesystem();
$paginator->setPostprocessor( $paginator->setPostprocessor(
function ($row) use ($streamer_id, $is_bootgrid, $router, $fsRecordings) { function ($row) use ($id, $is_bootgrid, $router, $fsRecordings) {
$return = $this->toArray($row); $return = $this->toArray($row);
unset($return['recordingPath']); unset($return['recordingPath']);
$recordingPath = $row->getRecordingPath(); $recordingPath = $row->getRecordingPath();
if (null === $streamer_id) { if (null === $id) {
$streamer = $row->getStreamer(); $streamer = $row->getStreamer();
$return['streamer'] = [ $return['streamer'] = [
'id' => $streamer->getId(), 'id' => $streamer->getId(),
@ -85,7 +85,7 @@ final class BroadcastsController extends AbstractApiCrudController
$routeParams = [ $routeParams = [
'broadcast_id' => $row->getId(), 'broadcast_id' => $row->getId(),
]; ];
if (null === $streamer_id) { if (null === $id) {
$routeParams['id'] = $row->getStreamer()->getId(); $routeParams['id'] = $row->getStreamer()->getId();
} }
@ -126,7 +126,7 @@ final class BroadcastsController extends AbstractApiCrudController
ServerRequest $request, ServerRequest $request,
Response $response, Response $response,
string $station_id, string $station_id,
string $streamer_id, string $id,
string $broadcast_id string $broadcast_id
): ResponseInterface { ): ResponseInterface {
$station = $request->getStation(); $station = $request->getStation();
@ -159,7 +159,7 @@ final class BroadcastsController extends AbstractApiCrudController
ServerRequest $request, ServerRequest $request,
Response $response, Response $response,
string $station_id, string $station_id,
string $streamer_id, string $id,
string $broadcast_id string $broadcast_id
): ResponseInterface { ): ResponseInterface {
$station = $request->getStation(); $station = $request->getStation();

View File

@ -7,7 +7,6 @@ namespace App\Controller\Api\Stations;
use App\Controller\Api\Traits\CanSortResults; use App\Controller\Api\Traits\CanSortResults;
use App\Doctrine\ReloadableEntityManagerInterface; use App\Doctrine\ReloadableEntityManagerInterface;
use App\Entity; use App\Entity;
use App\Exception\StationUnsupportedException;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\OpenApi; use App\OpenApi;
@ -286,38 +285,24 @@ final class StreamersController extends AbstractScheduledEntityController
$return['has_custom_art'] = (0 !== $record->getArtUpdatedAt()); $return['has_custom_art'] = (0 !== $record->getArtUpdatedAt());
$return['art'] = (string)$router->fromHere( $return['art'] = (string)$router->fromHere(
route_name: 'api:stations:streamer:art', route_name: 'api:stations:streamer:art',
route_params: ['streamer_id' => $record->getIdRequired() . '|' . $record->getArtUpdatedAt()], route_params: ['id' => $record->getIdRequired() . '|' . $record->getArtUpdatedAt()],
absolute: !$isInternal absolute: !$isInternal
); );
$return['links']['broadcasts'] = (string)$router->fromHere( $return['links']['broadcasts'] = (string)$router->fromHere(
route_name: 'api:stations:streamer:broadcasts', route_name: 'api:stations:streamer:broadcasts',
route_params: ['streamer_id' => $record->getId()], route_params: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
); );
$return['links']['art'] = (string)$router->fromHere( $return['links']['art'] = (string)$router->fromHere(
route_name: 'api:stations:streamer:art-internal', route_name: 'api:stations:streamer:art-internal',
route_params: ['streamer_id' => $record->getId()], route_params: ['id' => $record->getId()],
absolute: !$isInternal absolute: !$isInternal
); );
return $return; return $return;
} }
/**
* @inheritDoc
*/
protected function getStation(ServerRequest $request): Entity\Station
{
$station = parent::getStation($request);
if (!$station->getBackendTypeEnum()->isEnabled()) {
throw new StationUnsupportedException();
}
return $station;
}
protected function deleteRecord(object $record): void protected function deleteRecord(object $record): void
{ {
if (!($record instanceof Entity\StationStreamer)) { if (!($record instanceof Entity\StationStreamer)) {

View File

@ -6,7 +6,6 @@ namespace App\Controller\Stations;
use App\Entity\StationBackendConfiguration; use App\Entity\StationBackendConfiguration;
use App\Event\Radio\WriteLiquidsoapConfiguration; use App\Event\Radio\WriteLiquidsoapConfiguration;
use App\Exception\StationUnsupportedException;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\Radio\Backend\Liquidsoap; use App\Radio\Backend\Liquidsoap;
@ -27,10 +26,6 @@ final class EditLiquidsoapConfigAction
): ResponseInterface { ): ResponseInterface {
$station = $request->getStation(); $station = $request->getStation();
if (!$station->getBackendTypeEnum()->isEnabled()) {
throw new StationUnsupportedException();
}
$configSections = StationBackendConfiguration::getCustomConfigurationSections(); $configSections = StationBackendConfiguration::getCustomConfigurationSections();
$tokens = Liquidsoap\ConfigWriter::getDividerString(); $tokens = Liquidsoap\ConfigWriter::getDividerString();

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Controller\Stations; namespace App\Controller\Stations;
use App\Exception\StationUnsupportedException;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
@ -16,12 +15,6 @@ final class HlsStreamsAction
Response $response, Response $response,
string $station_id string $station_id
): ResponseInterface { ): ResponseInterface {
$station = $request->getStation();
if (!$station->getBackendTypeEnum()->isEnabled() || !$station->getEnableHls()) {
throw new StationUnsupportedException();
}
$router = $request->getRouter(); $router = $request->getRouter();
return $request->getView()->renderVuePage( return $request->getView()->renderVuePage(
response: $response, response: $response,

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Enums;
use App\Entity\Station;
enum StationFeatures
{
case CustomLiquidsoapConfig;
case Media;
case Sftp;
case MountPoints;
case RemoteRelays;
case HlsStreams;
case Streamers;
case Webhooks;
case Podcasts;
public function supportedForStation(Station $station): bool
{
$backendEnabled = $station->getBackendTypeEnum()->isEnabled();
return match ($this) {
self::Media, self::RemoteRelays, self::CustomLiquidsoapConfig => $backendEnabled,
self::Streamers => $backendEnabled && $station->getEnableStreamers(),
self::Sftp => $backendEnabled && $station->getMediaStorageLocation()->isLocal(),
self::MountPoints => $station->getFrontendTypeEnum()->supportsMounts(),
self::HlsStreams => $backendEnabled && $station->getEnableHls(),
self::Webhooks, self::Podcasts => true,
};
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace App\Middleware;
use App\Enums\StationFeatures;
use App\Exception;
use App\Http\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\RequestHandlerInterface;
final class StationSupportsFeature
{
public function __construct(
private readonly StationFeatures $feature
) {
}
public function __invoke(ServerRequest $request, RequestHandlerInterface $handler): ResponseInterface
{
if (!$this->feature->supportedForStation($request->getStation())) {
throw new Exception\StationUnsupportedException();
}
return $handler->handle($request);
}
}

View File

@ -14,6 +14,10 @@ class Api_Stations_StreamersCest extends CestAbstract
// Create new record // Create new record
$station = $this->getTestStation(); $station = $this->getTestStation();
$station->setEnableStreamers(true);
$this->em->persist($station);
$this->em->flush();
$listUrl = '/api/station/' . $station->getId() . '/streamers'; $listUrl = '/api/station/' . $station->getId() . '/streamers';
$I->sendPOST( $I->sendPOST(