2014-06-04 21:37:21 +02:00
|
|
|
<?php
|
2018-08-05 00:05:14 +02:00
|
|
|
namespace App\Radio\Frontend;
|
2014-06-04 21:37:21 +02:00
|
|
|
|
2018-08-05 00:05:14 +02:00
|
|
|
use App\Entity;
|
2020-02-12 17:53:14 +01:00
|
|
|
use App\Logger;
|
2019-09-12 07:31:01 +02:00
|
|
|
use App\Settings;
|
2019-08-07 06:33:55 +02:00
|
|
|
use App\Utilities;
|
2019-09-04 20:00:51 +02:00
|
|
|
use NowPlaying\Adapter\AdapterAbstract;
|
|
|
|
use NowPlaying\Adapter\SHOUTcast2;
|
2018-10-05 01:12:12 +02:00
|
|
|
use Psr\Http\Message\UriInterface;
|
2019-01-03 09:30:56 +01:00
|
|
|
use Symfony\Component\Process\Process;
|
2014-06-04 21:37:21 +02:00
|
|
|
|
2018-12-27 09:24:07 +01:00
|
|
|
class SHOUTcast extends AbstractFrontend
|
2014-06-04 21:37:21 +02:00
|
|
|
{
|
2019-09-04 20:00:51 +02:00
|
|
|
/**
|
|
|
|
* @return string|null
|
|
|
|
*/
|
|
|
|
public static function getVersion(): ?string
|
|
|
|
{
|
|
|
|
$binary_path = self::getBinary();
|
|
|
|
if (!$binary_path) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$process = new Process([$binary_path, '--version']);
|
|
|
|
$process->setWorkingDirectory(dirname($binary_path));
|
|
|
|
$process->run();
|
|
|
|
|
|
|
|
if (!$process->isSuccessful()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return trim($process->getOutput());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function getBinary()
|
|
|
|
{
|
2019-09-12 07:31:01 +02:00
|
|
|
$new_path = '/var/azuracast/servers/shoutcast2/sc_serv';
|
2019-09-04 20:00:51 +02:00
|
|
|
|
|
|
|
// Docker versions before 3 included the SC binary across the board.
|
2019-09-12 07:31:01 +02:00
|
|
|
$settings = Settings::getInstance();
|
|
|
|
if ($settings->isDocker() && $settings[Settings::DOCKER_REVISION] < 3) {
|
2019-09-04 20:00:51 +02:00
|
|
|
return $new_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
return file_exists($new_path)
|
|
|
|
? $new_path
|
|
|
|
: false;
|
|
|
|
}
|
|
|
|
|
2018-09-22 13:52:43 +02:00
|
|
|
public function getNowPlaying(Entity\Station $station, $payload = null, $include_clients = true): array
|
2017-01-22 21:58:40 +01:00
|
|
|
{
|
2018-09-22 13:52:43 +02:00
|
|
|
$fe_config = (array)$station->getFrontendConfig();
|
2017-01-24 01:17:50 +01:00
|
|
|
$radio_port = $fe_config['port'];
|
2014-06-05 08:48:36 +02:00
|
|
|
|
2019-09-12 07:31:01 +02:00
|
|
|
$base_url = 'http://' . (Settings::getInstance()->isDocker() ? 'stations' : 'localhost') . ':' . $radio_port;
|
2017-09-07 05:53:25 +02:00
|
|
|
|
2019-09-04 20:00:51 +02:00
|
|
|
$np_adapter = new SHOUTcast2($base_url, $this->http_client);
|
2018-09-13 19:45:56 +02:00
|
|
|
$np_adapter->setAdminPassword($fe_config['admin_pw']);
|
2017-01-24 01:17:50 +01:00
|
|
|
|
2019-09-04 20:00:51 +02:00
|
|
|
$np_final = AdapterAbstract::NOWPLAYING_EMPTY;
|
2018-10-02 16:53:58 +02:00
|
|
|
$np_final['listeners']['clients'] = [];
|
2017-05-18 09:24:54 +02:00
|
|
|
|
2018-09-13 19:45:56 +02:00
|
|
|
try {
|
2018-10-02 16:53:58 +02:00
|
|
|
$sid = 0;
|
|
|
|
|
2019-09-04 20:00:51 +02:00
|
|
|
foreach ($station->getMounts() as $mount) {
|
2018-10-02 16:53:58 +02:00
|
|
|
/** @var Entity\StationMount $mount */
|
|
|
|
$sid++;
|
2019-08-11 06:17:06 +02:00
|
|
|
$np_final = $this->_processNowPlayingForMount(
|
|
|
|
$mount,
|
|
|
|
$np_final,
|
|
|
|
$np_adapter->getNowPlaying($sid),
|
|
|
|
$include_clients ? $np_adapter->getClients($sid, true) : null
|
|
|
|
);
|
2017-05-18 09:24:54 +02:00
|
|
|
}
|
2020-02-12 17:55:17 +01:00
|
|
|
} catch (\Exception $e) {
|
2019-09-12 09:53:43 +02:00
|
|
|
Logger::getInstance()->error(sprintf('NowPlaying adapter error: %s', $e->getMessage()));
|
2017-05-16 09:46:43 +02:00
|
|
|
}
|
2018-10-02 16:53:58 +02:00
|
|
|
|
|
|
|
return $np_final;
|
2014-06-05 08:48:36 +02:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2019-09-04 20:00:51 +02:00
|
|
|
/*
|
|
|
|
* Process Management
|
|
|
|
*/
|
|
|
|
|
2018-05-26 23:26:34 +02:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
2018-09-22 13:52:43 +02:00
|
|
|
public function read(Entity\Station $station): bool
|
2016-11-22 06:09:18 +01:00
|
|
|
{
|
2018-09-22 13:52:43 +02:00
|
|
|
$config = $this->_getConfig($station);
|
|
|
|
$station->setFrontendConfigDefaults($this->_loadFromConfig($config));
|
2017-01-24 01:17:50 +01:00
|
|
|
return true;
|
2016-11-22 06:09:18 +01:00
|
|
|
}
|
|
|
|
|
2019-09-04 20:00:51 +02:00
|
|
|
protected function _getConfig(Entity\Station $station)
|
|
|
|
{
|
|
|
|
$config_dir = $station->getRadioConfigDir();
|
|
|
|
return @parse_ini_file($config_dir . '/sc_serv.conf', false, INI_SCANNER_RAW);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Configuration
|
|
|
|
*/
|
|
|
|
|
|
|
|
protected function _loadFromConfig($config): array
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'port' => $config['portbase'],
|
|
|
|
'source_pw' => $config['password'],
|
|
|
|
'admin_pw' => $config['adminpassword'],
|
|
|
|
'max_listeners' => $config['maxuser'],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2018-09-22 13:52:43 +02:00
|
|
|
public function write(Entity\Station $station): bool
|
2016-11-22 06:09:18 +01:00
|
|
|
{
|
2018-09-22 13:52:43 +02:00
|
|
|
$config = $this->_getDefaults($station);
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2018-09-22 13:52:43 +02:00
|
|
|
$frontend_config = (array)$station->getFrontendConfig();
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2017-01-24 01:35:16 +01:00
|
|
|
if (!empty($frontend_config['port'])) {
|
2016-11-22 06:09:18 +01:00
|
|
|
$config['portbase'] = $frontend_config['port'];
|
2017-01-24 01:35:16 +01:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2017-01-24 01:35:16 +01:00
|
|
|
if (!empty($frontend_config['source_pw'])) {
|
2016-11-22 06:09:18 +01:00
|
|
|
$config['password'] = $frontend_config['source_pw'];
|
2017-01-24 01:35:16 +01:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2017-01-24 01:35:16 +01:00
|
|
|
if (!empty($frontend_config['admin_pw'])) {
|
2016-11-22 06:09:18 +01:00
|
|
|
$config['adminpassword'] = $frontend_config['admin_pw'];
|
2017-01-24 01:35:16 +01:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2017-05-24 01:18:27 +02:00
|
|
|
if (!empty($frontend_config['max_listeners'])) {
|
|
|
|
$config['maxuser'] = $frontend_config['max_listeners'];
|
|
|
|
}
|
|
|
|
|
2017-01-24 01:35:16 +01:00
|
|
|
if (!empty($frontend_config['custom_config'])) {
|
2016-11-22 06:09:18 +01:00
|
|
|
$custom_conf = $this->_processCustomConfig($frontend_config['custom_config']);
|
2017-01-24 01:35:16 +01:00
|
|
|
if (!empty($custom_conf)) {
|
2016-11-22 06:09:18 +01:00
|
|
|
$config = array_merge($config, $custom_conf);
|
2017-01-24 01:35:16 +01:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
}
|
|
|
|
|
2017-04-17 10:57:42 +02:00
|
|
|
$i = 0;
|
2018-09-22 13:52:43 +02:00
|
|
|
foreach ($station->getMounts() as $mount_row) {
|
2017-08-17 20:28:48 +02:00
|
|
|
/** @var Entity\StationMount $mount_row */
|
2017-04-17 10:57:42 +02:00
|
|
|
$i++;
|
2019-09-04 20:00:51 +02:00
|
|
|
$config['streamid_' . $i] = $i;
|
|
|
|
$config['streampath_' . $i] = $mount_row->getName();
|
2017-04-17 10:57:42 +02:00
|
|
|
|
2017-08-17 20:28:48 +02:00
|
|
|
if ($mount_row->getRelayUrl()) {
|
2019-09-04 20:00:51 +02:00
|
|
|
$config['streamrelayurl_' . $i] = $mount_row->getRelayUrl();
|
2017-04-17 10:57:42 +02:00
|
|
|
}
|
2017-06-19 06:44:07 +02:00
|
|
|
|
2017-08-17 20:28:48 +02:00
|
|
|
if ($mount_row->getAuthhash()) {
|
|
|
|
$config['streamauthhash_' . $i] = $mount_row->getAuthhash();
|
2017-06-19 06:44:07 +02:00
|
|
|
}
|
2017-04-17 10:57:42 +02:00
|
|
|
}
|
|
|
|
|
2016-11-22 06:09:18 +01:00
|
|
|
// Set any unset values back to the DB config.
|
2018-09-22 13:52:43 +02:00
|
|
|
$station->setFrontendConfigDefaults($this->_loadFromConfig($config));
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2018-09-22 13:52:43 +02:00
|
|
|
$this->em->persist($station);
|
2018-03-06 18:42:01 +01:00
|
|
|
$this->em->flush();
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2018-09-22 13:52:43 +02:00
|
|
|
$config_path = $station->getRadioConfigDir();
|
2017-01-24 01:35:16 +01:00
|
|
|
$sc_path = $config_path . '/sc_serv.conf';
|
2016-11-22 06:09:18 +01:00
|
|
|
|
|
|
|
$sc_file = '';
|
2017-01-24 01:35:16 +01:00
|
|
|
foreach ($config as $config_key => $config_value) {
|
2019-12-07 01:57:50 +01:00
|
|
|
$sc_file .= $config_key . '=' . str_replace("\n", '', $config_value) . "\n";
|
2017-01-24 01:35:16 +01:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
|
|
|
|
file_put_contents($sc_path, $sc_file);
|
2018-09-07 03:39:49 +02:00
|
|
|
return true;
|
2016-11-22 06:09:18 +01:00
|
|
|
}
|
|
|
|
|
2018-09-22 13:52:43 +02:00
|
|
|
protected function _getDefaults(Entity\Station $station): array
|
2016-11-22 06:09:18 +01:00
|
|
|
{
|
2018-09-22 13:52:43 +02:00
|
|
|
$config_path = $station->getRadioConfigDir();
|
2017-01-22 21:58:40 +01:00
|
|
|
|
2019-12-07 01:57:50 +01:00
|
|
|
return [
|
2017-01-24 01:35:16 +01:00
|
|
|
'password' => Utilities::generatePassword(),
|
2017-01-24 01:17:50 +01:00
|
|
|
'adminpassword' => Utilities::generatePassword(),
|
2019-03-14 01:36:46 +01:00
|
|
|
'logfile' => '/tmp/sc_serv.log',
|
2017-01-24 01:35:16 +01:00
|
|
|
'w3clog' => $config_path . '/sc_w3c.log',
|
2020-03-02 01:03:03 +01:00
|
|
|
'banfile' => $this->writeIpBansFile($station),
|
2017-01-24 01:35:16 +01:00
|
|
|
'ripfile' => $config_path . '/sc_serv.rip',
|
2017-05-24 01:18:27 +02:00
|
|
|
'maxuser' => 250,
|
2018-09-22 13:59:54 +02:00
|
|
|
'portbase' => $this->_getRadioPort($station),
|
2017-04-23 03:48:19 +02:00
|
|
|
'requirestreamconfigs' => 1,
|
2016-11-22 06:09:18 +01:00
|
|
|
];
|
|
|
|
}
|
2017-05-19 08:38:17 +02:00
|
|
|
|
2019-09-04 20:00:51 +02:00
|
|
|
public function getCommand(Entity\Station $station): ?string
|
|
|
|
{
|
|
|
|
if ($binary = self::getBinary()) {
|
|
|
|
$config_path = $station->getRadioConfigDir();
|
|
|
|
$sc_config = $config_path . '/sc_serv.conf';
|
|
|
|
|
|
|
|
return $binary . ' ' . $sc_config;
|
|
|
|
}
|
|
|
|
|
|
|
|
return '/bin/false';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAdminUrl(Entity\Station $station, UriInterface $base_url = null): UriInterface
|
|
|
|
{
|
|
|
|
$public_url = $this->getPublicUrl($station, $base_url);
|
|
|
|
return $public_url
|
|
|
|
->withPath($public_url->getPath() . '/admin.cgi');
|
|
|
|
}
|
|
|
|
|
2018-09-22 13:52:43 +02:00
|
|
|
protected function _getDefaultMountSid(Entity\Station $station): int
|
2018-09-13 19:45:56 +02:00
|
|
|
{
|
|
|
|
$default_sid = null;
|
|
|
|
$sid = 0;
|
2019-09-04 20:00:51 +02:00
|
|
|
foreach ($station->getMounts() as $mount) {
|
2018-09-13 19:45:56 +02:00
|
|
|
/** @var Entity\StationMount $mount */
|
|
|
|
$sid++;
|
|
|
|
|
|
|
|
if ($mount->getIsDefault()) {
|
|
|
|
$default_sid = $sid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $default_sid ?? 1;
|
|
|
|
}
|
2018-08-05 00:05:14 +02:00
|
|
|
}
|