Merge branch 'saas' into saas-suspend
This commit is contained in:
commit
1964cbb75e
|
@ -37,7 +37,8 @@ $ccAcl->add(new Zend_Acl_Resource('library'))
|
|||
->add(new Zend_Acl_Resource('rest:show-image'))
|
||||
->add(new Zend_Acl_Resource('billing'))
|
||||
->add(new Zend_Acl_Resource('thank-you'))
|
||||
->add(new Zend_Acl_Resource('provisioning'));
|
||||
->add(new Zend_Acl_Resource('provisioning'))
|
||||
->add(new Zend_Acl_Resource('player'));
|
||||
|
||||
/** Creating permissions */
|
||||
$ccAcl->allow('G', 'index')
|
||||
|
@ -70,6 +71,7 @@ $ccAcl->allow('G', 'index')
|
|||
->allow('A', 'user')
|
||||
->allow('A', 'systemstatus')
|
||||
->allow('A', 'preference')
|
||||
->allow('A', 'player')
|
||||
->allow('S', 'thank-you')
|
||||
->allow('S', 'billing');
|
||||
|
||||
|
|
|
@ -85,6 +85,12 @@ $pages = array(
|
|||
'controller' => 'listenerstat',
|
||||
'action' => 'index',
|
||||
'resource' => 'listenerstat'
|
||||
),
|
||||
array(
|
||||
'label' => _('Player'),
|
||||
'module' => 'default',
|
||||
'controller' => 'player',
|
||||
'action' => 'customize'
|
||||
)
|
||||
)
|
||||
),
|
||||
|
|
|
@ -523,6 +523,7 @@ class ApiController extends Zend_Controller_Action
|
|||
$result["description"] = Application_Model_Preference::GetStationDescription();
|
||||
$result["timezone"] = Application_Model_Preference::GetDefaultTimezone();
|
||||
$result["locale"] = Application_Model_Preference::GetDefaultLocale();
|
||||
$result["stream_data"] = Application_Model_StreamSetting::getEnabledStreamData();
|
||||
|
||||
// used by caller to determine if the airtime they are running or widgets in use is out of date.
|
||||
$result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION;
|
||||
|
@ -1310,7 +1311,7 @@ class ApiController extends Zend_Controller_Action
|
|||
}
|
||||
|
||||
public function getStreamParametersAction() {
|
||||
$streams = array("s1", "s2", "s3");
|
||||
$streams = array("s1", "s2", "s3", "s4");
|
||||
$stream_params = array();
|
||||
foreach ($streams as $s) {
|
||||
$stream_params[$s] =
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
class EmbedController extends Zend_Controller_Action
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the action that is called to insert the player onto a web page.
|
||||
* It passes all the js and css files to the view, as well as all the
|
||||
* stream customization information.
|
||||
*
|
||||
* The view for this action contains all the inline javascript needed to
|
||||
* create the player.
|
||||
*/
|
||||
public function playerAction()
|
||||
{
|
||||
$this->view->layout()->disableLayout();
|
||||
|
||||
$CC_CONFIG = Config::getConfig();
|
||||
|
||||
$request = $this->getRequest();
|
||||
|
||||
$this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/player.css?".$CC_CONFIG['airtime_version'];
|
||||
$this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/mrp.js?".$CC_CONFIG['airtime_version'];
|
||||
$this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js";
|
||||
$this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf";
|
||||
$this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info";
|
||||
$this->view->player_title = json_encode($request->getParam('title'));
|
||||
|
||||
$stream = $request->getParam('stream');
|
||||
$streamData = Application_Model_StreamSetting::getEnabledStreamData();
|
||||
$availableMobileStreams = array();
|
||||
$availableDesktopStreams = array();
|
||||
|
||||
if ($stream == "auto") {
|
||||
$this->view->playerMode = "auto";
|
||||
$this->view->streamURL = json_encode("");
|
||||
foreach ($streamData as $s) {
|
||||
if ($s["mobile"]) {
|
||||
array_push($availableMobileStreams, $s);
|
||||
} else if (!$s["mobile"]) {
|
||||
array_push($availableDesktopStreams, $s);
|
||||
}
|
||||
}
|
||||
} elseif (!empty($stream)) {
|
||||
$this->view->playerMode = "manual";
|
||||
$selectedStreamData = $streamData[$stream];
|
||||
$this->view->streamURL = json_encode($selectedStreamData["url"]);
|
||||
$this->view->codec = $selectedStreamData["codec"];
|
||||
}
|
||||
$this->view->availableMobileStreams = json_encode($availableMobileStreams);
|
||||
$this->view->availableDesktopStreams = json_encode($availableDesktopStreams);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
class PlayerController extends Zend_Controller_Action
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function customizeAction()
|
||||
{
|
||||
$CC_CONFIG = Config::getConfig();
|
||||
$baseUrl = Application_Common_OsPath::getBaseDir();
|
||||
$this->view->headLink()->appendStylesheet($baseUrl.'css/player-form.css?'.$CC_CONFIG['airtime_version']);
|
||||
$this->view->headScript()->appendFile($baseUrl.'js/airtime/player/player.js?'.$CC_CONFIG['airtime_version']);
|
||||
|
||||
$form = new Application_Form_Player();
|
||||
|
||||
$apiEnabled = Application_Model_Preference::GetAllow3rdPartyApi();
|
||||
$numEnabledStreams = $form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams');
|
||||
|
||||
if ($numEnabledStreams > 0 && $apiEnabled) {
|
||||
$this->view->form = $form;
|
||||
} else {
|
||||
$this->view->errorMsg = "To configure and use the embeddable player you must:<br><br>
|
||||
1. Enable at least one MP3, AAC, or OGG stream under System -> Streams<br>
|
||||
2. Enable the Public Airtime API under System -> Preferences";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -141,11 +141,7 @@ class PreferenceController extends Zend_Controller_Action
|
|||
$this->view->headScript()->appendFile($baseUrl.'js/airtime/preferences/streamsetting.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
|
||||
|
||||
// get current settings
|
||||
$temp = Application_Model_StreamSetting::getStreamSetting();
|
||||
$setting = array();
|
||||
foreach ($temp as $t) {
|
||||
$setting[$t['keyname']] = $t['value'];
|
||||
}
|
||||
$setting = Application_Model_StreamSetting::getStreamSetting();
|
||||
|
||||
$name_map = array(
|
||||
'ogg' => 'Ogg Vorbis',
|
||||
|
@ -208,6 +204,7 @@ class PreferenceController extends Zend_Controller_Action
|
|||
$s1_data = array();
|
||||
$s2_data = array();
|
||||
$s3_data = array();
|
||||
$s4_data = array();
|
||||
$values = array();
|
||||
foreach($postData as $k=>$v) {
|
||||
$v = explode('=', urldecode($v));
|
||||
|
@ -223,6 +220,9 @@ class PreferenceController extends Zend_Controller_Action
|
|||
} elseif (strpos($v[0], "s3_data") !== false) {
|
||||
preg_match('/\[(.*)\]/', $v[0], $matches);
|
||||
$s3_data[$matches[1]] = $v[1];
|
||||
} elseif (strpos($v[0], "s4_data") !== false) {
|
||||
preg_match('/\[(.*)\]/', $v[0], $matches);
|
||||
$s4_data[$matches[1]] = $v[1];
|
||||
} else {
|
||||
$values[$v[0]] = $v[1];
|
||||
}
|
||||
|
@ -230,6 +230,7 @@ class PreferenceController extends Zend_Controller_Action
|
|||
$values["s1_data"] = $s1_data;
|
||||
$values["s2_data"] = $s2_data;
|
||||
$values["s3_data"] = $s3_data;
|
||||
$values["s4_data"] = $s4_data;
|
||||
|
||||
$error = false;
|
||||
if ($form->isValid($values)) {
|
||||
|
@ -245,6 +246,7 @@ class PreferenceController extends Zend_Controller_Action
|
|||
$s1_set_admin_pass = !empty($values["s1_data"]["admin_pass"]);
|
||||
$s2_set_admin_pass = !empty($values["s2_data"]["admin_pass"]);
|
||||
$s3_set_admin_pass = !empty($values["s3_data"]["admin_pass"]);
|
||||
$s4_set_admin_pass = !empty($values["s4_data"]["admin_pass"]);
|
||||
|
||||
// this goes into cc_pref table
|
||||
Application_Model_Preference::SetStreamLabelFormat($values['streamFormat']);
|
||||
|
@ -290,6 +292,7 @@ class PreferenceController extends Zend_Controller_Action
|
|||
"s1_set_admin_pass"=>$s1_set_admin_pass,
|
||||
"s2_set_admin_pass"=>$s2_set_admin_pass,
|
||||
"s3_set_admin_pass"=>$s3_set_admin_pass,
|
||||
"s4_set_admin_pass"=>$s4_set_admin_pass,
|
||||
));
|
||||
} else {
|
||||
$live_stream_subform->updateVariables();
|
||||
|
@ -441,7 +444,8 @@ class PreferenceController extends Zend_Controller_Action
|
|||
public function getAdminPasswordStatusAction()
|
||||
{
|
||||
$out = array();
|
||||
for ($i=1; $i<=3; $i++) {
|
||||
$num_of_stream = intval(Application_Model_Preference::GetNumOfStreams());
|
||||
for ($i=1; $i<=$num_of_stream; $i++) {
|
||||
if (Application_Model_StreamSetting::getAdminPass('s'.$i)=='') {
|
||||
$out["s".$i] = false;
|
||||
} else {
|
||||
|
|
|
@ -118,7 +118,8 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
|
|||
"locale",
|
||||
"upgrade",
|
||||
'whmcs-login',
|
||||
"provisioning"
|
||||
"provisioning",
|
||||
"embed"
|
||||
)))
|
||||
{
|
||||
$this->setRoleName("G");
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
define("OPUS", "opus");
|
||||
|
||||
class Application_Form_Player extends Zend_Form_SubForm
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->setDecorators(array(
|
||||
array('ViewScript', array('viewScript' => 'form/player.phtml'))
|
||||
));
|
||||
|
||||
$title = new Zend_Form_Element_Text('player_title');
|
||||
$title->setValue(_('Now Playing'));
|
||||
$title->setLabel(_('Title:'));
|
||||
$title->setDecorators(array(
|
||||
'ViewHelper',
|
||||
'Errors',
|
||||
'Label'
|
||||
));
|
||||
$title->addDecorator('Label', array('class' => 'player-title'));
|
||||
$this->addElement($title);
|
||||
|
||||
$streamMode = new Zend_Form_Element_Radio('player_stream_mode');
|
||||
$streamMode->setLabel(_('Select Stream:'));
|
||||
$streamMode->setMultiOptions(
|
||||
array(
|
||||
"auto" => _("Auto detect the most appropriate stream to use."),
|
||||
"manual" => _("Select a stream:")
|
||||
)
|
||||
);
|
||||
$streamMode->setValue("auto");
|
||||
$this->addElement($streamMode);
|
||||
|
||||
$streamURL = new Zend_Form_Element_Radio('player_stream_url');
|
||||
$opusStreamCount = 0;
|
||||
$urlOptions = Array();
|
||||
foreach(Application_Model_StreamSetting::getEnabledStreamData() as $stream => $data) {
|
||||
$urlOptions[$stream] = strtoupper($data["codec"])." - ".$data["bitrate"]."kbps";
|
||||
if ($data["mobile"]) {
|
||||
$urlOptions[$stream] .= _(" - Mobile friendly");
|
||||
}
|
||||
if ($data["codec"] == OPUS) {
|
||||
$opusStreamCount += 1;
|
||||
$urlOptions[$stream] .= _(" - The player does not support Opus streams.");
|
||||
}
|
||||
}
|
||||
$streamURL->setMultiOptions(
|
||||
$urlOptions
|
||||
);
|
||||
|
||||
// Set default value to the first non-opus stream we find
|
||||
foreach ($urlOptions as $o => $v) {
|
||||
if (strpos(strtolower($v), OPUS) !== false) {
|
||||
continue;
|
||||
} else {
|
||||
$streamURL->setValue($o);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$streamURL->setAttrib('numberOfEnabledStreams', sizeof($urlOptions)-$opusStreamCount);
|
||||
$streamURL->removeDecorator('label');
|
||||
$this->addElement($streamURL);
|
||||
|
||||
$embedSrc = new Zend_Form_Element_Textarea('player_embed_src');
|
||||
$embedSrc->setAttrib("readonly", "readonly");
|
||||
$embedSrc->setAttrib("class", "embed-player-text-box");
|
||||
$embedSrc->setAttrib('cols', '40')
|
||||
->setAttrib('rows', '4');
|
||||
$embedSrc->setLabel(_("Embeddable code:"));
|
||||
$embedSrc->setDescription(_("Copy this code and paste it into your website's HTML to embed the player in your site."));
|
||||
$embedSrc->setValue('<iframe frameborder="0" width="280" height="216" src="'.Application_Common_HTTPHelper::getStationUrl().'embed/player?stream=auto&title=Now Playing"></iframe>');
|
||||
$this->addElement($embedSrc);
|
||||
|
||||
$previewLabel = new Zend_Form_Element_Text('player_preview_label');
|
||||
$previewLabel->setLabel(_("Preview:"));
|
||||
$this->addElement($previewLabel);
|
||||
|
||||
}
|
||||
}
|
|
@ -53,6 +53,12 @@ class Application_Form_StreamSettingSubForm extends Zend_Form_SubForm
|
|||
}
|
||||
$this->addElement($enable);
|
||||
|
||||
$mobile = new Zend_Form_Element_Checkbox('mobile');
|
||||
$mobile->setLabel(_('Mobile:'));
|
||||
$mobile->setValue($setting[$prefix.'_mobile']);
|
||||
$mobile->setDecorators(array('ViewHelper'));
|
||||
$this->addElement($mobile);
|
||||
|
||||
$type = new Zend_Form_Element_Select('type');
|
||||
$type->setLabel(_("Stream Type:"))
|
||||
->setMultiOptions($stream_types)
|
||||
|
|
|
@ -12,4 +12,18 @@ class Airtime_Decorator_SuperAdmin_Only extends Zend_Form_Decorator_Abstract
|
|||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide a Zend_Form_Element unless you're logged in as an Admin or SuperAdmin. */
|
||||
class Airtime_Decorator_Admin_Only extends Zend_Form_Decorator_Abstract
|
||||
{
|
||||
public function render($content)
|
||||
{
|
||||
$currentUser = Application_Model_User::getCurrentUser();
|
||||
if ($currentUser->isSuperAdmin() || $currentUser->isAdmin()) {
|
||||
return $content;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,8 +37,6 @@ class Application_Model_Preference
|
|||
if ($isUserValue && is_null($userId))
|
||||
throw new Exception("User id can't be null for a user preference {$key}.");
|
||||
|
||||
Application_Common_Database::prepareAndExecute("LOCK TABLE cc_pref");
|
||||
|
||||
//Check if key already exists
|
||||
$sql = "SELECT COUNT(*) FROM cc_pref"
|
||||
." WHERE keystr = :key";
|
||||
|
|
|
@ -828,7 +828,8 @@ class Application_Model_Scheduler
|
|||
"fade_in = '{$file["fadein"]}', ".
|
||||
"fade_out = '{$file["fadeout"]}', ".
|
||||
"clip_length = '{$file["cliplength"]}', ".
|
||||
"position = {$pos} ".
|
||||
"position = {$pos}, ".
|
||||
"instance_id = {$instanceId} ".
|
||||
"WHERE id = {$sched["id"]}";
|
||||
|
||||
Application_Common_Database::prepareAndExecute(
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
define("MAX_NUM_STREAMS", 4);
|
||||
|
||||
class Application_Model_StreamSetting
|
||||
{
|
||||
public static function setValue($key, $value, $type)
|
||||
|
@ -41,7 +44,7 @@ class Application_Model_StreamSetting
|
|||
}
|
||||
}
|
||||
|
||||
public static function getValue($key)
|
||||
public static function getValue($key, $default="")
|
||||
{
|
||||
$con = Propel::getConnection();
|
||||
|
||||
|
@ -59,7 +62,27 @@ class Application_Model_StreamSetting
|
|||
throw new Exception("Error: $msg");
|
||||
}
|
||||
|
||||
return $result ? $result : "";
|
||||
return $result ? $result : $default;
|
||||
}
|
||||
|
||||
public static function getEnabledStreamData()
|
||||
{
|
||||
$streams = Array();
|
||||
$streamIds = self::getEnabledStreamIds();
|
||||
foreach ($streamIds as $id) {
|
||||
$streamData = self::getStreamData($id);
|
||||
$prefix = $id."_";
|
||||
$host = $streamData[$prefix."host"];
|
||||
$port = $streamData[$prefix."port"];
|
||||
$mount = $streamData[$prefix."mount"];
|
||||
$streams[$id] = Array(
|
||||
"url" => "http://$host:$port/$mount",
|
||||
"codec" => $streamData[$prefix."type"],
|
||||
"bitrate" => $streamData[$prefix."bitrate"],
|
||||
"mobile" => $streamData[$prefix."mobile"]
|
||||
);
|
||||
}
|
||||
return $streams;
|
||||
}
|
||||
|
||||
/* Returns the id's of all streams that are enabled in an array. An
|
||||
|
@ -83,50 +106,63 @@ class Application_Model_StreamSetting
|
|||
return $ids;
|
||||
}
|
||||
|
||||
/* Returns only global data as array*/
|
||||
public static function getGlobalData()
|
||||
{
|
||||
$con = Propel::getConnection();
|
||||
$sql = "SELECT * "
|
||||
."FROM cc_stream_setting "
|
||||
."WHERE keyname IN ('output_sound_device', 'icecast_vorbis_metadata')";
|
||||
|
||||
$rows = Application_Common_Database::prepareAndExecute($sql, array(), 'all');
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$data[$row["keyname"]] = $row["value"];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/* Returns all information related to a specific stream. An example
|
||||
* of a stream id is 's1' or 's2'. */
|
||||
public static function getStreamData($p_streamId)
|
||||
{
|
||||
$con = Propel::getConnection();
|
||||
$streamId = pg_escape_string($p_streamId);
|
||||
$sql = "SELECT * "
|
||||
."FROM cc_stream_setting "
|
||||
."WHERE keyname LIKE '{$streamId}_%'";
|
||||
|
||||
$stmt = $con->prepare($sql);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$rows = $stmt->fetchAll();
|
||||
} else {
|
||||
$msg = implode(',', $stmt->errorInfo());
|
||||
throw new Exception("Error: $msg");
|
||||
}
|
||||
$rows = CcStreamSettingQuery::create()
|
||||
->filterByDbKeyName("${p_streamId}_%")
|
||||
->find();
|
||||
|
||||
//This is way too much code because someone made only stupid decisions about how
|
||||
//the layout of this table worked. The git history doesn't lie.
|
||||
$data = array();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$data[$row["keyname"]] = $row["value"];
|
||||
$key = $row->getDbKeyName();
|
||||
$value = $row->getDbValue();
|
||||
$type = $row->getDbType();
|
||||
//Fix stupid defaults so we end up with proper typing in our JSON
|
||||
if ($row->getDbType() == "boolean") {
|
||||
if (empty($value)) {
|
||||
//In Python, there is no way to tell the difference between ints and booleans,
|
||||
//which we need to differentiate between for when we're generating the Liquidsoap
|
||||
//config file. Returning booleans as a string is a workaround that lets us do that.
|
||||
$value = "false";
|
||||
}
|
||||
$data[$key] = $value;
|
||||
}
|
||||
elseif ($row->getDbType() == "integer") {
|
||||
if (empty($value)) {
|
||||
$value = 0;
|
||||
}
|
||||
$data[$key] = intval($value);
|
||||
}
|
||||
else {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
//Add in defaults in case they don't exist in the database.
|
||||
$keyPrefix = $p_streamId . '_';
|
||||
self::ensureKeyExists($keyPrefix . 'admin_pass', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'admin_user', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'bitrate', $data, 128);
|
||||
self::ensureKeyExists($keyPrefix . 'channels', $data, "stereo");
|
||||
self::ensureKeyExists($keyPrefix . 'description', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'enable', $data, "false");
|
||||
self::ensureKeyExists($keyPrefix . 'genre', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'host', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'liquidsoap_error', $data, "waiting");
|
||||
self::ensureKeyExists($keyPrefix . 'mount', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'name', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'output', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'pass', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'port', $data, 8000);
|
||||
self::ensureKeyExists($keyPrefix . 'type', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'url', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'user', $data);
|
||||
self::ensureKeyExists($keyPrefix . 'mobile', $data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -134,76 +170,41 @@ class Application_Model_StreamSetting
|
|||
* make data easier to iterate over */
|
||||
public static function getStreamDataNormalized($p_streamId)
|
||||
{
|
||||
$con = Propel::getConnection();
|
||||
$streamId = pg_escape_string($p_streamId);
|
||||
$sql = "SELECT * "
|
||||
."FROM cc_stream_setting "
|
||||
."WHERE keyname LIKE '{$streamId}_%'";
|
||||
|
||||
$stmt = $con->prepare($sql);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$rows = $stmt->fetchAll();
|
||||
} else {
|
||||
$msg = implode(',', $stmt->errorInfo());
|
||||
throw new Exception("Error: $msg");
|
||||
$settings = self::getStreamData($p_streamId);
|
||||
foreach ($settings as $key => $value)
|
||||
{
|
||||
unset($settings[$key]);
|
||||
$newKey = substr($key, strlen($p_streamId)+1); //$p_streamId is assumed to be the key prefix.
|
||||
$settings[$newKey] = $value;
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
list($id, $key) = explode("_", $row["keyname"], 2);
|
||||
$data[$key] = $row["value"];
|
||||
private static function ensureKeyExists($key, &$array, $default='')
|
||||
{
|
||||
if (!array_key_exists($key, $array)) {
|
||||
$array[$key] = $default;
|
||||
}
|
||||
|
||||
return $data;
|
||||
return $array;
|
||||
}
|
||||
|
||||
public static function getStreamSetting()
|
||||
{
|
||||
$con = Propel::getConnection();
|
||||
$sql = "SELECT *"
|
||||
." FROM cc_stream_setting"
|
||||
." WHERE keyname not like '%_error' AND keyname not like '%_admin_%'";
|
||||
|
||||
$rows = Application_Common_Database::prepareAndExecute($sql, array(), 'all');
|
||||
|
||||
$exists = array();
|
||||
|
||||
foreach ($rows as $r) {
|
||||
if ($r['keyname'] == 'master_live_stream_port') {
|
||||
$exists['master_live_stream_port'] = true;
|
||||
} elseif ($r['keyname'] == 'master_live_stream_mp') {
|
||||
$exists['master_live_stream_mp'] = true;
|
||||
} elseif ($r['keyname'] == 'dj_live_stream_port') {
|
||||
$exists['dj_live_stream_port'] = true;
|
||||
} elseif ($r['keyname'] == 'dj_live_stream_mp') {
|
||||
$exists['dj_live_stream_mp'] = true;
|
||||
}
|
||||
$settings = array();
|
||||
$numStreams = MAX_NUM_STREAMS;
|
||||
for ($streamIdx = 1; $streamIdx <= $numStreams; $streamIdx++)
|
||||
{
|
||||
$settings = array_merge($settings, self::getStreamData("s" . $streamIdx));
|
||||
}
|
||||
|
||||
if (!isset($exists["master_live_stream_port"])) {
|
||||
$rows[] = array("keyname" =>"master_live_stream_port",
|
||||
"value"=>self::getMasterLiveStreamPort(),
|
||||
"type"=>"integer");
|
||||
}
|
||||
if (!isset($exists["master_live_stream_mp"])) {
|
||||
$rows[] = array("keyname" =>"master_live_stream_mp",
|
||||
"value"=>self::getMasterLiveStreamMountPoint(),
|
||||
"type"=>"string");
|
||||
}
|
||||
if (!isset($exists["dj_live_stream_port"])) {
|
||||
$rows[] = array("keyname" =>"dj_live_stream_port",
|
||||
"value"=>self::getDjLiveStreamPort(),
|
||||
"type"=>"integer");
|
||||
}
|
||||
if (!isset($exists["dj_live_stream_mp"])) {
|
||||
$rows[] = array("keyname" =>"dj_live_stream_mp",
|
||||
"value"=>self::getDjLiveStreamMountPoint(),
|
||||
"type"=>"string");
|
||||
}
|
||||
|
||||
return $rows;
|
||||
$settings["master_live_stream_port"] = self::getMasterLiveStreamPort();
|
||||
$settings["master_live_stream_mp"] = self::getMasterLiveStreamMountPoint();
|
||||
$settings["dj_live_stream_port"] = self::getDjLiveStreamPort();
|
||||
$settings["dj_live_stream_mp"] = self::getDjLiveStreamMountPoint();
|
||||
$settings["off_air_meta"] = self::getOffAirMeta();
|
||||
$settings["icecast_vorbis_metadata"] = self::getIcecastVorbisMetadata();
|
||||
$settings["output_sound_device"] = self::getOutputSoundDevice();
|
||||
$settings["output_sound_device_type"] = self::getOutputSoundDeviceType();
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
|
@ -211,7 +212,10 @@ class Application_Model_StreamSetting
|
|||
{
|
||||
$stream_setting = CcStreamSettingQuery::create()->filterByDbKeyName($key)->findOne();
|
||||
if (is_null($stream_setting)) {
|
||||
throw new Exception("Keyname $key does not exist!");
|
||||
//throw new Exception("Keyname $key does not exist!");
|
||||
$stream_setting = new CcStreamSetting();
|
||||
$stream_setting->setDbKeyName($key);
|
||||
$stream_setting->setDbType("");
|
||||
}
|
||||
|
||||
$stream_setting->setDbValue($value);
|
||||
|
@ -411,7 +415,7 @@ class Application_Model_StreamSetting
|
|||
|
||||
public static function getMasterLiveStreamPort()
|
||||
{
|
||||
return self::getValue("master_live_stream_port");
|
||||
return self::getValue("master_live_stream_port", 8001);
|
||||
}
|
||||
|
||||
public static function setMasterLiveStreamMountPoint($value)
|
||||
|
@ -421,7 +425,7 @@ class Application_Model_StreamSetting
|
|||
|
||||
public static function getMasterLiveStreamMountPoint()
|
||||
{
|
||||
return self::getValue("master_live_stream_mp");
|
||||
return self::getValue("master_live_stream_mp", "/master");
|
||||
}
|
||||
|
||||
public static function setDjLiveStreamPort($value)
|
||||
|
@ -431,7 +435,7 @@ class Application_Model_StreamSetting
|
|||
|
||||
public static function getDjLiveStreamPort()
|
||||
{
|
||||
return self::getValue("dj_live_stream_port");
|
||||
return self::getValue("dj_live_stream_port", 8001);
|
||||
}
|
||||
|
||||
public static function setDjLiveStreamMountPoint($value)
|
||||
|
@ -441,7 +445,7 @@ class Application_Model_StreamSetting
|
|||
|
||||
public static function getDjLiveStreamMountPoint()
|
||||
{
|
||||
return self::getValue("dj_live_stream_mp");
|
||||
return self::getValue("dj_live_stream_mp", "/show");
|
||||
}
|
||||
|
||||
public static function getAdminUser($stream){
|
||||
|
@ -488,4 +492,16 @@ class Application_Model_StreamSetting
|
|||
public static function SetListenerStatError($key, $v) {
|
||||
self::setValue($key, $v, 'string');
|
||||
}
|
||||
|
||||
public static function getIcecastVorbisMetadata() {
|
||||
return self::getValue("icecast_vorbis_metadata", "");
|
||||
}
|
||||
|
||||
public static function getOutputSoundDevice() {
|
||||
return self::getValue("output_sound_device", "false");
|
||||
}
|
||||
|
||||
public static function getOutputSoundDeviceType() {
|
||||
return self::getValue("output_sound_device_type", "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -430,6 +430,7 @@ class Application_Service_ShowFormService
|
|||
->filterByDbShowId($this->ccShow->getDbId())
|
||||
->filterByDbModifiedInstance(false)
|
||||
->filterByDbStarts(gmdate("Y-m-d H:i:s"), Criteria::GREATER_THAN)
|
||||
->orderByDbStarts()
|
||||
->findOne();
|
||||
|
||||
$starts = new DateTime($ccShowInstance->getDbStarts(), new DateTimeZone("UTC"));
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="<?php echo $this->css?>" type="text/css">
|
||||
<script src="<?php echo $this->mrp_js?>" type="text/javascript"></script>
|
||||
<script src="<?php echo $this->jquery?>" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var MAX_MOBILE_SCREEN_WIDTH = 760;
|
||||
|
||||
// We are creating a custom player object that acts as a wrapper
|
||||
// around the player object we get from Muses. We are doing this
|
||||
// for a couple reasons:
|
||||
//
|
||||
// 1. It will be easier to swap out Muses for a different player engine
|
||||
// in the future - if we ever decide to do that.
|
||||
// 2. We had to add in some custom behaviour depending on the player
|
||||
// customizations and whether or not the player is in Flash or HTML5
|
||||
// mode.
|
||||
var MusesPlayer = function() {
|
||||
this.mobileDetect = this.mobileDetect();
|
||||
this.playerMode = "<?php echo $this->playerMode ?>";
|
||||
this.flashDetect = FlashDetect.versionAtLeast(10, 1) ? true : false;
|
||||
this.settings = {
|
||||
'volume': 100,
|
||||
'jsevents': true,
|
||||
'autoplay': false,
|
||||
'buffering': 0,
|
||||
'title': 'test',
|
||||
'bgcolor': '#FFFFFF',
|
||||
'skin': 'mcclean',
|
||||
'width': 180,
|
||||
'height': 60
|
||||
};
|
||||
|
||||
if (this.playerMode == "manual") {
|
||||
this.settings.url = <?php echo $this->streamURL ?>;
|
||||
this.settings.codec = "<?php echo $this->codec ?>";
|
||||
} else if (this.playerMode == "auto") {
|
||||
this.availableMobileStreamQueue = <?php echo $this->availableMobileStreams?>;
|
||||
this.availableDesktopStreamQueue = <?php echo $this->availableDesktopStreams?>;
|
||||
var stream = this.getNextAvailableStream();
|
||||
this.settings.url = stream["url"];
|
||||
this.settings.codec = stream["codec"];
|
||||
}
|
||||
|
||||
// Create the Muses player object
|
||||
MRP.insert(this.settings);
|
||||
|
||||
// Configure player title
|
||||
var player_title = <?php echo $this->player_title?>;
|
||||
if (player_title === null) {
|
||||
$(".airtime_header").hide();
|
||||
$(".airtime_player").css('height', '150px');
|
||||
} else {
|
||||
$("p.station_name").html(player_title);
|
||||
}
|
||||
|
||||
attachStreamMetadataToPlayer();
|
||||
|
||||
// detects events in HTML5 mode
|
||||
if (!this.flashDetect) {
|
||||
|
||||
MRP.html.audio.addEventListener('error', function failed(e) {
|
||||
switch (e.target.error.code) {
|
||||
case e.target.error.MEDIA_ERR_NETWORK:
|
||||
// If there is a network error keep retrying to connect
|
||||
// to a stream.
|
||||
var stream;
|
||||
if (musesPlayer.playerMode == "auto") {
|
||||
var nextAvailableStream = musesPlayer.getNextAvailableStream();
|
||||
stream = nextAvailableStream["url"];
|
||||
} else {
|
||||
stream = musesPlayer.settings.url;
|
||||
}
|
||||
var audio = $(MRP.html.audio);
|
||||
audio[0].src = stream;
|
||||
audio[0].load();
|
||||
audio[0].play();
|
||||
break;
|
||||
case e.target.error.MEDIA_ERR_DECODE:
|
||||
// If there was a corruption error or a problem with the browser
|
||||
// display an error and stop playback.
|
||||
togglePlayStopButton();
|
||||
clearTimeout(metadataTimer);
|
||||
$("p.now_playing").html("Error - Try again later");
|
||||
break;
|
||||
case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
|
||||
// If in auto mode and the current stream format is not supported
|
||||
// or the max number of listeners has been reached
|
||||
// retry connection with the next available stream.
|
||||
if (musesPlayer.playerMode == "auto") {
|
||||
var nextAvailableStream = musesPlayer.getNextAvailableStream();
|
||||
var audio = $(MRP.html.audio);
|
||||
audio[0].src = nextAvailableStream["url"];;
|
||||
audio[0].load();
|
||||
audio[0].play();
|
||||
} else {
|
||||
// If in manual mode and the current stream format is not supported
|
||||
// or the max number of listeners has been reached
|
||||
// display an error and stop play back.
|
||||
togglePlayStopButton();
|
||||
clearTimeout(metadataTimer);
|
||||
$("p.now_playing").html("Error - Try again later");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
togglePlayStopButton();
|
||||
clearTimeout(metadataTimer);
|
||||
$("p.now_playing").html("Error - Try again later");
|
||||
break;
|
||||
}
|
||||
|
||||
}, true);
|
||||
}
|
||||
};
|
||||
|
||||
MusesPlayer.prototype.mobileDetect = function() {
|
||||
return (screen.width <= MAX_MOBILE_SCREEN_WIDTH);
|
||||
}
|
||||
|
||||
// This function is called if an error occurs while a client is
|
||||
// attempting to connect to a stream (An error would occur if
|
||||
// the streams listener count has been maxed out or if the stream is down).
|
||||
// It checks if the client is a mobile device or not and returns the next
|
||||
// best available stream.
|
||||
MusesPlayer.prototype.getNextAvailableStream = function() {
|
||||
if (this.mobileDetect && this.availableMobileStreamQueue.length > 0) {
|
||||
return this.getNextAvailableMobileStream();
|
||||
}
|
||||
|
||||
if (!this.mobileDetect && this.availableDesktopStreamQueue.length > 0) {
|
||||
return this.getNextAvailableDesktopStream();
|
||||
}
|
||||
|
||||
// If we get to this point there are no available streams for the
|
||||
// type of device the client has connected with so just return
|
||||
// the next available stream - first we'll try the desktop streams
|
||||
var desktopStream = this.getNextAvailableDesktopStream();
|
||||
if (desktopStream) {
|
||||
return desktopStream;
|
||||
} else {
|
||||
return this.getNextAvailableMobileStream();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Gets and returns the next available mobile stream from the queue,
|
||||
// but adds it back to the end of the queue to be recycled.
|
||||
MusesPlayer.prototype.getNextAvailableMobileStream = function() {
|
||||
var stream = this.availableMobileStreamQueue.shift();
|
||||
//add to end of queue
|
||||
this.availableMobileStreamQueue.push(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Gets and returns the next available desktop stream from the queue,
|
||||
// but adds it back to the end of the queue to be recycled.
|
||||
MusesPlayer.prototype.getNextAvailableDesktopStream = function() {
|
||||
var stream = this.availableDesktopStreamQueue.shift();
|
||||
//add to end of queue
|
||||
this.availableDesktopStreamQueue.push(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
MusesPlayer.prototype.play = function() {
|
||||
this.flashDetect ? MRP.play() : musesHTMLPlayClick();;
|
||||
togglePlayStopButton();
|
||||
};
|
||||
|
||||
MusesPlayer.prototype.stop = function() {
|
||||
this.flashDetect ? MRP.stop() : musesHTMLStopClick();
|
||||
togglePlayStopButton();
|
||||
};
|
||||
|
||||
MusesPlayer.prototype.setURL = function(url) {
|
||||
MRP.setUrl(url);
|
||||
};
|
||||
|
||||
// detects errors in FLASH mode
|
||||
function musesCallback(event,value) {
|
||||
switch (event) {
|
||||
case "ioError":
|
||||
// problem connecting to stream
|
||||
var stream;
|
||||
if (musesPlayer.playerMode == "auto") {
|
||||
stream = musesPlayer.getNextAvailableStream();
|
||||
musesPlayer.setURL(stream["url"]);
|
||||
} else {
|
||||
stream = musesPlayer.settings.url;
|
||||
musesPlayer.setURL(stream);
|
||||
}
|
||||
musesPlayer.play();
|
||||
break;
|
||||
case "securityError":
|
||||
// max listeners reached
|
||||
if (musesPlayer.playerMode == "auto") {
|
||||
var stream = musesPlayer.getNextAvailableStream();
|
||||
musesPlayer.setURL(stream["url"]);
|
||||
musesPlayer.play();
|
||||
} else {
|
||||
// If in manual mode and there is a problem connecting to
|
||||
// the stream display an error and stop play back.
|
||||
MRP.stop();
|
||||
if ($("#play_button").hasClass("hide_button")) {
|
||||
togglePlayStopButton();
|
||||
}
|
||||
clearTimeout(metadataTimer);
|
||||
$("p.now_playing").html("Error - Try again later");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Triggers the play function on the Muses player object in HTML5 mode
|
||||
function musesHTMLPlayClick() {
|
||||
/*if (MRP.html === undefined) {
|
||||
console.log("inserting player");
|
||||
MRP.insert(musesPlayer.settings);
|
||||
}*/
|
||||
MRP.html.audio.src = MRP.html.src;
|
||||
|
||||
MRP.html.audio.play();
|
||||
}
|
||||
|
||||
// Triggers the stop function on the Muses player object in HTML5 mode
|
||||
// NOTE: The HTML5 audio element doesn't have stop functionality. It
|
||||
// can only be paused.
|
||||
function musesHTMLStopClick() {
|
||||
MRP.html.audio.pause();
|
||||
//delete MRP.html;
|
||||
}
|
||||
|
||||
function togglePlayStopButton() {
|
||||
document.getElementById("play_button").classList.toggle("hide_button");
|
||||
document.getElementById("stop_button").classList.toggle("hide_button");
|
||||
}
|
||||
|
||||
// variables for updating the player's metadata
|
||||
var time_to_next_track_starts;
|
||||
var metadataTimer;
|
||||
|
||||
// Fetches the streams metadata from the Airtime live-info API
|
||||
// and attaches it to the player UI.
|
||||
//
|
||||
// The metadata is fetched when the current track is about to end.
|
||||
function attachStreamMetadataToPlayer(){
|
||||
$.ajax({url: "<?php echo $this->metadata_api_url?>",
|
||||
data: {type:"interval",limit:"5"},
|
||||
dataType: "jsonp",
|
||||
success: function(data) {
|
||||
|
||||
if (data.current === null) {
|
||||
$("p.now_playing").html("Offline");
|
||||
time_to_next_track_starts = 20000;
|
||||
} else {
|
||||
var artist = data.current.name.split(" - ")[0];
|
||||
var track = data.current.name.split(" - ")[1];
|
||||
$("p.now_playing").html(artist + "<span>" + track + "</span>");
|
||||
|
||||
var current_track_end_time = new Date(data.current.ends);
|
||||
var current_time = new Date();
|
||||
//convert current_time to UTC to match the timezone of time_to_next_track_starts
|
||||
current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset() * 60 * 1000);
|
||||
time_to_next_track_starts = current_track_end_time - current_time;
|
||||
}
|
||||
|
||||
if (data.next === null) {
|
||||
$("ul.schedule_list").find("li").html("Nothing scheduled");
|
||||
} else {
|
||||
$("ul.schedule_list").find("li").html(data.next.name);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
// Add 3 seconds to the timeout so Airtime has time to update the metadata before we fetch it
|
||||
metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts+3000);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
/*
|
||||
We have to have the default Muses skin displayed on the page or else
|
||||
the player will not work. Setting the display to none and hidden does
|
||||
not work. It has to be "visible" on the page. As a hacky work around we
|
||||
set the height and width to 1px so users will not see it.
|
||||
*/
|
||||
#muses_skin{width:1px; height:1px; overflow-x: hidden; overflow-y: hidden;}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="airtime_player">
|
||||
|
||||
<div class="airtime_header">
|
||||
<p class="station_name">fff</p>
|
||||
</div>
|
||||
|
||||
<div class="airtime_box">
|
||||
|
||||
<div class="airtime_button">
|
||||
<span id="play_button" class="play_button" onclick="musesPlayer.play()"></span>
|
||||
<span id="stop_button" class="stop_button hide_button" onclick="musesPlayer.stop()"></span>
|
||||
</div>
|
||||
<p class="now_playing"></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div style="clear:both"></div>
|
||||
|
||||
<div class="airtime_schedule">
|
||||
<p class="airtime_next">Next</p>
|
||||
<ul class="schedule_list">
|
||||
<li></li>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="airtime_pro" target="_blank" href="https://www.airtime.pro/">Powered by <span class="airtime-pro-orange">Airtime Pro</span></a>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="muses_skin">
|
||||
<script type="text/javascript">
|
||||
var musesPlayer = new MusesPlayer();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<fieldset class="padded">
|
||||
<dl class="zend_form">
|
||||
|
||||
<?php echo $this->element->getElement('player_preview_label')->renderLabel(); ?>
|
||||
|
||||
<div style="clear:both"></div>
|
||||
<div class="player-preview">
|
||||
<?php echo $this->element->getElement('player_embed_src')->getValue(); ?>
|
||||
</div>
|
||||
|
||||
<div id="player_instructions">
|
||||
Customize the player by configuring the options below. When you are done,
|
||||
copy the embeddable code below and paste it into your website's HTML.
|
||||
</div>
|
||||
|
||||
<?php echo $this->element->getElement('player_title')->render(); ?>
|
||||
|
||||
<?php echo $this->element->getElement('player_stream_mode')->render(); ?>
|
||||
|
||||
<?php echo $this->element->getElement('player_stream_url'); ?>
|
||||
|
||||
<?php echo $this->element->getElement('player_embed_src')->render(); ?>
|
||||
|
||||
<?php //echo $this->element->getElement('player_display_track_metadata'); ?>
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
</fieldset>
|
|
@ -14,6 +14,13 @@
|
|||
<dd id="<?php echo $s_name?>Enabled-element">
|
||||
<?php echo $this->element->getElement('enable')?>
|
||||
</dd>
|
||||
|
||||
<dt id="<?php echo $s_name?>Mobile-label">
|
||||
<label for="<?php echo $s_name?>Mobile"><?php echo $this->element->getElement('mobile')->getLabel() ?></label>
|
||||
</dt>
|
||||
<dd id="<?php echo $s_name?>Mobile-element">
|
||||
<?php echo $this->element->getElement('mobile')?>
|
||||
</dd>
|
||||
|
||||
<dt id="<?php echo $s_name?>Type-label">
|
||||
<label for="<?php echo $s_name?>Type"><?php echo $this->element->getElement('type')->getLabel()?></label>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<div class="ui-widget ui-widget-content block-shadow simple-formblock embed-player-form clearfix padded-strong ">
|
||||
|
||||
<?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?>
|
||||
|
||||
<form method="post" id="player_form" enctype="multipart/form-data">
|
||||
<h2 style="float:left"><?php echo _("Embeddable Player") ?></h2>
|
||||
<div style="clear:both"></div>
|
||||
<?php echo $this->errorMsg; ?>
|
||||
<?php echo $this->form; ?>
|
||||
|
||||
</form>
|
||||
</div>
|
|
@ -358,3 +358,21 @@ INSERT INTO cc_pref (subjid, keystr, valstr) VALUES (1, 'user_timezone', 'UTC');
|
|||
INSERT INTO cc_pref (keystr, valstr) VALUES ('import_timestamp', '0');
|
||||
|
||||
--end added in 2.5.2
|
||||
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_enable', 'false', 'boolean');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_output', 'icecast', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_name', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_type', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_bitrate', '', 'integer');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_host', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_port', '', 'integer');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_user', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_pass', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_user', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_admin_pass', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_mount', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_url', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_description', '', 'string');
|
||||
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s4_genre', '', 'string');
|
||||
INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s4_channels', 'stereo', 'string');
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,62 @@
|
|||
.embed-player-text-box {
|
||||
padding-right: 0px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
.embed-player-form {
|
||||
width: 40%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.embed-player-form dd {
|
||||
width: 100% !important;
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 4px 0px 4px 0px;
|
||||
}
|
||||
.player-preview {
|
||||
width: 100%;
|
||||
}
|
||||
.player-preview iframe {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
#player_form {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#player_form dd {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#player_instructions {
|
||||
border-bottom: 1px solid;
|
||||
padding-bottom: 10px;
|
||||
font-size: 14px;
|
||||
/*font-weight: bold;*/
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.player-title {
|
||||
clear: left;
|
||||
color: #5b5b5b;
|
||||
float: left;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
width: 40px;
|
||||
padding: 4px 0;
|
||||
text-align: left;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 17px;
|
||||
}
|
||||
#player_title {
|
||||
clear: left;
|
||||
}
|
||||
#player_stream_url-element {
|
||||
margin-left:30px;
|
||||
}
|
||||
#player_embed_src-element p {
|
||||
margin: 0px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
.airtime_player {
|
||||
width: 270px;
|
||||
height: 191px;
|
||||
position: relative;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #fff;
|
||||
background: rgba(53, 53, 53, 0.9);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset;
|
||||
-moz-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset;
|
||||
box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset;
|
||||
}
|
||||
|
||||
.airtime_header {
|
||||
background: rgba(53, 53, 53, 0.9);
|
||||
-webkit-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset;
|
||||
-moz-box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset;
|
||||
box-shadow: 0 3px 5px rgba(0,0,0,0.1) inset,0 1px 0 rgba(255,255,255,0.1),0 0 1px #000 inset;
|
||||
height: 37px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.airtime_box {
|
||||
margin-top: 15px;
|
||||
float: left;
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
}
|
||||
.station_name {
|
||||
font-size: 14px;
|
||||
padding-top: 10px;
|
||||
padding-left: 20px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 30px;
|
||||
}
|
||||
.airtime_pro {
|
||||
margin: 6px 20px;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
text-decoration: none;
|
||||
display:inline-block;
|
||||
float:right;
|
||||
}
|
||||
.airtime_pro_logo {
|
||||
background: url("embed-player-images/airtime_logo.png") center no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
/*.airtime_pro span {
|
||||
display: inline-block;
|
||||
vertical-align: 2px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.airtime_pro:hover span {
|
||||
display: inline-block;
|
||||
vertical-align: 2px;
|
||||
margin-right: 5px;
|
||||
}*/
|
||||
.airtime_box .airtime_button {
|
||||
text-indent: -9999px;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
background: rgb(100,100,100);
|
||||
background: -moz-linear-gradient(top, rgba(107, 107, 107, 1) 0%, rgba(88,88,88,1) 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(100,100,100,1)), color-stop(100%,rgba(88,88,88,1)));
|
||||
background: -webkit-linear-gradient(top, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%);
|
||||
background: -o-linear-gradient(top, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%);
|
||||
background: -ms-linear-gradient(top, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%);
|
||||
background: linear-gradient(to bottom, rgba(107, 107, 107, 1) 0%,rgba(88,88,88,1) 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#646464', endColorstr='#585858',GradientType=0 );
|
||||
-webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
-moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
width: 47px;
|
||||
height: 47px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.airtime_box .airtime_button:hover {
|
||||
background: rgb(147,147,147);
|
||||
background: -moz-linear-gradient(top, rgba(147,147,147,1) 0%, rgba(117,117,117,1) 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(147,147,147,1)), color-stop(100%,rgba(117,117,117,1)));
|
||||
background: -webkit-linear-gradient(top, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%);
|
||||
background: -o-linear-gradient(top, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%);
|
||||
background: -ms-linear-gradient(top, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%);
|
||||
background: linear-gradient(to bottom, rgba(147,147,147,1) 0%,rgba(117,117,117,1) 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#939393', endColorstr='#757575',GradientType=0 );
|
||||
}
|
||||
|
||||
.airtime_box .airtime_button .play_button {
|
||||
display: block;
|
||||
background: url("embed-player-images/play_button.png") center no-repeat;
|
||||
width: 47px;
|
||||
height: 47px;
|
||||
}
|
||||
|
||||
.airtime_box .airtime_button .stop_button {
|
||||
display: block;
|
||||
background: url("embed-player-images/pause_button.png") center no-repeat;
|
||||
width: 47px;
|
||||
height: 47px;
|
||||
}
|
||||
|
||||
.hide_button {
|
||||
display:none !important;
|
||||
}
|
||||
|
||||
.now_playing {
|
||||
margin-top: 8px;
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
width: 170px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.now_playing span {
|
||||
display: block;
|
||||
color: #aaaaaa;
|
||||
width: 170px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.airtime_volume {
|
||||
padding: 10px 0px 15px 0px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.airtime_volume .volume_control {
|
||||
margin-left: 55px;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.airtime_volume .mute {
|
||||
background: url("embed-player-images/mute.png") center no-repeat;
|
||||
display: block;
|
||||
margin-top: -4px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.airtime_volume_bar {
|
||||
border-color: #262526 #262526 #5E5E5E;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
background-color: #393939;
|
||||
width: auto;
|
||||
height: 5px;
|
||||
cursor: pointer;
|
||||
margin-left: 80px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.airtime_volume_bar_value {
|
||||
background-color: #ff9122;
|
||||
width: 0px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
.airtime_schedule {
|
||||
margin: 10px 20px 5px 20px;
|
||||
padding-top: 10px;
|
||||
font-size: 14px;
|
||||
color: #aaaaaa;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.airtime_next {
|
||||
float: left;
|
||||
margin: 0px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.schedule_list {
|
||||
list-style: none;
|
||||
padding-left: 0px;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 0px;
|
||||
margin-left: 60px;
|
||||
margin-bottom: 0px;
|
||||
line-height: 130%;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.schedule_list li {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.airtime-pro-orange {
|
||||
color: #ff5d1a;
|
||||
}
|
|
@ -484,7 +484,7 @@ fieldset.plain {
|
|||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2) inset;
|
||||
}
|
||||
|
||||
input[readonly]{
|
||||
input[readonly], textarea[readonly] {
|
||||
background-color:#b1b1b1
|
||||
}
|
||||
|
||||
|
@ -3226,4 +3226,4 @@ dd .stream-status {
|
|||
padding: 20px;
|
||||
font-size: 16px;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -0,0 +1,94 @@
|
|||
function updateEmbedSrcParams()
|
||||
{
|
||||
var $embedCodeParams = "?";
|
||||
var $streamMode = getStreamMode();
|
||||
if ($streamMode == "manual") {
|
||||
var $stream = $("input[name=player_stream_url]:radio:checked").val();
|
||||
$embedCodeParams += "stream="+$stream;
|
||||
} else if ($streamMode == "auto") {
|
||||
$embedCodeParams += "stream=auto";
|
||||
}
|
||||
|
||||
$embedCodeParams += "&title="+getPlayerTitle();
|
||||
|
||||
$embedCodeParams += "\"";
|
||||
|
||||
$("textarea[name=player_embed_src]").val(function(index, value) {
|
||||
return value.replace(/\?.*?"/, $embedCodeParams);
|
||||
});
|
||||
|
||||
updatePlayerIframeSrc($("textarea[name=player_embed_src]").val());
|
||||
}
|
||||
|
||||
function updatePlayerIframeSrc(iframe_text) {
|
||||
var $player_iframe = $("#player_form iframe");
|
||||
var player_iframe_src = iframe_text.match(/http.*?"/)[0].slice(0, -1);
|
||||
$player_iframe.attr('src', player_iframe_src);
|
||||
}
|
||||
|
||||
function getStreamMode() {
|
||||
return $("input[name=player_stream_mode]:radio:checked").val();
|
||||
}
|
||||
|
||||
function getPlayerTitle() {
|
||||
return $("input[name=player_title]").val();
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$("#player_stream_url-element").hide();
|
||||
|
||||
// stream mode change event
|
||||
$("#player_stream_mode-element").change(function() {
|
||||
var $streamMode = getStreamMode();
|
||||
|
||||
if ($streamMode == "auto") {
|
||||
$("#player_stream_url-element").hide();
|
||||
|
||||
} else if ($streamMode == "manual") {
|
||||
$("#player_stream_url-element").show();
|
||||
|
||||
$("input[name=player_stream_url]").each(function(i, obj) {
|
||||
if ($(this).parent().text().toLowerCase().indexOf("opus") >= 0) {
|
||||
$(this).attr("disabled", "disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateEmbedSrcParams();
|
||||
});
|
||||
|
||||
// stream url change event
|
||||
$("#player_stream_url-element").change(function() {
|
||||
updateEmbedSrcParams();
|
||||
});
|
||||
|
||||
// display title checkbox change event
|
||||
$("#player_display_title").change(function() {
|
||||
if ($(this).prop("checked")) {
|
||||
$("#player_title-label").show();
|
||||
$("#player_title-element").show();
|
||||
} else {
|
||||
$("#player_title-label").hide();
|
||||
$("#player_title-element").hide();
|
||||
}
|
||||
updateEmbedSrcParams();
|
||||
});
|
||||
|
||||
// title textbox change event
|
||||
// setup before functions
|
||||
var typingTimer;
|
||||
var doneTypingInterval = 3000;
|
||||
|
||||
// on keyup, start the countdown
|
||||
$("input[name=player_title]").keyup(function(){
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(updateEmbedSrcParams, doneTypingInterval);
|
||||
});
|
||||
|
||||
// on keydown, clear the countdown
|
||||
$("input[name=player_title]").keydown(function(){
|
||||
clearTimeout(typingTimer);
|
||||
});
|
||||
});
|
||||
|
|
@ -443,7 +443,7 @@ function setSliderForReplayGain(){
|
|||
$( "#replayGainModifier" ).val( $( "#slider-range-max" ).slider( "value" ) );
|
||||
}
|
||||
|
||||
function setPseudoAdminPassword(s1, s2, s3) {
|
||||
function setPseudoAdminPassword(s1, s2, s3, s4) {
|
||||
if (s1) {
|
||||
$('#s1_data-admin_pass').val('xxxxxx');
|
||||
}
|
||||
|
@ -453,11 +453,14 @@ function setPseudoAdminPassword(s1, s2, s3) {
|
|||
if (s3) {
|
||||
$('#s3_data-admin_pass').val('xxxxxx');
|
||||
}
|
||||
if (s4) {
|
||||
$('#s4_data-admin_pass').val('xxxxxx');
|
||||
}
|
||||
}
|
||||
|
||||
function getAdminPasswordStatus() {
|
||||
$.ajax({ url: baseUrl+'Preference/get-admin-password-status/format/json', dataType:"json", success:function(data){
|
||||
setPseudoAdminPassword(data.s1, data.s2, data.s3);
|
||||
setPseudoAdminPassword(data.s1, data.s2, data.s3, data.s4);
|
||||
}});
|
||||
}
|
||||
|
||||
|
@ -476,7 +479,7 @@ $(document).ready(function() {
|
|||
$('#content').empty().append(json.html);
|
||||
setupEventListeners();
|
||||
setSliderForReplayGain();
|
||||
setPseudoAdminPassword(json.s1_set_admin_pass, json.s2_set_admin_pass, json.s3_set_admin_pass);
|
||||
setPseudoAdminPassword(json.s1_set_admin_pass, json.s2_set_admin_pass, json.s3_set_admin_pass, json.s4_set_admin_pass);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -468,9 +468,11 @@ var AIRTIME = (function(AIRTIME){
|
|||
}
|
||||
|
||||
a = oData.ColReorder;
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
if (typeof(a[i]) === "string") {
|
||||
a[i] = parseInt(a[i], 10);
|
||||
if (a) {
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
if (typeof(a[i]) === "string") {
|
||||
a[i] = parseInt(a[i], 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,18 +10,23 @@ def generate_liquidsoap_config(ss):
|
|||
fh.write("################################################\n")
|
||||
fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n")
|
||||
fh.write("################################################\n")
|
||||
fh.write("# The ignore() lines are to squash unused variable warnings\n")
|
||||
|
||||
for d in data:
|
||||
key = d['keyname']
|
||||
for key, value in data.iteritems():
|
||||
try:
|
||||
str_buffer = "%s = %s\n" % (key, int(value))
|
||||
except ValueError:
|
||||
try: # Is it a boolean?
|
||||
if "true" in value or "false" in value:
|
||||
str_buffer = "%s = %s\n" % (key, value.lower())
|
||||
else:
|
||||
raise ValueError() # Just drop into the except below
|
||||
except: #Everything else is a string
|
||||
str_buffer = "%s = \"%s\"\n" % (key, value)
|
||||
|
||||
str_buffer = d[u'keyname'] + " = "
|
||||
if d[u'type'] == 'string':
|
||||
val = '"%s"' % d['value']
|
||||
else:
|
||||
val = d[u'value']
|
||||
val = val if len(val) > 0 else "0"
|
||||
str_buffer = "%s = %s\n" % (key, val)
|
||||
fh.write(str_buffer.encode('utf-8'))
|
||||
# ignore squashes unused variable errors from Liquidsoap
|
||||
fh.write(("ignore(%s)\n" % key).encode('utf-8'))
|
||||
|
||||
fh.write('log_file = "/var/log/airtime/pypo-liquidsoap/<script>.log"\n')
|
||||
fh.close()
|
||||
|
|
|
@ -10,18 +10,22 @@ output_sound_device_type = "ALSA"
|
|||
s1_output = "icecast"
|
||||
s2_output = "icecast"
|
||||
s3_output = "icecast"
|
||||
s4_output = "icecast"
|
||||
|
||||
s1_enable = true
|
||||
s2_enable = false
|
||||
s3_enable = false
|
||||
s4_enable = false
|
||||
|
||||
s1_type = "ogg"
|
||||
s2_type = "ogg"
|
||||
s3_type = "mp3"
|
||||
s4_type = "mp3"
|
||||
|
||||
s1_bitrate = 128
|
||||
s2_bitrate = 128
|
||||
s3_bitrate = 160
|
||||
s4_bitrate = 160
|
||||
|
||||
###########################################
|
||||
# Logging settings #
|
||||
|
@ -35,31 +39,39 @@ log_file = "/var/log/airtime/pypo-liquidsoap/<script>.log"
|
|||
s1_host = "127.0.0.1"
|
||||
s2_host = "127.0.0.1"
|
||||
s3_host = "127.0.0.1"
|
||||
s4_host = "127.0.0.1"
|
||||
s1_port = 8000
|
||||
s2_port = 8000
|
||||
s3_port = 8000
|
||||
s4_port = 8000
|
||||
s1_user = ""
|
||||
s2_user = ""
|
||||
s3_user = ""
|
||||
s4_user = ""
|
||||
s1_pass = "hackme"
|
||||
s2_pass = "hackme"
|
||||
s3_pass = "hackme"
|
||||
s4_pass = "hackme"
|
||||
|
||||
# Icecast mountpoint names
|
||||
s1_mount = "airtime_128.ogg"
|
||||
s2_mount = "airtime_128.ogg"
|
||||
s3_mount = "airtime_160.mp3"
|
||||
s4_mount = "airtime_160.mp3"
|
||||
|
||||
# Webstream metadata settings
|
||||
s1_url = "http://airtime.sourcefabric.org"
|
||||
s2_url = "http://airtime.sourcefabric.org"
|
||||
s3_url = "http://airtime.sourcefabric.org"
|
||||
s1_description = "Airtime Radio! stream1"
|
||||
s2_description = "Airtime Radio! stream2"
|
||||
s3_description = "Airtime Radio! stream3"
|
||||
s4_url = "http://airtime.sourcefabric.org"
|
||||
s1_description = "Airtime Radio! Stream 1"
|
||||
s2_description = "Airtime Radio! Stream 2"
|
||||
s3_description = "Airtime Radio! Stream 3"
|
||||
s4_description = "Airtime Radio! Stream 4"
|
||||
s1_genre = "genre"
|
||||
s2_genre = "genre"
|
||||
s3_genre = "genre"
|
||||
s4_genre = "genre"
|
||||
|
||||
# Audio stream metadata for vorbis/ogg is disabled by default
|
||||
# due to a number of client media players that disconnect
|
||||
|
|
|
@ -29,6 +29,7 @@ dynamic_metadata_callback = ref fun (s) -> begin () end
|
|||
s1_connected = ref ''
|
||||
s2_connected = ref ''
|
||||
s3_connected = ref ''
|
||||
s4_connected = ref ''
|
||||
s1_namespace = ref ''
|
||||
s2_namespace = ref ''
|
||||
s3_namespace = ref ''
|
||||
|
@ -103,7 +104,7 @@ server.register(namespace="vars",
|
|||
fun (s) -> begin log("vars.bootup_time") time := s s end)
|
||||
server.register(namespace="streams",
|
||||
"connection_status",
|
||||
fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected}" end)
|
||||
fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected},4:#{!s4_connected}" end)
|
||||
server.register(namespace="vars",
|
||||
"default_dj_fade",
|
||||
fun (s) -> begin log("vars.default_dj_fade") default_dj_fade := float_of_string(s) s end)
|
||||
|
@ -399,6 +400,21 @@ if s3_enable == true then
|
|||
s3_connected, s3_description, s3_channels)
|
||||
end
|
||||
|
||||
s4_namespace = ref ''
|
||||
if s4_enable == true then
|
||||
log("Stream 4 Enabled")
|
||||
if s4_output == 'shoutcast' then
|
||||
s4_namespace := "shoutcast_stream_4"
|
||||
else
|
||||
s4_namespace := s4_mount
|
||||
end
|
||||
server.register(namespace=!s4_namespace, "connected", fun (s) -> begin log("#{!s4_namespace}.connected") !s4_connected end)
|
||||
output_to(s4_output, s4_type, s4_bitrate, s4_host, s4_port, s4_pass,
|
||||
s4_mount, s4_url, s4_name, s4_genre, s4_user, s, "4",
|
||||
s4_connected, s4_description, s4_channels)
|
||||
end
|
||||
|
||||
|
||||
command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --liquidsoap-started &"
|
||||
log(command)
|
||||
system(command)
|
||||
|
|
|
@ -118,10 +118,10 @@ class ListenerStat(Thread):
|
|||
else:
|
||||
stats.append(self.get_shoutcast_stats(v))
|
||||
self.update_listener_stat_error(v["mount"], 'OK')
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
try:
|
||||
self.update_listener_stat_error(v["mount"], str(e))
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
self.logger.error('Exception: %s', e)
|
||||
|
||||
return stats
|
||||
|
|
|
@ -216,98 +216,9 @@ class PypoFetch(Thread):
|
|||
TODO: This function needs to be way shorter, and refactored :/ - MK
|
||||
"""
|
||||
def regenerate_liquidsoap_conf(self, setting):
|
||||
existing = {}
|
||||
self.restart_liquidsoap()
|
||||
self.update_liquidsoap_connection_status()
|
||||
|
||||
setting = sorted(setting.items())
|
||||
try:
|
||||
fh = open('/etc/airtime/liquidsoap.cfg', 'r')
|
||||
except IOError, e:
|
||||
#file does not exist
|
||||
self.restart_liquidsoap()
|
||||
return
|
||||
|
||||
self.logger.info("Reading existing config...")
|
||||
# read existing conf file and build dict
|
||||
while True:
|
||||
line = fh.readline()
|
||||
|
||||
# empty line means EOF
|
||||
if not line:
|
||||
break
|
||||
|
||||
line = line.strip()
|
||||
|
||||
if not len(line) or line[0] == "#":
|
||||
continue
|
||||
|
||||
try:
|
||||
key, value = line.split('=', 1)
|
||||
except ValueError:
|
||||
continue
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
value = value.replace('"', '')
|
||||
if value == '' or value == "0":
|
||||
value = ''
|
||||
existing[key] = value
|
||||
fh.close()
|
||||
|
||||
# dict flag for any change in config
|
||||
change = {}
|
||||
# this flag is to detect disable -> disable change
|
||||
# in that case, we don't want to restart even if there are changes.
|
||||
state_change_restart = {}
|
||||
#restart flag
|
||||
restart = False
|
||||
|
||||
self.logger.info("Looking for changes...")
|
||||
# look for changes
|
||||
for k, s in setting:
|
||||
if "output_sound_device" in s[u'keyname'] or "icecast_vorbis_metadata" in s[u'keyname']:
|
||||
dump, stream = s[u'keyname'].split('_', 1)
|
||||
state_change_restart[stream] = False
|
||||
# This is the case where restart is required no matter what
|
||||
if (existing[s[u'keyname']] != str(s[u'value'])):
|
||||
self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
|
||||
restart = True;
|
||||
elif "master_live_stream_port" in s[u'keyname'] or "master_live_stream_mp" in s[u'keyname'] or "dj_live_stream_port" in s[u'keyname'] or "dj_live_stream_mp" in s[u'keyname'] or "off_air_meta" in s[u'keyname']:
|
||||
if (existing[s[u'keyname']] != s[u'value']):
|
||||
self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
|
||||
restart = True;
|
||||
else:
|
||||
stream, dump = s[u'keyname'].split('_', 1)
|
||||
if "_output" in s[u'keyname']:
|
||||
if (existing[s[u'keyname']] != s[u'value']):
|
||||
self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
|
||||
restart = True;
|
||||
state_change_restart[stream] = True
|
||||
elif (s[u'value'] != 'disabled'):
|
||||
state_change_restart[stream] = True
|
||||
else:
|
||||
state_change_restart[stream] = False
|
||||
else:
|
||||
# setting inital value
|
||||
if stream not in change:
|
||||
change[stream] = False
|
||||
if not (s[u'value'] == existing[s[u'keyname']]):
|
||||
self.logger.info("Keyname: %s, Current value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value'])
|
||||
change[stream] = True
|
||||
|
||||
# set flag change for sound_device alway True
|
||||
self.logger.info("Change:%s, State_Change:%s...", change, state_change_restart)
|
||||
|
||||
for k, v in state_change_restart.items():
|
||||
if k == "sound_device" and v:
|
||||
restart = True
|
||||
elif v and change[k]:
|
||||
self.logger.info("'Need-to-restart' state detected for %s...", k)
|
||||
restart = True
|
||||
# rewrite
|
||||
if restart:
|
||||
self.restart_liquidsoap()
|
||||
else:
|
||||
self.logger.info("No change detected in setting...")
|
||||
self.update_liquidsoap_connection_status()
|
||||
|
||||
@ls_timeout
|
||||
def update_liquidsoap_connection_status(self):
|
||||
|
|
|
@ -15,6 +15,7 @@ class PypoLiquidsoap():
|
|||
"s1": None,
|
||||
"s2": None,
|
||||
"s3": None,
|
||||
"s4": None,
|
||||
}
|
||||
|
||||
self.telnet_liquidsoap = TelnetLiquidsoap(telnet_lock, \
|
||||
|
|
|
@ -83,12 +83,12 @@ class Notify:
|
|||
|
||||
# @pram time: time that LS started
|
||||
def notify_liquidsoap_status(self, msg, stream_id, time):
|
||||
logger.debug('#################################################')
|
||||
logger.debug('# Calling server to update liquidsoap status #')
|
||||
logger.debug('#################################################')
|
||||
logger.debug('msg = ' + str(msg))
|
||||
logger.info('#################################################')
|
||||
logger.info('# Calling server to update liquidsoap status #')
|
||||
logger.info('#################################################')
|
||||
logger.info('msg = ' + str(msg))
|
||||
response = self.api_client.notify_liquidsoap_status(msg, stream_id, time)
|
||||
logger.debug("Response: " + json.dumps(response))
|
||||
logger.info("Response: " + json.dumps(response))
|
||||
|
||||
def notify_source_status(self, source_name, status):
|
||||
logger.debug('#################################################')
|
||||
|
|
Loading…
Reference in New Issue