2015-01-02 08:16:54 +00:00
|
|
|
<?php
|
|
|
|
namespace Entity;
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
2016-09-27 21:54:19 +00:00
|
|
|
use Interop\Container\ContainerInterface;
|
2015-01-02 08:16:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @Table(name="station")
|
2017-01-17 08:46:43 +00:00
|
|
|
* @Entity(repositoryClass="Entity\Repository\StationRepository")
|
2015-02-26 05:01:02 +00:00
|
|
|
* @HasLifecycleCallbacks
|
2015-01-02 08:16:54 +00:00
|
|
|
*/
|
2016-05-02 08:18:32 +00:00
|
|
|
class Station extends \App\Doctrine\Entity
|
2015-01-02 08:16:54 +00:00
|
|
|
{
|
2017-01-24 00:17:50 +00:00
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
$this->automation_timestamp = 0;
|
|
|
|
$this->enable_streamers = false;
|
|
|
|
$this->enable_requests = false;
|
2017-04-14 21:03:59 +00:00
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
$this->request_delay = 5;
|
2017-04-14 21:03:59 +00:00
|
|
|
$this->request_threshold = 15;
|
2017-01-24 00:17:50 +00:00
|
|
|
|
|
|
|
$this->needs_restart = false;
|
|
|
|
|
|
|
|
$this->history = new ArrayCollection;
|
|
|
|
$this->managers = new ArrayCollection;
|
|
|
|
|
|
|
|
$this->media = new ArrayCollection;
|
|
|
|
$this->playlists = new ArrayCollection;
|
|
|
|
$this->mounts = new ArrayCollection;
|
|
|
|
|
|
|
|
$this->streamers = new ArrayCollection;
|
|
|
|
}
|
|
|
|
|
2015-01-02 08:16:54 +00:00
|
|
|
/**
|
|
|
|
* @Column(name="id", type="integer")
|
|
|
|
* @Id
|
|
|
|
* @GeneratedValue(strategy="IDENTITY")
|
|
|
|
*/
|
|
|
|
protected $id;
|
|
|
|
|
|
|
|
/** @Column(name="name", type="string", length=100, nullable=true) */
|
|
|
|
protected $name;
|
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
public function getShortName()
|
|
|
|
{
|
|
|
|
return self::getStationShortName($this->name);
|
|
|
|
}
|
|
|
|
|
2016-05-06 08:57:34 +00:00
|
|
|
/** @Column(name="frontend_type", type="string", length=100, nullable=true) */
|
|
|
|
protected $frontend_type;
|
2015-02-16 07:46:37 +00:00
|
|
|
|
2017-05-02 21:13:16 +00:00
|
|
|
/** @Column(name="frontend_config", type="json_array", nullable=true) */
|
2016-05-19 14:56:21 +00:00
|
|
|
protected $frontend_config;
|
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
/**
|
|
|
|
* @return \AzuraCast\Radio\Frontend\FrontendAbstract
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function getFrontendAdapter(ContainerInterface $di)
|
|
|
|
{
|
|
|
|
$adapters = self::getFrontendAdapters();
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
if (!isset($adapters['adapters'][$this->frontend_type])) {
|
|
|
|
throw new \Exception('Adapter not found: ' . $this->frontend_type);
|
|
|
|
}
|
2017-01-24 00:17:50 +00:00
|
|
|
|
|
|
|
$class_name = $adapters['adapters'][$this->frontend_type]['class'];
|
2017-01-24 00:35:16 +00:00
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
return new $class_name($di, $this);
|
|
|
|
}
|
|
|
|
|
2016-05-06 08:57:34 +00:00
|
|
|
/** @Column(name="backend_type", type="string", length=100, nullable=true) */
|
|
|
|
protected $backend_type;
|
2015-02-17 12:23:21 +00:00
|
|
|
|
2017-05-02 21:13:16 +00:00
|
|
|
/** @Column(name="backend_config", type="json_array", nullable=true) */
|
2016-05-19 14:56:21 +00:00
|
|
|
protected $backend_config;
|
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
/**
|
|
|
|
* @return \AzuraCast\Radio\Backend\BackendAbstract
|
|
|
|
* @throws \Exception
|
|
|
|
*/
|
|
|
|
public function getBackendAdapter(ContainerInterface $di)
|
|
|
|
{
|
|
|
|
$adapters = self::getBackendAdapters();
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
if (!isset($adapters['adapters'][$this->backend_type])) {
|
|
|
|
throw new \Exception('Adapter not found: ' . $this->backend_type);
|
|
|
|
}
|
2017-01-24 00:17:50 +00:00
|
|
|
|
|
|
|
$class_name = $adapters['adapters'][$this->backend_type]['class'];
|
2017-01-24 00:35:16 +00:00
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
return new $class_name($di, $this);
|
|
|
|
}
|
|
|
|
|
2016-05-06 08:57:34 +00:00
|
|
|
/** @Column(name="description", type="text", nullable=true) */
|
|
|
|
protected $description;
|
|
|
|
|
2017-04-23 20:37:10 +00:00
|
|
|
/** @Column(name="url", type="string", length=255, nullable=true) */
|
|
|
|
protected $url;
|
|
|
|
|
2016-05-06 08:57:34 +00:00
|
|
|
/** @Column(name="radio_base_dir", type="string", length=255, nullable=true) */
|
|
|
|
protected $radio_base_dir;
|
2015-01-02 08:16:54 +00:00
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
public function setRadioBaseDir($new_dir)
|
|
|
|
{
|
2017-01-24 00:35:16 +00:00
|
|
|
if (strcmp($this->radio_base_dir, $new_dir) !== 0) {
|
2017-01-24 00:17:50 +00:00
|
|
|
$this->radio_base_dir = $new_dir;
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
$radio_dirs = [
|
|
|
|
$this->radio_base_dir,
|
|
|
|
$this->getRadioMediaDir(),
|
|
|
|
$this->getRadioPlaylistsDir(),
|
|
|
|
$this->getRadioConfigDir()
|
|
|
|
];
|
|
|
|
foreach ($radio_dirs as $radio_dir) {
|
|
|
|
if (!file_exists($radio_dir)) {
|
2017-01-24 00:17:50 +00:00
|
|
|
mkdir($radio_dir, 0777);
|
2017-01-24 00:35:16 +00:00
|
|
|
}
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getRadioMediaDir()
|
|
|
|
{
|
2017-01-24 00:35:16 +00:00
|
|
|
return $this->radio_base_dir . '/media';
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getRadioPlaylistsDir()
|
|
|
|
{
|
2017-01-24 00:35:16 +00:00
|
|
|
return $this->radio_base_dir . '/playlists';
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getRadioConfigDir()
|
|
|
|
{
|
2017-01-24 00:35:16 +00:00
|
|
|
return $this->radio_base_dir . '/config';
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
2017-05-02 21:13:16 +00:00
|
|
|
/** @Column(name="nowplaying_data", type="json_array", nullable=true) */
|
2016-05-19 14:56:21 +00:00
|
|
|
protected $nowplaying_data;
|
|
|
|
|
2017-05-02 21:13:16 +00:00
|
|
|
/** @Column(name="automation_settings", type="json_array", nullable=true) */
|
2016-08-30 06:28:56 +00:00
|
|
|
protected $automation_settings;
|
|
|
|
|
|
|
|
/** @Column(name="automation_timestamp", type="integer", nullable=true) */
|
|
|
|
protected $automation_timestamp;
|
|
|
|
|
2016-09-01 18:22:23 +00:00
|
|
|
/** @Column(name="enable_requests", type="boolean", nullable=false) */
|
|
|
|
protected $enable_requests;
|
|
|
|
|
2016-09-04 23:24:53 +00:00
|
|
|
/** @Column(name="request_delay", type="integer", nullable=true) */
|
|
|
|
protected $request_delay;
|
|
|
|
|
2017-04-14 21:03:59 +00:00
|
|
|
/** @Column(name="request_threshold", type="integer", nullable=true) */
|
|
|
|
protected $request_threshold;
|
|
|
|
|
2016-09-01 18:22:23 +00:00
|
|
|
/** @Column(name="enable_streamers", type="boolean", nullable=false) */
|
|
|
|
protected $enable_streamers;
|
|
|
|
|
2016-11-20 04:00:25 +00:00
|
|
|
/** @Column(name="needs_restart", type="boolean") */
|
|
|
|
protected $needs_restart;
|
|
|
|
|
2015-01-02 08:16:54 +00:00
|
|
|
/**
|
|
|
|
* @OneToMany(targetEntity="SongHistory", mappedBy="station")
|
|
|
|
* @OrderBy({"timestamp" = "DESC"})
|
|
|
|
*/
|
|
|
|
protected $history;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @OneToMany(targetEntity="StationMedia", mappedBy="station")
|
|
|
|
*/
|
|
|
|
protected $media;
|
|
|
|
|
2016-09-02 00:34:08 +00:00
|
|
|
/**
|
|
|
|
* @OneToMany(targetEntity="StationStreamer", mappedBy="station")
|
|
|
|
*/
|
|
|
|
protected $streamers;
|
|
|
|
|
2016-05-04 00:03:28 +00:00
|
|
|
/**
|
|
|
|
* @OneToMany(targetEntity="StationPlaylist", mappedBy="station")
|
2016-09-21 07:53:48 +00:00
|
|
|
* @OrderBy({"type" = "ASC","weight" = "DESC"})
|
2016-05-04 00:03:28 +00:00
|
|
|
*/
|
|
|
|
protected $playlists;
|
2015-01-02 08:16:54 +00:00
|
|
|
|
2016-11-17 03:15:34 +00:00
|
|
|
/**
|
|
|
|
* @OneToMany(targetEntity="StationMount", mappedBy="station")
|
|
|
|
*/
|
|
|
|
protected $mounts;
|
|
|
|
|
2016-12-12 23:01:28 +00:00
|
|
|
/**
|
|
|
|
* Write all configuration changes to the filesystem and reload supervisord.
|
|
|
|
*
|
|
|
|
* @param ContainerInterface $di
|
|
|
|
*/
|
|
|
|
public function writeConfiguration(ContainerInterface $di)
|
|
|
|
{
|
2017-01-24 00:35:16 +00:00
|
|
|
if (APP_TESTING_MODE) {
|
2016-12-13 04:02:15 +00:00
|
|
|
return;
|
2017-01-24 00:35:16 +00:00
|
|
|
}
|
2016-12-13 04:02:15 +00:00
|
|
|
|
2016-12-12 23:01:28 +00:00
|
|
|
$config_path = $this->getRadioConfigDir();
|
|
|
|
$supervisor_config = [];
|
2017-01-24 00:35:16 +00:00
|
|
|
$supervisor_config_path = $config_path . '/supervisord.conf';
|
2016-12-12 23:01:28 +00:00
|
|
|
|
|
|
|
$frontend = $this->getFrontendAdapter($di);
|
|
|
|
$backend = $this->getBackendAdapter($di);
|
|
|
|
|
|
|
|
// If no processes need to be managed, remove any existing config.
|
2017-01-24 00:35:16 +00:00
|
|
|
if (!$frontend->hasCommand() && !$backend->hasCommand()) {
|
2016-12-12 23:01:28 +00:00
|
|
|
@unlink($supervisor_config_path);
|
2017-04-08 19:34:59 +00:00
|
|
|
$this->_reloadSupervisor($di['supervisor']);
|
2016-12-12 23:01:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write config files for both backend and frontend.
|
|
|
|
$frontend->write();
|
|
|
|
$backend->write();
|
|
|
|
|
|
|
|
// Get group information
|
|
|
|
$backend_name = $backend->getProgramName();
|
|
|
|
list($backend_group, $backend_program) = explode(':', $backend_name);
|
|
|
|
|
|
|
|
$frontend_name = $frontend->getProgramName();
|
|
|
|
list($frontend_group, $frontend_program) = explode(':', $frontend_name);
|
|
|
|
|
|
|
|
// Write group section of config
|
|
|
|
$programs = [];
|
2017-01-24 00:35:16 +00:00
|
|
|
if ($backend->hasCommand()) {
|
2016-12-12 23:01:28 +00:00
|
|
|
$programs[] = $backend_program;
|
2017-01-24 00:35:16 +00:00
|
|
|
}
|
|
|
|
if ($frontend->hasCommand()) {
|
2016-12-12 23:01:28 +00:00
|
|
|
$programs[] = $frontend_program;
|
2017-01-24 00:35:16 +00:00
|
|
|
}
|
2016-12-12 23:01:28 +00:00
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
$supervisor_config[] = '[group:' . $backend_group . ']';
|
|
|
|
$supervisor_config[] = 'programs=' . implode(',', $programs);
|
2016-12-12 23:01:28 +00:00
|
|
|
$supervisor_config[] = '';
|
|
|
|
|
|
|
|
// Write frontend
|
2017-01-24 00:35:16 +00:00
|
|
|
if ($frontend->hasCommand()) {
|
|
|
|
$supervisor_config[] = '[program:' . $frontend_program . ']';
|
|
|
|
$supervisor_config[] = 'directory=' . $config_path;
|
|
|
|
$supervisor_config[] = 'command=' . $frontend->getCommand();
|
2016-12-12 23:01:28 +00:00
|
|
|
$supervisor_config[] = 'user=azuracast';
|
|
|
|
$supervisor_config[] = 'priority=90';
|
|
|
|
$supervisor_config[] = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write backend
|
2017-01-24 00:35:16 +00:00
|
|
|
if ($backend->hasCommand()) {
|
|
|
|
$supervisor_config[] = '[program:' . $backend_program . ']';
|
|
|
|
$supervisor_config[] = 'directory=' . $config_path;
|
|
|
|
$supervisor_config[] = 'command=' . $backend->getCommand();
|
2016-12-12 23:01:28 +00:00
|
|
|
$supervisor_config[] = 'user=azuracast';
|
|
|
|
$supervisor_config[] = 'priority=100';
|
|
|
|
$supervisor_config[] = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write config contents
|
|
|
|
$supervisor_config_data = implode("\n", $supervisor_config);
|
|
|
|
file_put_contents($supervisor_config_path, $supervisor_config_data);
|
|
|
|
|
2017-04-08 19:34:59 +00:00
|
|
|
$this->_reloadSupervisor($di['supervisor']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove configuration (i.e. prior to station removal) and trigger a Supervisor refresh.
|
|
|
|
* @param ContainerInterface $di
|
|
|
|
*/
|
|
|
|
public function removeConfiguration(ContainerInterface $di)
|
|
|
|
{
|
|
|
|
if (APP_TESTING_MODE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$config_path = $this->getRadioConfigDir();
|
|
|
|
$supervisor_config_path = $config_path . '/supervisord.conf';
|
|
|
|
|
|
|
|
@unlink($supervisor_config_path);
|
|
|
|
|
|
|
|
$this->_reloadSupervisor($di['supervisor']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger a supervisord reload and restart all relevant services.
|
|
|
|
* @param \Supervisor\Supervisor $supervisor
|
|
|
|
*/
|
|
|
|
protected function _reloadSupervisor(\Supervisor\Supervisor $supervisor)
|
|
|
|
{
|
2016-12-12 23:01:28 +00:00
|
|
|
$reload_result = $supervisor->reloadConfig();
|
|
|
|
|
|
|
|
$reload_added = $reload_result[0][0];
|
|
|
|
$reload_changed = $reload_result[0][1];
|
|
|
|
$reload_removed = $reload_result[0][2];
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
foreach ($reload_removed as $group) {
|
2016-12-12 23:01:28 +00:00
|
|
|
$supervisor->stopProcessGroup($group);
|
|
|
|
$supervisor->removeProcessGroup($group);
|
|
|
|
}
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
foreach ($reload_changed as $group) {
|
2016-12-12 23:01:28 +00:00
|
|
|
$supervisor->stopProcessGroup($group);
|
|
|
|
$supervisor->removeProcessGroup($group);
|
|
|
|
$supervisor->addProcessGroup($group);
|
|
|
|
}
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
foreach ($reload_added as $group) {
|
2016-12-12 23:01:28 +00:00
|
|
|
$supervisor->addProcessGroup($group);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-08 19:34:59 +00:00
|
|
|
|
|
|
|
|
2015-01-02 08:16:54 +00:00
|
|
|
/**
|
2017-01-24 00:17:50 +00:00
|
|
|
* Static Functions
|
2016-09-27 20:39:54 +00:00
|
|
|
*/
|
2017-01-24 00:17:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $name
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public static function getStationShortName($name)
|
2015-01-02 08:16:54 +00:00
|
|
|
{
|
2017-01-24 00:17:50 +00:00
|
|
|
return strtolower(preg_replace("/[^A-Za-z0-9_]/", '', str_replace(' ', '_', trim($name))));
|
|
|
|
}
|
2016-09-06 06:25:41 +00:00
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
/**
|
|
|
|
* @param $name
|
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public static function getStationClassName($name)
|
|
|
|
{
|
|
|
|
$name = preg_replace("/[^A-Za-z0-9_ ]/", '', $name);
|
|
|
|
$name = str_replace('_', ' ', $name);
|
|
|
|
$name = str_replace(' ', '', $name);
|
2017-01-24 00:35:16 +00:00
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
return $name;
|
|
|
|
}
|
2016-05-06 08:57:34 +00:00
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function getFrontendAdapters()
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'default' => 'icecast',
|
|
|
|
'adapters' => [
|
|
|
|
'icecast' => [
|
|
|
|
'name' => 'IceCast 2.4',
|
|
|
|
'class' => '\AzuraCast\Radio\Frontend\IceCast',
|
|
|
|
],
|
|
|
|
'shoutcast2' => [
|
|
|
|
'name' => 'ShoutCast 2',
|
|
|
|
'class' => '\AzuraCast\Radio\Frontend\ShoutCast2',
|
|
|
|
],
|
|
|
|
'remote' => [
|
|
|
|
'name' => _('External Radio Server (Statistics Only)'),
|
|
|
|
'class' => '\AzuraCast\Radio\Frontend\Remote',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
];
|
2016-05-06 08:57:34 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 20:39:54 +00:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2016-05-06 08:57:34 +00:00
|
|
|
public static function getBackendAdapters()
|
|
|
|
{
|
2016-11-21 17:56:56 +00:00
|
|
|
return [
|
2016-05-06 08:57:34 +00:00
|
|
|
'default' => 'liquidsoap',
|
2016-11-21 17:56:56 +00:00
|
|
|
'adapters' => [
|
|
|
|
'liquidsoap' => [
|
|
|
|
'name' => 'LiquidSoap',
|
2016-12-16 20:49:07 +00:00
|
|
|
'class' => '\AzuraCast\Radio\Backend\LiquidSoap',
|
2016-11-21 17:56:56 +00:00
|
|
|
],
|
|
|
|
'none' => [
|
|
|
|
'name' => _('Disabled'),
|
2016-12-16 20:49:07 +00:00
|
|
|
'class' => '\AzuraCast\Radio\Backend\None',
|
2016-11-21 17:56:56 +00:00
|
|
|
],
|
|
|
|
],
|
|
|
|
];
|
2016-05-06 08:57:34 +00:00
|
|
|
}
|
2017-01-24 00:17:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the API version of the object/array.
|
|
|
|
*
|
|
|
|
* @param $row
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function api(Station $row, ContainerInterface $di)
|
|
|
|
{
|
|
|
|
$fa = $row->getFrontendAdapter($di);
|
|
|
|
|
|
|
|
$api = [
|
2017-01-24 00:35:16 +00:00
|
|
|
'id' => (int)$row->id,
|
2017-05-09 07:33:40 +00:00
|
|
|
'name' => (string)$row->name,
|
|
|
|
'shortcode' => (string)self::getStationShortName($row['name']),
|
|
|
|
'description' => (string)$row->description,
|
|
|
|
'frontend' => (string)$row->frontend_type,
|
|
|
|
'backend' => (string)$row->backend_type,
|
|
|
|
'listen_url' => (string)$fa->getStreamUrl(),
|
2017-01-24 00:35:16 +00:00
|
|
|
'mounts' => [],
|
2017-01-24 00:17:50 +00:00
|
|
|
];
|
|
|
|
|
2017-01-24 00:35:16 +00:00
|
|
|
if ($row->mounts->count() > 0) {
|
|
|
|
if ($fa->supportsMounts()) {
|
|
|
|
foreach ($row->mounts as $mount_row) {
|
2017-01-24 00:17:50 +00:00
|
|
|
$api['mounts'][] = [
|
2017-05-09 07:33:40 +00:00
|
|
|
'name' => (string)$mount_row->name,
|
2017-01-24 00:17:50 +00:00
|
|
|
'is_default' => (bool)$mount_row->is_default,
|
2017-05-09 07:33:40 +00:00
|
|
|
'url' => (string)$fa->getUrlForMount($mount_row->name),
|
2017-01-24 00:17:50 +00:00
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $api;
|
|
|
|
}
|
2014-02-21 09:25:10 +00:00
|
|
|
}
|