Adapter simplification:
- Remove the stub "None"/"Remote" adapters; return null for disabled adapters - Add capability checks to adapter enums - Remove the Remote proxy
This commit is contained in:
parent
80400dc28f
commit
ad3d2b2c69
|
@ -13,13 +13,14 @@ return function (App\Event\BuildStationMenu $e) {
|
||||||
$backendConfig = $station->getBackendConfig();
|
$backendConfig = $station->getBackendConfig();
|
||||||
|
|
||||||
$router = $request->getRouter();
|
$router = $request->getRouter();
|
||||||
$backend = $request->getStationBackend();
|
|
||||||
$frontend = $request->getStationFrontend();
|
$backendEnum = $station->getBackendTypeEnum();
|
||||||
|
$frontendEnum = $station->getFrontendTypeEnum();
|
||||||
|
|
||||||
$willDisconnectMessage = __('Restart broadcasting? This will disconnect any current listeners.');
|
$willDisconnectMessage = __('Restart broadcasting? This will disconnect any current listeners.');
|
||||||
$willNotDisconnectMessage = __('Reload broadcasting? Current listeners will not be disconnected.');
|
$willNotDisconnectMessage = __('Reload broadcasting? Current listeners will not be disconnected.');
|
||||||
|
|
||||||
$reloadSupported = $frontend->supportsReload();
|
$reloadSupported = $frontendEnum->supportsReload();
|
||||||
$reloadMessage = $reloadSupported ? $willNotDisconnectMessage : $willDisconnectMessage;
|
$reloadMessage = $reloadSupported ? $willNotDisconnectMessage : $willDisconnectMessage;
|
||||||
|
|
||||||
$settings = $e->getSettings();
|
$settings = $e->getSettings();
|
||||||
|
@ -67,28 +68,28 @@ return function (App\Event\BuildStationMenu $e) {
|
||||||
'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' => $backend->supportsMedia(),
|
'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' => $backend->supportsMedia(),
|
'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' => $backend->supportsMedia(),
|
'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' => $backend->supportsMedia(),
|
'visible' => $backendEnum->isEnabled(),
|
||||||
'permission' => StationPermissions::Media,
|
'permission' => StationPermissions::Media,
|
||||||
],
|
],
|
||||||
'ondemand' => [
|
'ondemand' => [
|
||||||
|
@ -110,7 +111,7 @@ 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' => $backend->supportsMedia(),
|
'visible' => $backendEnum->isEnabled(),
|
||||||
'permission' => StationPermissions::Media,
|
'permission' => StationPermissions::Media,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -120,7 +121,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' => $backend->supportsMedia(),
|
'visible' => $backendEnum->isEnabled(),
|
||||||
'permission' => StationPermissions::Media,
|
'permission' => StationPermissions::Media,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@ return function (App\Event\BuildStationMenu $e) {
|
||||||
'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' => $backend->supportsStreamers() && $station->getEnableStreamers(),
|
'visible' => $backendEnum->isEnabled() && $station->getEnableStreamers(),
|
||||||
'permission' => StationPermissions::Streamers,
|
'permission' => StationPermissions::Streamers,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -203,13 +204,13 @@ 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' => $frontend->supportsMounts(),
|
'visible' => $frontendEnum->supportsMounts(),
|
||||||
'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' => $backend->supportsHls() && $station->getEnableHls(),
|
'visible' => $backendEnum->isEnabled() && $station->getEnableHls(),
|
||||||
'permission' => StationPermissions::MountPoints,
|
'permission' => StationPermissions::MountPoints,
|
||||||
],
|
],
|
||||||
'remotes' => [
|
'remotes' => [
|
||||||
|
@ -228,8 +229,7 @@ return function (App\Event\BuildStationMenu $e) {
|
||||||
'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()
|
'visible' => $settings->getEnableAdvancedFeatures() && $backendEnum->isEnabled(),
|
||||||
&& $backend instanceof App\Radio\Backend\Liquidsoap,
|
|
||||||
'permission' => StationPermissions::Broadcasting,
|
'permission' => StationPermissions::Broadcasting,
|
||||||
],
|
],
|
||||||
'stations:stereo_tool_config' => [
|
'stations:stereo_tool_config' => [
|
||||||
|
@ -237,7 +237,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()
|
||||||
&& $backend instanceof App\Radio\Backend\Liquidsoap
|
&& App\Radio\Enums\BackendAdapters::Liquidsoap === $backendEnum
|
||||||
&& AudioProcessingMethods::StereoTool === $backendConfig->getAudioProcessingMethodEnum(),
|
&& AudioProcessingMethods::StereoTool === $backendConfig->getAudioProcessingMethodEnum(),
|
||||||
'permission' => StationPermissions::Broadcasting,
|
'permission' => StationPermissions::Broadcasting,
|
||||||
],
|
],
|
||||||
|
|
|
@ -35,12 +35,14 @@ class OnSslRenewal extends CommandAbstract
|
||||||
|
|
||||||
foreach ($stations as $station) {
|
foreach ($stations as $station) {
|
||||||
/** @var Entity\Station $station */
|
/** @var Entity\Station $station */
|
||||||
|
if ($station->getFrontendTypeEnum()->supportsReload()) {
|
||||||
$frontend = $this->adapters->getFrontendAdapter($station);
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
if ($frontend->supportsReload()) {
|
if (null !== $frontend) {
|
||||||
$frontend->write($station);
|
$frontend->write($station);
|
||||||
$frontend->reload($station);
|
$frontend->reload($station);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller\Admin\Debug;
|
namespace App\Controller\Admin\Debug;
|
||||||
|
|
||||||
|
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\Adapters;
|
||||||
use Monolog\Handler\TestHandler;
|
use Monolog\Handler\TestHandler;
|
||||||
use Monolog\Level;
|
use Monolog\Level;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
|
@ -15,7 +16,8 @@ use Psr\Http\Message\ResponseInterface;
|
||||||
final class TelnetAction
|
final class TelnetAction
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Logger $logger
|
private readonly Logger $logger,
|
||||||
|
private readonly Adapters $adapters
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +30,12 @@ final class TelnetAction
|
||||||
$this->logger->pushHandler($testHandler);
|
$this->logger->pushHandler($testHandler);
|
||||||
|
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$backend = $request->getStationBackend();
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
|
|
||||||
|
if (null === $backend) {
|
||||||
|
throw new StationUnsupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
if ($backend instanceof Liquidsoap) {
|
|
||||||
$command = $request->getParam('command');
|
$command = $request->getParam('command');
|
||||||
|
|
||||||
$telnetResponse = $backend->command($station, $command);
|
$telnetResponse = $backend->command($station, $command);
|
||||||
|
@ -40,7 +45,6 @@ final class TelnetAction
|
||||||
'response' => $telnetResponse,
|
'response' => $telnetResponse,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
$this->logger->popHandler();
|
$this->logger->popHandler();
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,6 @@ final class RelaysController
|
||||||
|
|
||||||
$return = [];
|
$return = [];
|
||||||
foreach ($stations as $station) {
|
foreach ($stations as $station) {
|
||||||
$fa = $this->adapters->getFrontendAdapter($station);
|
|
||||||
|
|
||||||
$row = new Entity\Api\Admin\Relay();
|
$row = new Entity\Api\Admin\Relay();
|
||||||
$row->id = $station->getIdRequired();
|
$row->id = $station->getIdRequired();
|
||||||
$row->name = $station->getName();
|
$row->name = $station->getName();
|
||||||
|
@ -70,7 +68,9 @@ final class RelaysController
|
||||||
$row->admin_pw = $frontend_config->getAdminPassword();
|
$row->admin_pw = $frontend_config->getAdminPassword();
|
||||||
|
|
||||||
$mounts = [];
|
$mounts = [];
|
||||||
if ($station->getMounts()->count() > 0) {
|
|
||||||
|
$fa = $this->adapters->getFrontendAdapter($station);
|
||||||
|
if (null !== $fa && $station->getMounts()->count() > 0) {
|
||||||
foreach ($station->getMounts() as $mount) {
|
foreach ($station->getMounts() as $mount) {
|
||||||
/** @var Entity\StationMount $mount */
|
/** @var Entity\StationMount $mount */
|
||||||
$mounts[] = $mount->api($fa);
|
$mounts[] = $mount->api($fa);
|
||||||
|
|
|
@ -10,7 +10,6 @@ use App\Entity;
|
||||||
use App\Environment;
|
use App\Environment;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\Radio\Adapters;
|
|
||||||
use App\Radio\Configuration;
|
use App\Radio\Configuration;
|
||||||
use DeepCopy;
|
use DeepCopy;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
@ -36,7 +35,6 @@ final class CloneAction extends StationsController
|
||||||
Entity\Repository\StationRepository $stationRepo,
|
Entity\Repository\StationRepository $stationRepo,
|
||||||
Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
||||||
Entity\Repository\StationQueueRepository $queueRepo,
|
Entity\Repository\StationQueueRepository $queueRepo,
|
||||||
Adapters $adapters,
|
|
||||||
Configuration $configuration,
|
Configuration $configuration,
|
||||||
ReloadableEntityManagerInterface $reloadableEm,
|
ReloadableEntityManagerInterface $reloadableEm,
|
||||||
Serializer $serializer,
|
Serializer $serializer,
|
||||||
|
@ -47,7 +45,6 @@ final class CloneAction extends StationsController
|
||||||
$stationRepo,
|
$stationRepo,
|
||||||
$storageLocationRepo,
|
$storageLocationRepo,
|
||||||
$queueRepo,
|
$queueRepo,
|
||||||
$adapters,
|
|
||||||
$configuration,
|
$configuration,
|
||||||
$reloadableEm,
|
$reloadableEm,
|
||||||
$serializer,
|
$serializer,
|
||||||
|
@ -164,10 +161,7 @@ final class CloneAction extends StationsController
|
||||||
$this->cloneCollection($record->getMounts(), $newStation, $copier);
|
$this->cloneCollection($record->getMounts(), $newStation, $copier);
|
||||||
} else {
|
} else {
|
||||||
$newStation = $this->reloadableEm->refetch($newStation);
|
$newStation = $this->reloadableEm->refetch($newStation);
|
||||||
|
$this->stationRepo->resetMounts($newStation);
|
||||||
// Create default mountpoints if station supports them.
|
|
||||||
$frontendAdapter = $this->adapters->getFrontendAdapter($newStation);
|
|
||||||
$this->stationRepo->resetMounts($newStation, $frontendAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array(self::CLONE_REMOTES, $toClone, true)) {
|
if (in_array(self::CLONE_REMOTES, $toClone, true)) {
|
||||||
|
|
|
@ -11,7 +11,6 @@ use App\Exception\ValidationException;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\OpenApi;
|
use App\OpenApi;
|
||||||
use App\Radio\Adapters;
|
|
||||||
use App\Radio\Configuration;
|
use App\Radio\Configuration;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
@ -147,7 +146,6 @@ class StationsController extends AbstractAdminApiCrudController
|
||||||
protected Entity\Repository\StationRepository $stationRepo,
|
protected Entity\Repository\StationRepository $stationRepo,
|
||||||
protected Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
protected Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
||||||
protected Entity\Repository\StationQueueRepository $queueRepo,
|
protected Entity\Repository\StationQueueRepository $queueRepo,
|
||||||
protected Adapters $adapters,
|
|
||||||
protected Configuration $configuration,
|
protected Configuration $configuration,
|
||||||
protected ReloadableEntityManagerInterface $reloadableEm,
|
protected ReloadableEntityManagerInterface $reloadableEm,
|
||||||
Serializer $serializer,
|
Serializer $serializer,
|
||||||
|
@ -323,13 +321,11 @@ class StationsController extends AbstractAdminApiCrudController
|
||||||
$hls_changed = $old_hls !== $station->getEnableHls();
|
$hls_changed = $old_hls !== $station->getEnableHls();
|
||||||
|
|
||||||
if ($frontend_changed) {
|
if ($frontend_changed) {
|
||||||
$frontend = $this->adapters->getFrontendAdapter($station);
|
$this->stationRepo->resetMounts($station);
|
||||||
$this->stationRepo->resetMounts($station, $frontend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($hls_changed || $backend_changed) {
|
if ($hls_changed || $backend_changed) {
|
||||||
$backend = $this->adapters->getBackendAdapter($station);
|
$this->stationRepo->resetHls($station);
|
||||||
$this->stationRepo->resetHls($station, $backend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($adapter_changed || !$station->getIsEnabled()) {
|
if ($adapter_changed || !$station->getIsEnabled()) {
|
||||||
|
@ -355,8 +351,7 @@ class StationsController extends AbstractAdminApiCrudController
|
||||||
$this->configuration->initializeConfiguration($station);
|
$this->configuration->initializeConfiguration($station);
|
||||||
|
|
||||||
// Create default mountpoints if station supports them.
|
// Create default mountpoints if station supports them.
|
||||||
$frontend_adapter = $this->adapters->getFrontendAdapter($station);
|
$this->stationRepo->resetMounts($station);
|
||||||
$this->stationRepo->resetMounts($station, $frontend_adapter);
|
|
||||||
|
|
||||||
return $station;
|
return $station;
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ final class BatchAction
|
||||||
$fs
|
$fs
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->writePlaylistChanges($request, $affectedPlaylists);
|
$this->writePlaylistChanges($station, $affectedPlaylists);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ final class BatchAction
|
||||||
|
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
|
|
||||||
$this->writePlaylistChanges($request, $affectedPlaylists);
|
$this->writePlaylistChanges($station, $affectedPlaylists);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
@ -440,13 +440,11 @@ final class BatchAction
|
||||||
}
|
}
|
||||||
|
|
||||||
private function writePlaylistChanges(
|
private function writePlaylistChanges(
|
||||||
ServerRequest $request,
|
Entity\Station $station,
|
||||||
array $playlists
|
array $playlists
|
||||||
): void {
|
): void {
|
||||||
// Write new PLS playlist configuration.
|
// Write new PLS playlist configuration.
|
||||||
$backend = $request->getStationBackend();
|
if ($station->getBackendTypeEnum()->isEnabled()) {
|
||||||
|
|
||||||
if ($backend instanceof Liquidsoap) {
|
|
||||||
foreach ($playlists as $playlistId => $playlistRow) {
|
foreach ($playlists as $playlistId => $playlistRow) {
|
||||||
// Instruct the message queue to start a new "write playlist to file" task.
|
// Instruct the message queue to start a new "write playlist to file" task.
|
||||||
$message = new Message\WritePlaylistFileMessage();
|
$message = new Message\WritePlaylistFileMessage();
|
||||||
|
|
|
@ -145,8 +145,7 @@ final class HlsStreamsController extends AbstractStationApiCrudController
|
||||||
{
|
{
|
||||||
$station = parent::getStation($request);
|
$station = parent::getStation($request);
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
if (!$station->getBackendTypeEnum()->isEnabled()) {
|
||||||
if (!$backend->supportsHls()) {
|
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use App\Http\Response;
|
||||||
use App\Http\Router;
|
use App\Http\Router;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\OpenApi;
|
use App\OpenApi;
|
||||||
|
use App\Radio\Adapters;
|
||||||
use App\Service\Flow\UploadedFile;
|
use App\Service\Flow\UploadedFile;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
@ -152,7 +153,8 @@ final class MountsController extends AbstractStationApiCrudController
|
||||||
ReloadableEntityManagerInterface $em,
|
ReloadableEntityManagerInterface $em,
|
||||||
Serializer $serializer,
|
Serializer $serializer,
|
||||||
ValidatorInterface $validator,
|
ValidatorInterface $validator,
|
||||||
private readonly Entity\Repository\StationMountRepository $mountRepo
|
private readonly Entity\Repository\StationMountRepository $mountRepo,
|
||||||
|
private readonly Adapters $adapters,
|
||||||
) {
|
) {
|
||||||
parent::__construct($em, $serializer, $validator);
|
parent::__construct($em, $serializer, $validator);
|
||||||
}
|
}
|
||||||
|
@ -195,20 +197,23 @@ final class MountsController extends AbstractStationApiCrudController
|
||||||
$return = parent::viewRecord($record, $request);
|
$return = parent::viewRecord($record, $request);
|
||||||
|
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$frontend = $request->getStationFrontend();
|
|
||||||
$router = $request->getRouter();
|
$router = $request->getRouter();
|
||||||
|
|
||||||
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
|
|
||||||
$return['links']['intro'] = (string)$router->fromHere(
|
$return['links']['intro'] = (string)$router->fromHere(
|
||||||
route_name: 'api:stations:mounts:intro',
|
route_name: 'api:stations:mounts:intro',
|
||||||
route_params: ['id' => $record->getId()],
|
route_params: ['id' => $record->getId()],
|
||||||
absolute: true
|
absolute: true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (null !== $frontend) {
|
||||||
$return['links']['listen'] = (string)Router::resolveUri(
|
$return['links']['listen'] = (string)Router::resolveUri(
|
||||||
$router->getBaseUrl(),
|
$router->getBaseUrl(),
|
||||||
$frontend->getUrlForMount($station, $record),
|
$frontend->getUrlForMount($station, $record),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
@ -259,8 +264,7 @@ final class MountsController extends AbstractStationApiCrudController
|
||||||
{
|
{
|
||||||
$station = parent::getStation($request);
|
$station = parent::getStation($request);
|
||||||
|
|
||||||
$frontend = $request->getStationFrontend();
|
if (!$station->getFrontendTypeEnum()->supportsMounts()) {
|
||||||
if (!$frontend->supportsMounts()) {
|
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ namespace App\Controller\Api\Stations;
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
|
use App\Radio\Adapters;
|
||||||
use GuzzleHttp\Psr7\Uri;
|
use GuzzleHttp\Psr7\Uri;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
@ -15,7 +16,8 @@ final class ProfileAction
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Entity\Repository\StationScheduleRepository $scheduleRepo,
|
private readonly Entity\Repository\StationScheduleRepository $scheduleRepo,
|
||||||
private readonly Entity\ApiGenerator\NowPlayingApiGenerator $nowPlayingApiGenerator,
|
private readonly Entity\ApiGenerator\NowPlayingApiGenerator $nowPlayingApiGenerator,
|
||||||
private readonly Entity\ApiGenerator\StationApiGenerator $stationApiGenerator
|
private readonly Entity\ApiGenerator\StationApiGenerator $stationApiGenerator,
|
||||||
|
private readonly Adapters $adapters,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +27,8 @@ final class ProfileAction
|
||||||
string $station_id
|
string $station_id
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$backend = $request->getStationBackend();
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
$frontend = $request->getStationFrontend();
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
|
|
||||||
$baseUri = new Uri('');
|
$baseUri = new Uri('');
|
||||||
$nowPlayingApi = $this->nowPlayingApiGenerator->currentOrEmpty($station, $baseUri);
|
$nowPlayingApi = $this->nowPlayingApiGenerator->currentOrEmpty($station, $baseUri);
|
||||||
|
@ -38,8 +40,8 @@ final class ProfileAction
|
||||||
$apiResponse->cache = 'database';
|
$apiResponse->cache = 'database';
|
||||||
|
|
||||||
$apiResponse->services = new Entity\Api\StationServiceStatus(
|
$apiResponse->services = new Entity\Api\StationServiceStatus(
|
||||||
$backend->isRunning($station),
|
null !== $backend && $backend->isRunning($station),
|
||||||
$frontend->isRunning($station),
|
null !== $frontend && $frontend->isRunning($station),
|
||||||
$station->getHasStarted(),
|
$station->getHasStarted(),
|
||||||
$station->getNeedsRestart()
|
$station->getNeedsRestart()
|
||||||
);
|
);
|
||||||
|
|
|
@ -81,8 +81,7 @@ final class RequestsController
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
|
|
||||||
// Verify that the station supports requests.
|
// Verify that the station supports requests.
|
||||||
$ba = $request->getStationBackend();
|
if (!$station->getBackendTypeEnum()->isEnabled() || !$station->getEnableRequests()) {
|
||||||
if (!$ba->supportsRequests() || !$station->getEnableRequests()) {
|
|
||||||
return $response->withStatus(403)
|
return $response->withStatus(403)
|
||||||
->withJson(new Entity\Api\Error(403, __('This station does not accept requests currently.')));
|
->withJson(new Entity\Api\Error(403, __('This station does not accept requests currently.')));
|
||||||
}
|
}
|
||||||
|
@ -190,8 +189,7 @@ final class RequestsController
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
|
|
||||||
// Verify that the station supports requests.
|
// Verify that the station supports requests.
|
||||||
$ba = $request->getStationBackend();
|
if (!$station->getBackendTypeEnum()->isEnabled() || !$station->getEnableRequests()) {
|
||||||
if (!$ba->supportsRequests() || !$station->getEnableRequests()) {
|
|
||||||
return $response->withStatus(403)
|
return $response->withStatus(403)
|
||||||
->withJson(new Entity\Api\Error(403, __('This station does not accept requests currently.')));
|
->withJson(new Entity\Api\Error(403, __('This station does not accept requests currently.')));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@ 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\Exception\Supervisor\NotRunningException;
|
use App\Exception\Supervisor\NotRunningException;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\Nginx\Nginx;
|
use App\Nginx\Nginx;
|
||||||
use App\OpenApi;
|
use App\OpenApi;
|
||||||
use App\Radio\Backend\Liquidsoap;
|
use App\Radio\Adapters;
|
||||||
use App\Radio\Configuration;
|
use App\Radio\Configuration;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
|
@ -109,6 +110,7 @@ final class ServicesController
|
||||||
private readonly EntityManagerInterface $em,
|
private readonly EntityManagerInterface $em,
|
||||||
private readonly Configuration $configuration,
|
private readonly Configuration $configuration,
|
||||||
private readonly Nginx $nginx,
|
private readonly Nginx $nginx,
|
||||||
|
private readonly Adapters $adapters,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,13 +121,13 @@ final class ServicesController
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
$frontend = $request->getStationFrontend();
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
|
|
||||||
return $response->withJson(
|
return $response->withJson(
|
||||||
new Entity\Api\StationServiceStatus(
|
new Entity\Api\StationServiceStatus(
|
||||||
$backend->isRunning($station),
|
null !== $backend && $backend->isRunning($station),
|
||||||
$frontend->isRunning($station),
|
null !== $frontend && $frontend->isRunning($station),
|
||||||
$station->getHasStarted(),
|
$station->getHasStarted(),
|
||||||
$station->getNeedsRestart()
|
$station->getNeedsRestart()
|
||||||
)
|
)
|
||||||
|
@ -202,7 +204,11 @@ final class ServicesController
|
||||||
string $do = 'restart'
|
string $do = 'restart'
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$frontend = $request->getStationFrontend();
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
|
|
||||||
|
if (null === $frontend) {
|
||||||
|
throw new StationUnsupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
switch ($do) {
|
switch ($do) {
|
||||||
case 'stop':
|
case 'stop':
|
||||||
|
@ -242,20 +248,20 @@ final class ServicesController
|
||||||
string $do = 'restart'
|
string $do = 'restart'
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$backend = $request->getStationBackend();
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
|
|
||||||
|
if (null === $backend) {
|
||||||
|
throw new StationUnsupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
switch ($do) {
|
switch ($do) {
|
||||||
case 'skip':
|
case 'skip':
|
||||||
if ($backend instanceof Liquidsoap) {
|
|
||||||
$backend->skip($station);
|
$backend->skip($station);
|
||||||
}
|
|
||||||
|
|
||||||
return $response->withJson(new Entity\Api\Status(true, __('Song skipped.')));
|
return $response->withJson(new Entity\Api\Status(true, __('Song skipped.')));
|
||||||
|
|
||||||
case 'disconnect':
|
case 'disconnect':
|
||||||
if ($backend instanceof Liquidsoap) {
|
|
||||||
$backend->disconnectStreamer($station);
|
$backend->disconnectStreamer($station);
|
||||||
}
|
|
||||||
|
|
||||||
return $response->withJson(new Entity\Api\Status(true, __('Streamer disconnected.')));
|
return $response->withJson(new Entity\Api\Status(true, __('Streamer disconnected.')));
|
||||||
|
|
||||||
|
|
|
@ -311,8 +311,7 @@ final class StreamersController extends AbstractScheduledEntityController
|
||||||
{
|
{
|
||||||
$station = parent::getStation($request);
|
$station = parent::getStation($request);
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
if (!$station->getBackendTypeEnum()->isEnabled()) {
|
||||||
if (!$backend->supportsStreamers()) {
|
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,25 +5,32 @@ 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\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
|
use App\Radio\Adapters;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
use const ARRAY_FILTER_USE_KEY;
|
use const ARRAY_FILTER_USE_KEY;
|
||||||
|
|
||||||
final class UpdateMetadataAction
|
final class UpdateMetadataAction
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly Adapters $adapters,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function __invoke(
|
public function __invoke(
|
||||||
ServerRequest $request,
|
ServerRequest $request,
|
||||||
Response $response,
|
Response $response,
|
||||||
string $station_id
|
string $station_id
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$backend = $request->getStationBackend();
|
|
||||||
|
|
||||||
if (!method_exists($backend, 'updateMetadata')) {
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
return $response->withStatus(500)
|
|
||||||
->withJson(new Entity\Api\Error(500, 'This function is not supported on this station.'));
|
if (null === $backend) {
|
||||||
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$allowedMetaFields = [
|
$allowedMetaFields = [
|
||||||
|
|
|
@ -7,10 +7,16 @@ namespace App\Controller\Frontend\PublicPages;
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
|
use App\Radio\Adapters;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
final class PlaylistAction
|
final class PlaylistAction
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly Adapters $adapters,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function __invoke(
|
public function __invoke(
|
||||||
ServerRequest $request,
|
ServerRequest $request,
|
||||||
Response $response,
|
Response $response,
|
||||||
|
@ -22,7 +28,8 @@ final class PlaylistAction
|
||||||
$streams = [];
|
$streams = [];
|
||||||
$stream_urls = [];
|
$stream_urls = [];
|
||||||
|
|
||||||
$fa = $request->getStationFrontend();
|
$fa = $this->adapters->getFrontendAdapter($station);
|
||||||
|
if (null !== $fa) {
|
||||||
foreach ($station->getMounts() as $mount) {
|
foreach ($station->getMounts() as $mount) {
|
||||||
/** @var Entity\StationMount $mount */
|
/** @var Entity\StationMount $mount */
|
||||||
if (!$mount->getIsVisibleOnPublicPages()) {
|
if (!$mount->getIsVisibleOnPublicPages()) {
|
||||||
|
@ -37,16 +44,15 @@ final class PlaylistAction
|
||||||
'url' => $stream_url,
|
'url' => $stream_url,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($request->getStationRemotes() as $remote_proxy) {
|
foreach ($station->getRemotes() as $remote) {
|
||||||
$adapter = $remote_proxy->getAdapter();
|
|
||||||
$remote = $remote_proxy->getRemote();
|
|
||||||
|
|
||||||
if (!$remote->getIsVisibleOnPublicPages()) {
|
if (!$remote->getIsVisibleOnPublicPages()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stream_url = $adapter->getPublicUrl($remote);
|
$stream_url = $this->adapters->getRemoteAdapter($station, $remote)
|
||||||
|
->getPublicUrl($remote);
|
||||||
|
|
||||||
$stream_urls[] = $stream_url;
|
$stream_urls[] = $stream_url;
|
||||||
$streams[] = [
|
$streams[] = [
|
||||||
|
|
|
@ -9,13 +9,14 @@ use App\Exception\StationNotFoundException;
|
||||||
use App\Exception\StationUnsupportedException;
|
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\Adapters;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
final class WebDjAction
|
final class WebDjAction
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Assets $assets
|
private readonly Assets $assets,
|
||||||
|
private readonly Adapters $adapters,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +35,8 @@ final class WebDjAction
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
|
if (null === $backend) {
|
||||||
if (!($backend instanceof Liquidsoap)) {
|
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,7 @@ final class EditLiquidsoapConfigAction
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
if (!$station->getBackendTypeEnum()->isEnabled()) {
|
||||||
if (!($backend instanceof Liquidsoap)) {
|
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ final class FilesAction
|
||||||
|
|
||||||
$router = $request->getRouter();
|
$router = $request->getRouter();
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
$backendEnum = $station->getBackendTypeEnum();
|
||||||
|
|
||||||
return $request->getView()->renderVuePage(
|
return $request->getView()->renderVuePage(
|
||||||
response: $response,
|
response: $response,
|
||||||
|
@ -63,7 +63,7 @@ final class FilesAction
|
||||||
'stationTimeZone' => $station->getTimezone(),
|
'stationTimeZone' => $station->getTimezone(),
|
||||||
'showSftp' => SftpGo::isSupportedForStation($station),
|
'showSftp' => SftpGo::isSupportedForStation($station),
|
||||||
'sftpUrl' => (string)$router->fromHere('stations:sftp_users:index'),
|
'sftpUrl' => (string)$router->fromHere('stations:sftp_users:index'),
|
||||||
'supportsImmediateQueue' => $backend->supportsImmediateQueue(),
|
'supportsImmediateQueue' => $backendEnum->isEnabled(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,8 @@ final class HlsStreamsAction
|
||||||
string $station_id
|
string $station_id
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$backend = $request->getStationBackend();
|
|
||||||
|
|
||||||
if (!$backend->supportsHls() || !$station->getEnableHls()) {
|
if (!$station->getBackendTypeEnum()->isEnabled() || !$station->getEnableHls()) {
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,7 @@ final class PlaylistsAction
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
if (!$station->getBackendTypeEnum()->isEnabled()) {
|
||||||
if (!$backend->supportsMedia()) {
|
|
||||||
throw new Exception(__('This feature is not currently supported on this station.'));
|
throw new Exception(__('This feature is not currently supported on this station.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ namespace App\Controller\Stations;
|
||||||
use App\Enums\StationPermissions;
|
use App\Enums\StationPermissions;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
|
use App\Radio\Adapters;
|
||||||
use App\VueComponent\StationFormComponent;
|
use App\VueComponent\StationFormComponent;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
@ -18,6 +19,7 @@ final class ProfileController
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly EntityManagerInterface $em,
|
private readonly EntityManagerInterface $em,
|
||||||
private readonly StationFormComponent $stationFormComponent,
|
private readonly StationFormComponent $stationFormComponent,
|
||||||
|
private readonly Adapters $adapters,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +59,9 @@ final class ProfileController
|
||||||
|
|
||||||
$csrf = $request->getCsrf()->generate(self::CSRF_NAMESPACE);
|
$csrf = $request->getCsrf()->generate(self::CSRF_NAMESPACE);
|
||||||
|
|
||||||
$backend = $request->getStationBackend();
|
$backendEnum = $station->getBackendTypeEnum();
|
||||||
$frontend = $request->getStationFrontend();
|
|
||||||
|
|
||||||
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
$frontendConfig = $station->getFrontendConfig();
|
$frontendConfig = $station->getFrontendConfig();
|
||||||
|
|
||||||
$acl = $request->getAcl();
|
$acl = $request->getAcl();
|
||||||
|
@ -75,8 +77,8 @@ final class ProfileController
|
||||||
'backendType' => $station->getBackendType(),
|
'backendType' => $station->getBackendType(),
|
||||||
'frontendType' => $station->getFrontendType(),
|
'frontendType' => $station->getFrontendType(),
|
||||||
'stationTimeZone' => $station->getTimezone(),
|
'stationTimeZone' => $station->getTimezone(),
|
||||||
'stationSupportsRequests' => $backend->supportsRequests(),
|
'stationSupportsRequests' => $backendEnum->isEnabled(),
|
||||||
'stationSupportsStreamers' => $backend->supportsStreamers(),
|
'stationSupportsStreamers' => $backendEnum->isEnabled(),
|
||||||
'enableRequests' => $station->getEnableRequests(),
|
'enableRequests' => $station->getEnableRequests(),
|
||||||
'enableStreamers' => $station->getEnableStreamers(),
|
'enableStreamers' => $station->getEnableStreamers(),
|
||||||
'enablePublicPage' => $station->getEnablePublicPage(),
|
'enablePublicPage' => $station->getEnablePublicPage(),
|
||||||
|
@ -175,7 +177,7 @@ final class ProfileController
|
||||||
),
|
),
|
||||||
|
|
||||||
// Frontend
|
// Frontend
|
||||||
'frontendAdminUri' => (string)$frontend->getAdminUrl($station, $router->getBaseUrl()),
|
'frontendAdminUri' => (string)$frontend?->getAdminUrl($station, $router->getBaseUrl()),
|
||||||
'frontendAdminPassword' => $frontendConfig->getAdminPassword(),
|
'frontendAdminPassword' => $frontendConfig->getAdminPassword(),
|
||||||
'frontendSourcePassword' => $frontendConfig->getSourcePassword(),
|
'frontendSourcePassword' => $frontendConfig->getSourcePassword(),
|
||||||
'frontendRelayPassword' => $frontendConfig->getRelayPassword(),
|
'frontendRelayPassword' => $frontendConfig->getRelayPassword(),
|
||||||
|
|
|
@ -8,6 +8,7 @@ use App\Entity;
|
||||||
use App\Exception\StationUnsupportedException;
|
use App\Exception\StationUnsupportedException;
|
||||||
use App\Http\Response;
|
use App\Http\Response;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
|
use App\Radio\Adapters;
|
||||||
use App\Service\AzuraCastCentral;
|
use App\Service\AzuraCastCentral;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
@ -15,7 +16,8 @@ final class StreamersAction
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AzuraCastCentral $acCentral,
|
private readonly AzuraCastCentral $acCentral,
|
||||||
private readonly Entity\Repository\SettingsRepository $settingsRepo
|
private readonly Entity\Repository\SettingsRepository $settingsRepo,
|
||||||
|
private readonly Adapters $adapters,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +27,9 @@ final class StreamersAction
|
||||||
string $station_id
|
string $station_id
|
||||||
): ResponseInterface {
|
): ResponseInterface {
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$backend = $request->getStationBackend();
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
|
|
||||||
if (!$backend->supportsStreamers() && !$station->getEnableStreamers()) {
|
if (null === $backend || !$station->getEnableStreamers()) {
|
||||||
throw new StationUnsupportedException();
|
throw new StationUnsupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Station implements ResolvableUrlInterface
|
||||||
description: 'The full URL to listen to the default mount of the station',
|
description: 'The full URL to listen to the default mount of the station',
|
||||||
example: 'http://localhost:8000/radio.mp3'
|
example: 'http://localhost:8000/radio.mp3'
|
||||||
)]
|
)]
|
||||||
public string|UriInterface $listen_url;
|
public string|UriInterface|null $listen_url;
|
||||||
|
|
||||||
#[OA\Property(
|
#[OA\Property(
|
||||||
description: 'The public URL of the station.',
|
description: 'The public URL of the station.',
|
||||||
|
@ -115,7 +115,9 @@ class Station implements ResolvableUrlInterface
|
||||||
*/
|
*/
|
||||||
public function resolveUrls(UriInterface $base): void
|
public function resolveUrls(UriInterface $base): void
|
||||||
{
|
{
|
||||||
$this->listen_url = (string)Router::resolveUri($base, $this->listen_url, true);
|
$this->listen_url = (null !== $this->listen_url)
|
||||||
|
? (string)Router::resolveUri($base, $this->listen_url, true)
|
||||||
|
: null;
|
||||||
|
|
||||||
$this->public_player_url = (string)Router::resolveUri($base, $this->public_player_url, true);
|
$this->public_player_url = (string)Router::resolveUri($base, $this->public_player_url, true);
|
||||||
$this->playlist_pls_url = (string)Router::resolveUri($base, $this->playlist_pls_url, true);
|
$this->playlist_pls_url = (string)Router::resolveUri($base, $this->playlist_pls_url, true);
|
||||||
|
|
|
@ -22,9 +22,8 @@ class StationApiGenerator
|
||||||
?UriInterface $baseUri = null,
|
?UriInterface $baseUri = null,
|
||||||
bool $showAllMounts = false
|
bool $showAllMounts = false
|
||||||
): Entity\Api\NowPlaying\Station {
|
): Entity\Api\NowPlaying\Station {
|
||||||
$fa = $this->adapters->getFrontendAdapter($station);
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
$backend = $this->adapters->getBackendAdapter($station);
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
$remoteAdapters = $this->adapters->getRemoteAdapters($station);
|
|
||||||
|
|
||||||
$response = new Entity\Api\NowPlaying\Station();
|
$response = new Entity\Api\NowPlaying\Station();
|
||||||
$response->id = (int)$station->getId();
|
$response->id = (int)$station->getId();
|
||||||
|
@ -35,7 +34,7 @@ class StationApiGenerator
|
||||||
$response->backend = (string)$station->getBackendType();
|
$response->backend = (string)$station->getBackendType();
|
||||||
$response->url = $station->getUrl();
|
$response->url = $station->getUrl();
|
||||||
$response->is_public = $station->getEnablePublicPage();
|
$response->is_public = $station->getEnablePublicPage();
|
||||||
$response->listen_url = $fa->getStreamUrl($station, $baseUri);
|
$response->listen_url = $frontend?->getStreamUrl($station, $baseUri);
|
||||||
|
|
||||||
$response->public_player_url = (string)$this->router->named(
|
$response->public_player_url = (string)$this->router->named(
|
||||||
'public:index',
|
'public:index',
|
||||||
|
@ -51,26 +50,31 @@ class StationApiGenerator
|
||||||
);
|
);
|
||||||
|
|
||||||
$mounts = [];
|
$mounts = [];
|
||||||
if ($fa->supportsMounts() && $station->getMounts()->count() > 0) {
|
if (
|
||||||
|
null !== $frontend && $station->getFrontendTypeEnum()->supportsMounts() && $station->getMounts()->count(
|
||||||
|
) > 0
|
||||||
|
) {
|
||||||
foreach ($station->getMounts() as $mount) {
|
foreach ($station->getMounts() as $mount) {
|
||||||
if ($showAllMounts || $mount->getIsVisibleOnPublicPages()) {
|
if ($showAllMounts || $mount->getIsVisibleOnPublicPages()) {
|
||||||
$mounts[] = $mount->api($fa, $baseUri);
|
$mounts[] = $mount->api($frontend, $baseUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$response->mounts = $mounts;
|
$response->mounts = $mounts;
|
||||||
|
|
||||||
$remotes = [];
|
$remotes = [];
|
||||||
foreach ($remoteAdapters as $ra_proxy) {
|
foreach ($station->getRemotes() as $remote) {
|
||||||
$remote = $ra_proxy->getRemote();
|
|
||||||
if ($showAllMounts || $remote->getIsVisibleOnPublicPages()) {
|
if ($showAllMounts || $remote->getIsVisibleOnPublicPages()) {
|
||||||
$remotes[] = $remote->api($ra_proxy->getAdapter());
|
$remotes[] = $remote->api(
|
||||||
|
$this->adapters->getRemoteAdapter($station, $remote)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$response->remotes = $remotes;
|
$response->remotes = $remotes;
|
||||||
|
|
||||||
$response->hls_enabled = $backend->supportsHls() && $station->getEnableHls();
|
$response->hls_enabled = $station->getBackendTypeEnum()->isEnabled() && $station->getEnableHls();
|
||||||
$response->hls_url = ($response->hls_enabled)
|
$response->hls_url = (null !== $backend && $response->hls_enabled)
|
||||||
? $backend->getHlsUrl($station, $baseUri)
|
? $backend->getHlsUrl($station, $baseUri)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,7 @@ use App\Doctrine\ReloadableEntityManagerInterface;
|
||||||
use App\Doctrine\Repository;
|
use App\Doctrine\Repository;
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Flysystem\StationFilesystems;
|
use App\Flysystem\StationFilesystems;
|
||||||
use App\Radio\Backend\AbstractBackend;
|
use App\Radio\Enums\StreamFormats;
|
||||||
use App\Radio\Frontend\AbstractFrontend;
|
|
||||||
use App\Service\Flow\UploadedFile;
|
use App\Service\Flow\UploadedFile;
|
||||||
use Azura\Files\ExtendedFilesystemInterface;
|
use Azura\Files\ExtendedFilesystemInterface;
|
||||||
use Closure;
|
use Closure;
|
||||||
|
@ -87,47 +86,49 @@ final class StationRepository extends Repository
|
||||||
)->toIterable();
|
)->toIterable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $short_code
|
|
||||||
*/
|
|
||||||
public function findByShortCode(string $short_code): ?Entity\Station
|
|
||||||
{
|
|
||||||
return $this->repository->findOneBy(['short_name' => $short_code]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset mount points to their adapter defaults (in the event of an adapter change).
|
* Reset mount points to their adapter defaults (in the event of an adapter change).
|
||||||
*
|
|
||||||
* @param Entity\Station $station
|
|
||||||
* @param AbstractFrontend $frontend_adapter
|
|
||||||
*/
|
*/
|
||||||
public function resetMounts(Entity\Station $station, AbstractFrontend $frontend_adapter): void
|
public function resetMounts(Entity\Station $station): void
|
||||||
{
|
{
|
||||||
foreach ($station->getMounts() as $mount) {
|
foreach ($station->getMounts() as $mount) {
|
||||||
$this->em->remove($mount);
|
$this->em->remove($mount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create default mountpoints if station supports them.
|
// Create default mountpoints if station supports them.
|
||||||
if ($frontend_adapter->supportsMounts()) {
|
if ($station->getFrontendTypeEnum()->supportsMounts()) {
|
||||||
// Create default mount points.
|
$record = new Entity\StationMount($station);
|
||||||
foreach ($frontend_adapter->getDefaultMounts($station) as $mount) {
|
$record->setName('/radio.mp3');
|
||||||
$this->em->persist($mount);
|
$record->setIsDefault(true);
|
||||||
}
|
$record->setEnableAutodj(true);
|
||||||
|
$record->setAutodjFormat(StreamFormats::Mp3->value);
|
||||||
|
$record->setAutodjBitrate(128);
|
||||||
|
$this->em->persist($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
$this->em->refresh($station);
|
$this->em->refresh($station);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resetHls(Entity\Station $station, AbstractBackend $backend): void
|
public function resetHls(Entity\Station $station): void
|
||||||
{
|
{
|
||||||
foreach ($station->getHlsStreams() as $hlsStream) {
|
foreach ($station->getHlsStreams() as $hlsStream) {
|
||||||
$this->em->remove($hlsStream);
|
$this->em->remove($hlsStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($station->getEnableHls() && $backend->supportsHls()) {
|
if ($station->getEnableHls() && $station->getBackendTypeEnum()->isEnabled()) {
|
||||||
foreach ($backend->getDefaultHlsStreams($station) as $hlsStream) {
|
$streams = [
|
||||||
$this->em->persist($hlsStream);
|
'aac_lofi' => 48,
|
||||||
|
'aac_midfi' => 96,
|
||||||
|
'aac_hifi' => 192,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($streams as $name => $bitrate) {
|
||||||
|
$record = new Entity\StationHlsStream($station);
|
||||||
|
$record->setName($name);
|
||||||
|
$record->setFormat(StreamFormats::Aac->value);
|
||||||
|
$record->setBitrate($bitrate);
|
||||||
|
$this->em->persist($record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ declare(strict_types=1);
|
||||||
namespace App\Event\Radio;
|
namespace App\Event\Radio;
|
||||||
|
|
||||||
use App\Entity\Station;
|
use App\Entity\Station;
|
||||||
use App\Radio;
|
use App\Radio\Adapters;
|
||||||
|
use App\Radio\Frontend\AbstractFrontend;
|
||||||
use NowPlaying\Result\Result;
|
use NowPlaying\Result\Result;
|
||||||
use Symfony\Contracts\EventDispatcher\Event;
|
use Symfony\Contracts\EventDispatcher\Event;
|
||||||
|
|
||||||
|
@ -14,9 +15,8 @@ class GenerateRawNowPlaying extends Event
|
||||||
protected ?Result $result = null;
|
protected ?Result $result = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
protected Adapters $adapters,
|
||||||
protected Station $station,
|
protected Station $station,
|
||||||
protected Radio\Frontend\AbstractFrontend $frontend,
|
|
||||||
protected array $remotes,
|
|
||||||
protected bool $include_clients = false
|
protected bool $include_clients = false
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
@ -26,17 +26,21 @@ class GenerateRawNowPlaying extends Event
|
||||||
return $this->station;
|
return $this->station;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFrontend(): Radio\Frontend\AbstractFrontend
|
public function getFrontend(): ?AbstractFrontend
|
||||||
{
|
{
|
||||||
return $this->frontend;
|
return $this->adapters->getFrontendAdapter($this->station);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Radio\Remote\AdapterProxy[]
|
|
||||||
*/
|
|
||||||
public function getRemotes(): array
|
public function getRemotes(): array
|
||||||
{
|
{
|
||||||
return $this->remotes;
|
$remotes = [];
|
||||||
|
foreach ($this->station->getRemotes() as $remote) {
|
||||||
|
$remotes[] = [
|
||||||
|
$remote,
|
||||||
|
$this->adapters->getRemoteAdapter($this->station, $remote),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $remotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeClients(): bool
|
public function includeClients(): bool
|
||||||
|
|
|
@ -10,7 +10,6 @@ use App\Customization;
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Enums\SupportedLocales;
|
use App\Enums\SupportedLocales;
|
||||||
use App\Exception;
|
use App\Exception;
|
||||||
use App\Radio;
|
|
||||||
use App\RateLimit;
|
use App\RateLimit;
|
||||||
use App\Session;
|
use App\Session;
|
||||||
use App\View;
|
use App\View;
|
||||||
|
@ -30,9 +29,6 @@ final class ServerRequest extends \Slim\Http\ServerRequest
|
||||||
public const ATTR_CUSTOMIZATION = 'customization';
|
public const ATTR_CUSTOMIZATION = 'customization';
|
||||||
public const ATTR_AUTH = 'auth';
|
public const ATTR_AUTH = 'auth';
|
||||||
public const ATTR_STATION = 'station';
|
public const ATTR_STATION = 'station';
|
||||||
public const ATTR_STATION_BACKEND = 'station_backend';
|
|
||||||
public const ATTR_STATION_FRONTEND = 'station_frontend';
|
|
||||||
public const ATTR_STATION_REMOTES = 'station_remotes';
|
|
||||||
public const ATTR_USER = 'user';
|
public const ATTR_USER = 'user';
|
||||||
|
|
||||||
public function getView(): View
|
public function getView(): View
|
||||||
|
@ -95,34 +91,6 @@ final class ServerRequest extends \Slim\Http\ServerRequest
|
||||||
return $this->getAttributeOfClass(self::ATTR_STATION, Entity\Station::class);
|
return $this->getAttributeOfClass(self::ATTR_STATION, Entity\Station::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStationFrontend(): Radio\Frontend\AbstractFrontend
|
|
||||||
{
|
|
||||||
return $this->getAttributeOfClass(self::ATTR_STATION_FRONTEND, Radio\Frontend\AbstractFrontend::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStationBackend(): Radio\Backend\AbstractBackend
|
|
||||||
{
|
|
||||||
return $this->getAttributeOfClass(self::ATTR_STATION_BACKEND, Radio\Backend\AbstractBackend::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Radio\Remote\AdapterProxy[]
|
|
||||||
* @throws Exception\InvalidRequestAttribute
|
|
||||||
*/
|
|
||||||
public function getStationRemotes(): array
|
|
||||||
{
|
|
||||||
$remotes = $this->serverRequest->getAttribute(self::ATTR_STATION_REMOTES);
|
|
||||||
|
|
||||||
if (null === $remotes) {
|
|
||||||
throw new Exception\InvalidRequestAttribute(sprintf(
|
|
||||||
'Attribute "%s" was not set.',
|
|
||||||
self::ATTR_STATION_REMOTES
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $remotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $attr
|
* @param string $attr
|
||||||
* @param string $class_name
|
* @param string $class_name
|
||||||
|
|
|
@ -7,7 +7,6 @@ namespace App\Middleware;
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Entity\Repository\StationRepository;
|
use App\Entity\Repository\StationRepository;
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
use App\Radio\Adapters;
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Server\MiddlewareInterface;
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
@ -20,8 +19,7 @@ use Slim\Routing\RouteContext;
|
||||||
class GetStation implements MiddlewareInterface
|
class GetStation implements MiddlewareInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected StationRepository $station_repo,
|
protected StationRepository $station_repo
|
||||||
protected Adapters $adapters
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,15 +33,7 @@ class GetStation implements MiddlewareInterface
|
||||||
$record = $this->station_repo->findByIdentifier($id);
|
$record = $this->station_repo->findByIdentifier($id);
|
||||||
|
|
||||||
if ($record instanceof Entity\Station) {
|
if ($record instanceof Entity\Station) {
|
||||||
$backend = $this->adapters->getBackendAdapter($record);
|
$request = $request->withAttribute(ServerRequest::ATTR_STATION, $record);
|
||||||
$frontend = $this->adapters->getFrontendAdapter($record);
|
|
||||||
$remotes = $this->adapters->getRemoteAdapters($record);
|
|
||||||
|
|
||||||
$request = $request
|
|
||||||
->withAttribute(ServerRequest::ATTR_STATION, $record)
|
|
||||||
->withAttribute(ServerRequest::ATTR_STATION_BACKEND, $backend)
|
|
||||||
->withAttribute(ServerRequest::ATTR_STATION_FRONTEND, $frontend)
|
|
||||||
->withAttribute(ServerRequest::ATTR_STATION_REMOTES, $remotes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,7 @@ class StationFiles
|
||||||
{
|
{
|
||||||
public function __invoke(ServerRequest $request, RequestHandlerInterface $handler): ResponseInterface
|
public function __invoke(ServerRequest $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
{
|
{
|
||||||
$backend = $request->getStationBackend();
|
if (!$request->getStation()->getBackendTypeEnum()->isEnabled()) {
|
||||||
if (!$backend->supportsMedia()) {
|
|
||||||
throw new Exception(__('This feature is not currently supported on this station.'));
|
throw new Exception(__('This feature is not currently supported on this station.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,14 +29,9 @@ class Stations
|
||||||
$view = $request->getView();
|
$view = $request->getView();
|
||||||
|
|
||||||
$station = $request->getStation();
|
$station = $request->getStation();
|
||||||
$backend = $request->getStationBackend();
|
|
||||||
$frontend = $request->getStationFrontend();
|
|
||||||
|
|
||||||
$view->addData(
|
$view->addData(
|
||||||
[
|
[
|
||||||
'station' => $station,
|
'station' => $station,
|
||||||
'frontend' => $frontend,
|
|
||||||
'backend' => $backend,
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use Supervisor\Exception\Fault;
|
||||||
use Supervisor\Exception\SupervisorException as SupervisorLibException;
|
use Supervisor\Exception\SupervisorException as SupervisorLibException;
|
||||||
use Supervisor\SupervisorInterface;
|
use Supervisor\SupervisorInterface;
|
||||||
|
|
||||||
abstract class AbstractAdapter
|
abstract class AbstractLocalAdapter
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected Environment $environment,
|
protected Environment $environment,
|
||||||
|
@ -155,14 +155,6 @@ abstract class AbstractAdapter
|
||||||
$this->start($station);
|
$this->start($station);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool Whether this adapter supports a non-destructive reload.
|
|
||||||
*/
|
|
||||||
public function supportsReload(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a non-destructive reload if the adapter supports it.
|
* Execute a non-destructive reload if the adapter supports it.
|
||||||
*
|
*
|
|
@ -6,6 +6,7 @@ namespace App\Radio;
|
||||||
|
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Exception\NotFoundException;
|
use App\Exception\NotFoundException;
|
||||||
|
use App\Radio\Backend\Liquidsoap;
|
||||||
use App\Radio\Enums\AdapterTypeInterface;
|
use App\Radio\Enums\AdapterTypeInterface;
|
||||||
use App\Radio\Enums\BackendAdapters;
|
use App\Radio\Enums\BackendAdapters;
|
||||||
use App\Radio\Enums\FrontendAdapters;
|
use App\Radio\Enums\FrontendAdapters;
|
||||||
|
@ -22,19 +23,13 @@ class Adapters
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getFrontendAdapter(Entity\Station $station): ?Frontend\AbstractFrontend
|
||||||
* @param Entity\Station $station
|
|
||||||
*
|
|
||||||
* @throws NotFoundException
|
|
||||||
*/
|
|
||||||
public function getFrontendAdapter(Entity\Station $station): Frontend\AbstractFrontend
|
|
||||||
{
|
{
|
||||||
$class_name = $station->getFrontendTypeEnum()->getClass();
|
$className = $station->getFrontendTypeEnum()->getClass();
|
||||||
if ($this->adapters->has($class_name)) {
|
|
||||||
return $this->adapters->get($class_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotFoundException('Adapter not found: ' . $class_name);
|
return (null !== $className && $this->adapters->has($className))
|
||||||
|
? $this->adapters->get($className)
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,19 +41,13 @@ class Adapters
|
||||||
return $this->listAdaptersFromEnum(FrontendAdapters::cases(), $checkInstalled);
|
return $this->listAdaptersFromEnum(FrontendAdapters::cases(), $checkInstalled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getBackendAdapter(Entity\Station $station): ?Liquidsoap
|
||||||
* @param Entity\Station $station
|
|
||||||
*
|
|
||||||
* @throws NotFoundException
|
|
||||||
*/
|
|
||||||
public function getBackendAdapter(Entity\Station $station): Backend\AbstractBackend
|
|
||||||
{
|
{
|
||||||
$class_name = $station->getBackendTypeEnum()->getClass();
|
$className = $station->getBackendTypeEnum()->getClass();
|
||||||
if ($this->adapters->has($class_name)) {
|
|
||||||
return $this->adapters->get($class_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotFoundException('Adapter not found: ' . $class_name);
|
return (null !== $className && $this->adapters->has($className))
|
||||||
|
? $this->adapters->get($className)
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,21 +59,6 @@ class Adapters
|
||||||
return $this->listAdaptersFromEnum(BackendAdapters::cases(), $checkInstalled);
|
return $this->listAdaptersFromEnum(BackendAdapters::cases(), $checkInstalled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Entity\Station $station
|
|
||||||
*
|
|
||||||
* @return Remote\AdapterProxy[]
|
|
||||||
* @throws NotFoundException
|
|
||||||
*/
|
|
||||||
public function getRemoteAdapters(Entity\Station $station): array
|
|
||||||
{
|
|
||||||
$remote_adapters = [];
|
|
||||||
foreach ($station->getRemotes() as $remote) {
|
|
||||||
$remote_adapters[] = new Remote\AdapterProxy($this->getRemoteAdapter($station, $remote), $remote);
|
|
||||||
}
|
|
||||||
return $remote_adapters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRemoteAdapter(Entity\Station $station, Entity\StationRemote $remote): Remote\AbstractRemote
|
public function getRemoteAdapter(Entity\Station $station, Entity\StationRemote $remote): Remote\AbstractRemote
|
||||||
{
|
{
|
||||||
$class_name = $remote->getTypeEnum()->getClass();
|
$class_name = $remote->getTypeEnum()->getClass();
|
||||||
|
@ -123,7 +97,11 @@ class Adapters
|
||||||
return array_filter(
|
return array_filter(
|
||||||
$adapters,
|
$adapters,
|
||||||
function ($adapter_info) {
|
function ($adapter_info) {
|
||||||
/** @var AbstractAdapter $adapter */
|
if (null === $adapter_info['class']) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var AbstractLocalAdapter $adapter */
|
||||||
$adapter = $this->adapters->get($adapter_info['class']);
|
$adapter = $this->adapters->get($adapter_info['class']);
|
||||||
return $adapter->isInstalled();
|
return $adapter->isInstalled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,9 @@ class Annotations implements EventSubscriberInterface
|
||||||
$event->setSongPath('media:' . ltrim($media->getPath(), '/'));
|
$event->setSongPath('media:' . ltrim($media->getPath(), '/'));
|
||||||
|
|
||||||
$backend = $this->adapters->getBackendAdapter($event->getStation());
|
$backend = $this->adapters->getBackendAdapter($event->getStation());
|
||||||
|
if (null !== $backend) {
|
||||||
$event->addAnnotations($backend->annotateMedia($media));
|
$event->addAnnotations($backend->annotateMedia($media));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$queue = $event->getQueue();
|
$queue = $event->getQueue();
|
||||||
if ($queue instanceof Entity\StationQueue) {
|
if ($queue instanceof Entity\StationQueue) {
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Radio\Backend;
|
|
||||||
|
|
||||||
use App\Entity;
|
|
||||||
use App\Nginx\CustomUrls;
|
|
||||||
use App\Radio\AbstractAdapter;
|
|
||||||
use App\Radio\Enums\StreamFormats;
|
|
||||||
use Psr\Http\Message\UriInterface;
|
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
abstract class AbstractBackend extends AbstractAdapter
|
|
||||||
{
|
|
||||||
public function supportsMedia(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsRequests(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsStreamers(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsWebStreaming(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsImmediateQueue(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsHls(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDefaultHlsStreams(Entity\Station $station): array
|
|
||||||
{
|
|
||||||
return array_map(
|
|
||||||
function (string $name, int $bitrate) use ($station) {
|
|
||||||
$record = new Entity\StationHlsStream($station);
|
|
||||||
$record->setName($name);
|
|
||||||
$record->setFormat(StreamFormats::Aac->value);
|
|
||||||
$record->setBitrate($bitrate);
|
|
||||||
return $record;
|
|
||||||
},
|
|
||||||
['aac_lofi', 'aac_midfi', 'aac_hifi'],
|
|
||||||
[48, 96, 192]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHlsUrl(Entity\Station $station, UriInterface $baseUrl = null): UriInterface
|
|
||||||
{
|
|
||||||
if (!$this->supportsHls()) {
|
|
||||||
throw new RuntimeException('Cannot generate HLS URL.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$baseUrl ??= $this->router->getBaseUrl();
|
|
||||||
|
|
||||||
return $baseUrl->withPath(
|
|
||||||
$baseUrl->getPath() . CustomUrls::getHlsUrl($station) . '/live.m3u8'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStreamPort(Entity\Station $station): ?int
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Entity\StationMedia $media
|
|
||||||
*
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
public function annotateMedia(Entity\StationMedia $media): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getProgramName(Entity\Station $station): string
|
|
||||||
{
|
|
||||||
return 'station_' . $station->getId() . ':station_' . $station->getId() . '_backend';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,43 +8,14 @@ use App\Entity;
|
||||||
use App\Event\Radio\WriteLiquidsoapConfiguration;
|
use App\Event\Radio\WriteLiquidsoapConfiguration;
|
||||||
use App\Exception;
|
use App\Exception;
|
||||||
use App\Nginx\CustomUrls;
|
use App\Nginx\CustomUrls;
|
||||||
|
use App\Radio\AbstractLocalAdapter;
|
||||||
use App\Radio\Enums\LiquidsoapQueues;
|
use App\Radio\Enums\LiquidsoapQueues;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use Psr\Http\Message\UriInterface;
|
use Psr\Http\Message\UriInterface;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
class Liquidsoap extends AbstractBackend
|
class Liquidsoap extends AbstractLocalAdapter
|
||||||
{
|
{
|
||||||
public function supportsMedia(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsRequests(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsStreamers(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsWebStreaming(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsImmediateQueue(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsHls(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
@ -234,6 +205,14 @@ class Liquidsoap extends AbstractBackend
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHlsUrl(Entity\Station $station, UriInterface $baseUrl = null): UriInterface
|
||||||
|
{
|
||||||
|
$baseUrl ??= $this->router->getBaseUrl();
|
||||||
|
return $baseUrl->withPath(
|
||||||
|
$baseUrl->getPath() . CustomUrls::getHlsUrl($station) . '/live.m3u8'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function isQueueEmpty(
|
public function isQueueEmpty(
|
||||||
Entity\Station $station,
|
Entity\Station $station,
|
||||||
LiquidsoapQueues $queue
|
LiquidsoapQueues $queue
|
||||||
|
@ -337,4 +316,9 @@ class Liquidsoap extends AbstractBackend
|
||||||
throw new LogicException($process->getOutput());
|
throw new LogicException($process->getOutput());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getProgramName(Entity\Station $station): string
|
||||||
|
{
|
||||||
|
return 'station_' . $station->getIdRequired() . ':station_' . $station->getIdRequired() . '_backend';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Radio\Backend;
|
|
||||||
|
|
||||||
use App\Entity;
|
|
||||||
|
|
||||||
class None extends AbstractBackend
|
|
||||||
{
|
|
||||||
public function isInstalled(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function start(Entity\Station $station): void
|
|
||||||
{
|
|
||||||
$this->logger->error(
|
|
||||||
'Cannot start process; AutoDJ is currently disabled.',
|
|
||||||
['station_id' => $station->getId(), 'station_name' => $station->getName()]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -113,51 +113,52 @@ class Configuration
|
||||||
throw new RuntimeException('Station is disabled.');
|
throw new RuntimeException('Station is disabled.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$frontendEnum = $station->getFrontendTypeEnum();
|
||||||
|
$backendEnum = $station->getBackendTypeEnum();
|
||||||
|
|
||||||
$frontend = $this->adapters->getFrontendAdapter($station);
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
$backend = $this->adapters->getBackendAdapter($station);
|
$backend = $this->adapters->getBackendAdapter($station);
|
||||||
|
|
||||||
// If no processes need to be managed, remove any existing config.
|
// If no processes need to be managed, remove any existing config.
|
||||||
if (!$frontend->hasCommand($station) && !$backend->hasCommand($station)) {
|
if (
|
||||||
|
(null === $frontend || !$frontend->hasCommand($station))
|
||||||
|
&& (null === $backend || !$backend->hasCommand($station))
|
||||||
|
) {
|
||||||
$this->unlinkAndStopStation($station, $reloadSupervisor);
|
$this->unlinkAndStopStation($station, $reloadSupervisor);
|
||||||
throw new RuntimeException('Station has no local services.');
|
throw new RuntimeException('Station has no local services.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If using AutoDJ and there is no media, don't spin up services.
|
// If using AutoDJ and there is no media, don't spin up services.
|
||||||
if (
|
if (
|
||||||
BackendAdapters::None !== $station->getBackendTypeEnum()
|
$backendEnum->isEnabled()
|
||||||
&& !$this->stationPlaylistRepo->stationHasActivePlaylists($station)
|
&& !$this->stationPlaylistRepo->stationHasActivePlaylists($station)
|
||||||
) {
|
) {
|
||||||
$this->unlinkAndStopStation($station, $reloadSupervisor);
|
$this->unlinkAndStopStation($station, $reloadSupervisor);
|
||||||
throw new RuntimeException('Station has no media assigned to playlists.');
|
throw new RuntimeException('Station has no media assigned to playlists.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get group information
|
|
||||||
$backend_name = $backend->getProgramName($station);
|
|
||||||
[$backend_group, $backend_program] = explode(':', $backend_name);
|
|
||||||
|
|
||||||
$frontend_name = $frontend->getProgramName($station);
|
|
||||||
[, $frontend_program] = explode(':', $frontend_name);
|
|
||||||
|
|
||||||
// Write group section of config
|
// Write group section of config
|
||||||
$programs = [];
|
$programs = [];
|
||||||
if ($backend->hasCommand($station)) {
|
if (null !== $backend && $backend->hasCommand($station)) {
|
||||||
$programs[] = $backend_program;
|
$programs[] = (explode(':', $backend->getProgramName($station)))[2];
|
||||||
}
|
}
|
||||||
if ($frontend->hasCommand($station)) {
|
if (null !== $frontend && $frontend->hasCommand($station)) {
|
||||||
$programs[] = $frontend_program;
|
$programs[] = (explode(':', $frontend->getProgramName($station)))[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
$supervisorConfig[] = '[group:' . $backend_group . ']';
|
$stationGroup = 'station_' . $station->getIdRequired();
|
||||||
|
|
||||||
|
$supervisorConfig[] = '[group:' . $stationGroup . ']';
|
||||||
$supervisorConfig[] = 'programs=' . implode(',', $programs);
|
$supervisorConfig[] = 'programs=' . implode(',', $programs);
|
||||||
$supervisorConfig[] = '';
|
$supervisorConfig[] = '';
|
||||||
|
|
||||||
// Write frontend
|
// Write frontend
|
||||||
if ($frontend->hasCommand($station)) {
|
if (null !== $frontend && $frontend->hasCommand($station)) {
|
||||||
$supervisorConfig[] = $this->writeConfigurationSection($station, $frontend, 90);
|
$supervisorConfig[] = $this->writeConfigurationSection($station, $frontend, 90);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write backend
|
// Write backend
|
||||||
if ($backend->hasCommand($station)) {
|
if (null !== $backend && $backend->hasCommand($station)) {
|
||||||
$supervisorConfig[] = $this->writeConfigurationSection($station, $backend, 100);
|
$supervisorConfig[] = $this->writeConfigurationSection($station, $backend, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,22 +167,22 @@ class Configuration
|
||||||
file_put_contents($supervisorConfigFile, $supervisor_config_data);
|
file_put_contents($supervisorConfigFile, $supervisor_config_data);
|
||||||
|
|
||||||
// Write supporting configurations.
|
// Write supporting configurations.
|
||||||
$frontend->write($station);
|
$frontend?->write($station);
|
||||||
$backend->write($station);
|
$backend?->write($station);
|
||||||
|
|
||||||
// Reload Supervisord and process groups
|
// Reload Supervisord and process groups
|
||||||
if ($reloadSupervisor) {
|
if ($reloadSupervisor) {
|
||||||
$affected_groups = $this->reloadSupervisor();
|
$affected_groups = $this->reloadSupervisor();
|
||||||
$was_restarted = in_array($backend_group, $affected_groups, true);
|
$was_restarted = in_array($stationGroup, $affected_groups, true);
|
||||||
|
|
||||||
if (!$was_restarted && $forceRestart) {
|
if (!$was_restarted && $forceRestart) {
|
||||||
try {
|
try {
|
||||||
if ($attemptReload && ($backend->supportsReload() || $frontend->supportsReload())) {
|
if ($attemptReload && ($backendEnum->isEnabled() || $frontendEnum->supportsReload())) {
|
||||||
$backend->reload($station);
|
$backend?->reload($station);
|
||||||
$frontend->reload($station);
|
$frontend?->reload($station);
|
||||||
} else {
|
} else {
|
||||||
$this->supervisor->stopProcessGroup($backend_group);
|
$this->supervisor->stopProcessGroup($stationGroup);
|
||||||
$this->supervisor->startProcessGroup($backend_group);
|
$this->supervisor->startProcessGroup($stationGroup);
|
||||||
}
|
}
|
||||||
} catch (SupervisorException) {
|
} catch (SupervisorException) {
|
||||||
}
|
}
|
||||||
|
@ -310,8 +311,8 @@ class Configuration
|
||||||
public function assignRadioPorts(Station $station, bool $force = false): void
|
public function assignRadioPorts(Station $station, bool $force = false): void
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
FrontendAdapters::Remote !== $station->getFrontendTypeEnum()
|
$station->getFrontendTypeEnum()->isEnabled()
|
||||||
|| BackendAdapters::Liquidsoap !== $station->getBackendTypeEnum()
|
|| $station->getBackendTypeEnum()->isEnabled()
|
||||||
) {
|
) {
|
||||||
$frontend_config = $station->getFrontendConfig();
|
$frontend_config = $station->getFrontendConfig();
|
||||||
$backend_config = $station->getBackendConfig();
|
$backend_config = $station->getBackendConfig();
|
||||||
|
@ -444,7 +445,7 @@ class Configuration
|
||||||
|
|
||||||
protected function writeConfigurationSection(
|
protected function writeConfigurationSection(
|
||||||
Station $station,
|
Station $station,
|
||||||
AbstractAdapter $adapter,
|
AbstractLocalAdapter $adapter,
|
||||||
?int $priority
|
?int $priority
|
||||||
): string {
|
): string {
|
||||||
[, $program_name] = explode(':', $adapter->getProgramName($station));
|
[, $program_name] = explode(':', $adapter->getProgramName($station));
|
||||||
|
|
|
@ -10,5 +10,5 @@ interface AdapterTypeInterface
|
||||||
|
|
||||||
public function getName(): string;
|
public function getName(): string;
|
||||||
|
|
||||||
public function getClass(): string;
|
public function getClass(): ?string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ declare(strict_types=1);
|
||||||
namespace App\Radio\Enums;
|
namespace App\Radio\Enums;
|
||||||
|
|
||||||
use App\Radio\Backend\Liquidsoap;
|
use App\Radio\Backend\Liquidsoap;
|
||||||
use App\Radio\Backend\None;
|
|
||||||
|
|
||||||
enum BackendAdapters: string implements AdapterTypeInterface
|
enum BackendAdapters: string implements AdapterTypeInterface
|
||||||
{
|
{
|
||||||
|
@ -27,14 +26,19 @@ enum BackendAdapters: string implements AdapterTypeInterface
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClass(): string
|
public function getClass(): ?string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::Liquidsoap => Liquidsoap::class,
|
self::Liquidsoap => Liquidsoap::class,
|
||||||
self::None => None::class,
|
default => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
return self::None !== $this;
|
||||||
|
}
|
||||||
|
|
||||||
public static function default(): self
|
public static function default(): self
|
||||||
{
|
{
|
||||||
return self::Liquidsoap;
|
return self::Liquidsoap;
|
||||||
|
|
|
@ -7,7 +7,6 @@ declare(strict_types=1);
|
||||||
namespace App\Radio\Enums;
|
namespace App\Radio\Enums;
|
||||||
|
|
||||||
use App\Radio\Frontend\Icecast;
|
use App\Radio\Frontend\Icecast;
|
||||||
use App\Radio\Frontend\Remote;
|
|
||||||
use App\Radio\Frontend\Shoutcast;
|
use App\Radio\Frontend\Shoutcast;
|
||||||
|
|
||||||
enum FrontendAdapters: string implements AdapterTypeInterface
|
enum FrontendAdapters: string implements AdapterTypeInterface
|
||||||
|
@ -30,15 +29,33 @@ enum FrontendAdapters: string implements AdapterTypeInterface
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClass(): string
|
public function getClass(): ?string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::Icecast => Icecast::class,
|
self::Icecast => Icecast::class,
|
||||||
self::Shoutcast => Shoutcast::class,
|
self::Shoutcast => Shoutcast::class,
|
||||||
self::Remote => Remote::class,
|
default => null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
return self::Remote !== $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsMounts(): bool
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::Shoutcast, self::Icecast => true,
|
||||||
|
default => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsReload(): bool
|
||||||
|
{
|
||||||
|
return self::Icecast === $this;
|
||||||
|
}
|
||||||
|
|
||||||
public static function default(): self
|
public static function default(): self
|
||||||
{
|
{
|
||||||
return self::Icecast;
|
return self::Icecast;
|
||||||
|
|
|
@ -8,8 +8,7 @@ use App\Entity;
|
||||||
use App\Environment;
|
use App\Environment;
|
||||||
use App\Http\Router;
|
use App\Http\Router;
|
||||||
use App\Nginx\CustomUrls;
|
use App\Nginx\CustomUrls;
|
||||||
use App\Radio\AbstractAdapter;
|
use App\Radio\AbstractLocalAdapter;
|
||||||
use App\Radio\Enums\StreamFormats;
|
|
||||||
use App\Xml\Reader;
|
use App\Xml\Reader;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
@ -24,7 +23,7 @@ use Psr\Http\Message\UriInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Supervisor\SupervisorInterface;
|
use Supervisor\SupervisorInterface;
|
||||||
|
|
||||||
abstract class AbstractFrontend extends AbstractAdapter
|
abstract class AbstractFrontend extends AbstractLocalAdapter
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Environment $environment,
|
Environment $environment,
|
||||||
|
@ -41,31 +40,6 @@ abstract class AbstractFrontend extends AbstractAdapter
|
||||||
parent::__construct($environment, $em, $supervisor, $dispatcher, $logger, $router);
|
parent::__construct($environment, $em, $supervisor, $dispatcher, $logger, $router);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool Whether the station supports multiple mount points per station
|
|
||||||
*/
|
|
||||||
public function supportsMounts(): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the default mounts when resetting or initializing a station.
|
|
||||||
*
|
|
||||||
* @return Entity\StationMount[]
|
|
||||||
*/
|
|
||||||
public function getDefaultMounts(Entity\Station $station): array
|
|
||||||
{
|
|
||||||
$record = new Entity\StationMount($station);
|
|
||||||
$record->setName('/radio.mp3');
|
|
||||||
$record->setIsDefault(true);
|
|
||||||
$record->setEnableAutodj(true);
|
|
||||||
$record->setAutodjFormat(StreamFormats::Mp3->value);
|
|
||||||
$record->setAutodjBitrate(128);
|
|
||||||
|
|
||||||
return [$record];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,16 +22,6 @@ class Icecast extends AbstractFrontend
|
||||||
public const LOGLEVEL_WARN = 2;
|
public const LOGLEVEL_WARN = 2;
|
||||||
public const LOGLEVEL_ERROR = 1;
|
public const LOGLEVEL_ERROR = 1;
|
||||||
|
|
||||||
public function supportsMounts(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsReload(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reload(Entity\Station $station): void
|
public function reload(Entity\Station $station): void
|
||||||
{
|
{
|
||||||
if ($this->hasCommand($station)) {
|
if ($this->hasCommand($station)) {
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Radio\Frontend;
|
|
||||||
|
|
||||||
use App\Entity;
|
|
||||||
use GuzzleHttp\Psr7\Uri;
|
|
||||||
use Psr\Http\Message\UriInterface;
|
|
||||||
|
|
||||||
class Remote extends AbstractFrontend
|
|
||||||
{
|
|
||||||
public function isInstalled(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStreamUrl(Entity\Station $station, UriInterface $base_url = null): UriInterface
|
|
||||||
{
|
|
||||||
return new Uri('');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function getStreamUrls(Entity\Station $station, UriInterface $base_url = null): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAdminUrl(Entity\Station $station, UriInterface $base_url = null): UriInterface
|
|
||||||
{
|
|
||||||
return new Uri('');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,11 +13,6 @@ use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
class Shoutcast extends AbstractFrontend
|
class Shoutcast extends AbstractFrontend
|
||||||
{
|
{
|
||||||
public function supportsMounts(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Radio\Remote;
|
|
||||||
|
|
||||||
use App\Entity;
|
|
||||||
|
|
||||||
class AdapterProxy
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected AbstractRemote $adapter,
|
|
||||||
protected Entity\StationRemote $remote
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAdapter(): AbstractRemote
|
|
||||||
{
|
|
||||||
return $this->adapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRemote(): Entity\StationRemote
|
|
||||||
{
|
|
||||||
return $this->remote;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -176,13 +176,13 @@ final class Acme
|
||||||
$this->nginx->reload();
|
$this->nginx->reload();
|
||||||
|
|
||||||
foreach ($this->stationRepo->iterateEnabledStations() as $station) {
|
foreach ($this->stationRepo->iterateEnabledStations() as $station) {
|
||||||
if (!$station->getHasStarted()) {
|
$frontendType = $station->getFrontendTypeEnum();
|
||||||
|
if (!$station->getHasStarted() || !$frontendType->supportsReload()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$frontend = $this->adapters->getFrontendAdapter($station);
|
$frontend = $this->adapters->getFrontendAdapter($station);
|
||||||
|
if (null !== $frontend && $frontend->isRunning($station)) {
|
||||||
if ($frontend->supportsReload() && $frontend->isRunning($station)) {
|
|
||||||
$frontend->reload($station);
|
$frontend->reload($station);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,15 +63,11 @@ class NowPlayingTask implements NowPlayingTaskInterface, EventSubscriberInterfac
|
||||||
|
|
||||||
$include_clients = $this->settingsRepo->readSettings()->isAnalyticsEnabled();
|
$include_clients = $this->settingsRepo->readSettings()->isAnalyticsEnabled();
|
||||||
|
|
||||||
$frontend_adapter = $this->adapters->getFrontendAdapter($station);
|
|
||||||
$remote_adapters = $this->adapters->getRemoteAdapters($station);
|
|
||||||
|
|
||||||
// Build the new "raw" NowPlaying data.
|
// Build the new "raw" NowPlaying data.
|
||||||
try {
|
try {
|
||||||
$event = new GenerateRawNowPlaying(
|
$event = new GenerateRawNowPlaying(
|
||||||
|
$this->adapters,
|
||||||
$station,
|
$station,
|
||||||
$frontend_adapter,
|
|
||||||
$remote_adapters,
|
|
||||||
$include_clients
|
$include_clients
|
||||||
);
|
);
|
||||||
$this->eventDispatcher->dispatch($event);
|
$this->eventDispatcher->dispatch($event);
|
||||||
|
@ -121,15 +117,13 @@ class NowPlayingTask implements NowPlayingTaskInterface, EventSubscriberInterfac
|
||||||
public function loadRawFromFrontend(GenerateRawNowPlaying $event): void
|
public function loadRawFromFrontend(GenerateRawNowPlaying $event): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$result = $event
|
$result = $event->getFrontend()?->getNowPlaying($event->getStation(), $event->includeClients());
|
||||||
->getFrontend()
|
if (null !== $result) {
|
||||||
->getNowPlaying($event->getStation(), $event->includeClients());
|
$event->setResult($result);
|
||||||
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->logger->error(sprintf('NowPlaying adapter error: %s', $e->getMessage()));
|
$this->logger->error(sprintf('NowPlaying adapter error: %s', $e->getMessage()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$event->setResult($result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addToRawFromRemotes(GenerateRawNowPlaying $event): void
|
public function addToRawFromRemotes(GenerateRawNowPlaying $event): void
|
||||||
|
@ -137,11 +131,11 @@ class NowPlayingTask implements NowPlayingTaskInterface, EventSubscriberInterfac
|
||||||
$result = $event->getResult();
|
$result = $event->getResult();
|
||||||
|
|
||||||
// Loop through all remotes and update NP data accordingly.
|
// Loop through all remotes and update NP data accordingly.
|
||||||
foreach ($event->getRemotes() as $ra_proxy) {
|
foreach ($event->getRemotes() as [$remote, $adapter]) {
|
||||||
try {
|
try {
|
||||||
$result = $ra_proxy->getAdapter()->updateNowPlaying(
|
$result = $adapter->updateNowPlaying(
|
||||||
$result,
|
$result,
|
||||||
$ra_proxy->getRemote(),
|
$remote,
|
||||||
$event->includeClients()
|
$event->includeClients()
|
||||||
);
|
);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
Loading…
Reference in New Issue