2014-06-04 21:37:21 +02:00
|
|
|
<?php
|
2020-10-15 00:19:31 +02:00
|
|
|
|
2021-07-19 07:53:45 +02:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
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;
|
2022-06-09 09:27:19 +02:00
|
|
|
use App\Service\Acme;
|
2020-07-08 09:03:50 +02:00
|
|
|
use Exception;
|
2020-07-03 22:24:04 +02:00
|
|
|
use NowPlaying\Result\Result;
|
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
|
|
|
|
2022-02-07 05:10:45 +01:00
|
|
|
class Shoutcast extends AbstractFrontend
|
2014-06-04 21:37:21 +02:00
|
|
|
{
|
2021-01-19 18:52:45 +01:00
|
|
|
public function supportsMounts(): bool
|
2019-09-04 20:00:51 +02:00
|
|
|
{
|
2021-01-19 18:52:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-11 05:47:37 +02:00
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
public function getBinary(): ?string
|
|
|
|
{
|
|
|
|
$new_path = '/var/azuracast/servers/shoutcast2/sc_serv';
|
|
|
|
return file_exists($new_path)
|
|
|
|
? $new_path
|
|
|
|
: null;
|
|
|
|
}
|
|
|
|
|
2021-01-19 18:52:45 +01:00
|
|
|
public function getVersion(): ?string
|
|
|
|
{
|
2022-04-11 04:18:36 +02:00
|
|
|
$binary = $this->getBinary();
|
|
|
|
if (!$binary) {
|
2019-09-04 20:00:51 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-04-11 04:18:36 +02:00
|
|
|
$process = new Process([$binary, '--version']);
|
|
|
|
$process->setWorkingDirectory(dirname($binary));
|
2019-09-04 20:00:51 +02:00
|
|
|
$process->run();
|
|
|
|
|
|
|
|
if (!$process->isSuccessful()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-04-11 04:18:36 +02:00
|
|
|
return preg_match('/^SHOUTcast .* v(\S+) .*$/i', $process->getOutput(), $matches)
|
|
|
|
? $matches[1]
|
|
|
|
: null;
|
2019-09-04 20:00:51 +02:00
|
|
|
}
|
|
|
|
|
2020-07-03 22:24:04 +02:00
|
|
|
public function getNowPlaying(Entity\Station $station, bool $includeClients = true): Result
|
2017-01-22 21:58:40 +01:00
|
|
|
{
|
2020-07-03 22:24:04 +02:00
|
|
|
$feConfig = $station->getFrontendConfig();
|
|
|
|
$radioPort = $feConfig->getPort();
|
2021-06-10 05:22:13 +02:00
|
|
|
|
2022-05-07 15:50:48 +02:00
|
|
|
$baseUrl = $this->environment->getLocalUri()
|
2022-01-28 02:33:07 +01:00
|
|
|
->withPort($radioPort);
|
2014-06-05 08:48:36 +02:00
|
|
|
|
2021-04-12 00:33:51 +02:00
|
|
|
$npAdapter = $this->adapterFactory->getShoutcast2Adapter($baseUrl);
|
2020-09-16 13:56:25 +02:00
|
|
|
$npAdapter->setAdminPassword($feConfig->getAdminPassword());
|
2017-09-07 05:53:25 +02:00
|
|
|
|
2020-07-03 22:24:04 +02:00
|
|
|
$defaultResult = Result::blank();
|
|
|
|
$otherResults = [];
|
2017-05-18 09:24:54 +02:00
|
|
|
|
2021-04-16 07:13:42 +02:00
|
|
|
$sid = 0;
|
|
|
|
foreach ($station->getMounts() as $mount) {
|
|
|
|
$sid++;
|
2020-07-03 22:24:04 +02:00
|
|
|
|
2021-04-16 07:13:42 +02:00
|
|
|
try {
|
2020-09-01 00:32:30 +02:00
|
|
|
$result = $npAdapter->getNowPlaying((string)$sid, $includeClients);
|
2020-07-03 22:24:04 +02:00
|
|
|
|
2021-01-27 18:42:54 +01:00
|
|
|
if (!empty($result->clients)) {
|
|
|
|
foreach ($result->clients as $client) {
|
|
|
|
$client->mount = 'local_' . $mount->getId();
|
|
|
|
}
|
|
|
|
}
|
2021-04-16 07:13:42 +02:00
|
|
|
} catch (Exception $e) {
|
|
|
|
$this->logger->error(sprintf('NowPlaying adapter error: %s', $e->getMessage()));
|
2021-01-27 18:42:54 +01:00
|
|
|
|
2021-04-16 07:13:42 +02:00
|
|
|
$result = Result::blank();
|
2020-07-03 22:24:04 +02:00
|
|
|
}
|
|
|
|
|
2021-04-16 07:13:42 +02:00
|
|
|
$mount->setListenersTotal($result->listeners->total);
|
2021-04-25 03:53:33 +02:00
|
|
|
$mount->setListenersUnique($result->listeners->unique ?? 0);
|
2021-04-16 07:13:42 +02:00
|
|
|
$this->em->persist($mount);
|
2020-07-03 22:24:04 +02:00
|
|
|
|
2021-04-16 07:13:42 +02:00
|
|
|
if ($mount->getIsDefault()) {
|
|
|
|
$defaultResult = $result;
|
|
|
|
} else {
|
|
|
|
$otherResults[] = $result;
|
2017-05-18 09:24:54 +02:00
|
|
|
}
|
2021-04-16 07:13:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
|
|
|
foreach ($otherResults as $otherResult) {
|
|
|
|
$defaultResult = $defaultResult->merge($otherResult);
|
2017-05-16 09:46:43 +02:00
|
|
|
}
|
2018-10-02 16:53:58 +02:00
|
|
|
|
2020-07-03 22:24:04 +02:00
|
|
|
return $defaultResult;
|
2014-06-05 08:48:36 +02:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2021-01-19 18:52:45 +01:00
|
|
|
public function getConfigurationPath(Entity\Station $station): ?string
|
2016-11-22 06:09:18 +01:00
|
|
|
{
|
2021-01-19 18:52:45 +01:00
|
|
|
return $station->getRadioConfigDir() . '/sc_serv.conf';
|
2016-11-22 06:09:18 +01:00
|
|
|
}
|
|
|
|
|
2021-01-19 18:52:45 +01:00
|
|
|
public function getCurrentConfiguration(Entity\Station $station): ?string
|
2019-09-04 20:00:51 +02:00
|
|
|
{
|
2021-01-19 18:52:45 +01:00
|
|
|
$configPath = $station->getRadioConfigDir();
|
|
|
|
$frontendConfig = $station->getFrontendConfig();
|
|
|
|
|
2022-06-09 09:27:19 +02:00
|
|
|
[$certPath, $certKey] = Acme::getCertificatePaths();
|
2022-02-07 05:10:45 +01:00
|
|
|
|
|
|
|
$config = [
|
|
|
|
'password' => $frontendConfig->getSourcePassword(),
|
|
|
|
'adminpassword' => $frontendConfig->getAdminPassword(),
|
|
|
|
'logfile' => $configPath . '/sc_serv.log',
|
|
|
|
'w3clog' => $configPath . '/sc_w3c.log',
|
2022-02-18 10:34:42 +01:00
|
|
|
'banfile' => $this->writeIpBansFile($station),
|
2022-02-14 13:36:28 +01:00
|
|
|
'agentfile' => $this->writeUserAgentBansFile($station, 'sc_serv.agent'),
|
2022-02-07 05:10:45 +01:00
|
|
|
'ripfile' => $configPath . '/sc_serv.rip',
|
|
|
|
'maxuser' => $frontendConfig->getMaxListeners() ?? 250,
|
|
|
|
'portbase' => $frontendConfig->getPort(),
|
2021-01-19 18:52:45 +01:00
|
|
|
'requirestreamconfigs' => 1,
|
2022-02-18 10:34:42 +01:00
|
|
|
'savebanlistonexit' => '0',
|
|
|
|
'saveagentlistonexit' => '0',
|
2022-02-07 05:10:45 +01:00
|
|
|
'licenceid' => $frontendConfig->getScLicenseId(),
|
|
|
|
'userid' => $frontendConfig->getScUserId(),
|
2022-06-09 09:27:19 +02:00
|
|
|
'sslCertificateFile' => $certPath,
|
|
|
|
'sslCertificateKeyFile' => $certKey,
|
2022-02-07 05:10:45 +01:00
|
|
|
];
|
2019-09-04 20:00:51 +02:00
|
|
|
|
2021-05-05 09:31:06 +02:00
|
|
|
$customConfig = trim($frontendConfig->getCustomConfiguration() ?? '');
|
2020-05-02 09:58:59 +02:00
|
|
|
if (!empty($customConfig)) {
|
2020-10-15 00:19:31 +02:00
|
|
|
$custom_conf = $this->processCustomConfig($customConfig);
|
2021-05-05 09:31:06 +02:00
|
|
|
|
|
|
|
if (false !== $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
|
|
|
|
2021-08-01 12:00:42 +02:00
|
|
|
if (!empty($mount_row->getIntroPath())) {
|
|
|
|
$introPath = $mount_row->getIntroPath();
|
|
|
|
$config['streamintrofile_' . $i] = $station->getRadioConfigDir() . '/' . $introPath;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2021-06-21 00:22:32 +02:00
|
|
|
|
|
|
|
if ($mount_row->getMaxListenerDuration()) {
|
|
|
|
$config['streammaxuser_' . $i] = $mount_row->getMaxListenerDuration();
|
|
|
|
}
|
2017-04-17 10:57:42 +02:00
|
|
|
}
|
|
|
|
|
2021-01-19 18:52:45 +01:00
|
|
|
$configFileOutput = '';
|
2017-01-24 01:35:16 +01:00
|
|
|
foreach ($config as $config_key => $config_value) {
|
2022-06-09 09:27:19 +02:00
|
|
|
$configFileOutput .= $config_key . '=' . str_replace("\n", '', (string)$config_value) . "\n";
|
2017-01-24 01:35:16 +01:00
|
|
|
}
|
2016-11-22 06:09:18 +01:00
|
|
|
|
2021-01-19 18:52:45 +01:00
|
|
|
return $configFileOutput;
|
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
|
|
|
|
{
|
2021-01-19 18:52:45 +01:00
|
|
|
if ($binary = $this->getBinary()) {
|
|
|
|
return $binary . ' ' . $this->getConfigurationPath($station);
|
2019-09-04 20:00:51 +02:00
|
|
|
}
|
2021-01-19 18:52:45 +01:00
|
|
|
return null;
|
2019-09-04 20:00:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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');
|
|
|
|
}
|
2022-02-18 10:34:42 +01:00
|
|
|
|
|
|
|
protected function writeIpBansFile(
|
|
|
|
Entity\Station $station,
|
|
|
|
string $fileName = 'sc_serv.ban',
|
|
|
|
string $ipsSeparator = ";255;\n"
|
|
|
|
): string {
|
|
|
|
$ips = $this->getBannedIps($station);
|
|
|
|
|
|
|
|
$configDir = $station->getRadioConfigDir();
|
|
|
|
$bansFile = $configDir . '/' . $fileName;
|
|
|
|
|
|
|
|
$bannedIpsString = implode($ipsSeparator, $ips);
|
|
|
|
if (!empty($bannedIpsString)) {
|
|
|
|
$bannedIpsString .= $ipsSeparator;
|
|
|
|
}
|
|
|
|
|
|
|
|
file_put_contents($bansFile, $bannedIpsString);
|
|
|
|
|
|
|
|
return $bansFile;
|
|
|
|
}
|
2018-08-05 00:05:14 +02:00
|
|
|
}
|