Merge branch '2.3.x' into devel
Conflicts: airtime_mvc/application/controllers/LibraryController.php airtime_mvc/application/models/StoredFile.php
This commit is contained in:
commit
93ec4c001b
39 changed files with 424 additions and 146 deletions
|
@ -60,8 +60,10 @@ class AudiopreviewController extends Zend_Controller_Action
|
||||||
$this->view->uri = $uri;
|
$this->view->uri = $uri;
|
||||||
$this->view->mime = $mime;
|
$this->view->mime = $mime;
|
||||||
$this->view->audioFileID = $audioFileID;
|
$this->view->audioFileID = $audioFileID;
|
||||||
$this->view->audioFileArtist = $audioFileArtist;
|
// We need to decode artist and title because it gets
|
||||||
$this->view->audioFileTitle = $audioFileTitle;
|
// encoded twice in js
|
||||||
|
$this->view->audioFileArtist = urldecode($audioFileArtist);
|
||||||
|
$this->view->audioFileTitle = urldecode($audioFileTitle);
|
||||||
$this->view->type = $type;
|
$this->view->type = $type;
|
||||||
|
|
||||||
$this->_helper->viewRenderer->setRender('audio-preview');
|
$this->_helper->viewRenderer->setRender('audio-preview');
|
||||||
|
|
|
@ -412,7 +412,7 @@ class LibraryController extends Zend_Controller_Action
|
||||||
$formValues = $this->_getParam('data', null);
|
$formValues = $this->_getParam('data', null);
|
||||||
$formdata = array();
|
$formdata = array();
|
||||||
foreach ($formValues as $val) {
|
foreach ($formValues as $val) {
|
||||||
$formdata[$val["name"]] = $val["value"];
|
$formdata[$val["name"]] = htmlspecialchars($val["value"]);
|
||||||
}
|
}
|
||||||
$file->setDbColMetadata($formdata);
|
$file->setDbColMetadata($formdata);
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@ class LocaleController extends Zend_Controller_Action
|
||||||
{
|
{
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
$ajaxContext = $this->_helper->getHelper("AjaxContext");
|
|
||||||
$ajaxContext->addActionContext("general-translation-table", "json")
|
|
||||||
->addActionContext("datatables-translation-table", "json")
|
|
||||||
->initContext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function datatablesTranslationTableAction()
|
public function datatablesTranslationTableAction()
|
||||||
|
|
|
@ -274,6 +274,7 @@ class PreferenceController extends Zend_Controller_Action
|
||||||
Application_Model_Preference::setReplayGainModifier($values["replayGainModifier"]);
|
Application_Model_Preference::setReplayGainModifier($values["replayGainModifier"]);
|
||||||
$md = array('schedule' => Application_Model_Schedule::getSchedule());
|
$md = array('schedule' => Application_Model_Schedule::getSchedule());
|
||||||
Application_Model_RabbitMq::SendMessageToPypo("update_schedule", $md);
|
Application_Model_RabbitMq::SendMessageToPypo("update_schedule", $md);
|
||||||
|
//Application_Model_RabbitMq::PushSchedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Application_Model_Preference::GetMasterDjConnectionUrlOverride()) {
|
if (!Application_Model_Preference::GetMasterDjConnectionUrlOverride()) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ class ScheduleController extends Zend_Controller_Action
|
||||||
{
|
{
|
||||||
$ajaxContext = $this->_helper->getHelper('AjaxContext');
|
$ajaxContext = $this->_helper->getHelper('AjaxContext');
|
||||||
$ajaxContext->addActionContext('event-feed', 'json')
|
$ajaxContext->addActionContext('event-feed', 'json')
|
||||||
|
->addActionContext('event-feed-preload', 'json')
|
||||||
->addActionContext('make-context-menu', 'json')
|
->addActionContext('make-context-menu', 'json')
|
||||||
->addActionContext('add-show-dialog', 'json')
|
->addActionContext('add-show-dialog', 'json')
|
||||||
->addActionContext('add-show', 'json')
|
->addActionContext('add-show', 'json')
|
||||||
|
@ -89,15 +90,23 @@ class ScheduleController extends Zend_Controller_Action
|
||||||
$this->view->headLink()->appendStylesheet($baseUrl.'css/showbuilder.css?'.$CC_CONFIG['airtime_version']);
|
$this->view->headLink()->appendStylesheet($baseUrl.'css/showbuilder.css?'.$CC_CONFIG['airtime_version']);
|
||||||
//End Show builder JS/CSS requirements
|
//End Show builder JS/CSS requirements
|
||||||
|
|
||||||
|
|
||||||
Application_Model_Schedule::createNewFormSections($this->view);
|
Application_Model_Schedule::createNewFormSections($this->view);
|
||||||
|
|
||||||
$user = Application_Model_User::getCurrentUser();
|
$user = Application_Model_User::getCurrentUser();
|
||||||
|
|
||||||
if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
|
if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
|
||||||
$this->view->preloadShowForm = true;
|
$this->view->preloadShowForm = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->headScript()->appendScript("var weekStart = ".Application_Model_Preference::GetWeekStartDay().";");
|
$this->view->headScript()->appendScript(
|
||||||
|
"var calendarPref = {};\n".
|
||||||
|
"calendarPref.weekStart = ".Application_Model_Preference::GetWeekStartDay().";\n".
|
||||||
|
"calendarPref.timestamp = ".time().";\n".
|
||||||
|
"calendarPref.timezoneOffset = ".date("Z").";\n".
|
||||||
|
"calendarPref.timeScale = '".Application_Model_Preference::GetCalendarTimeScale()."';\n".
|
||||||
|
"calendarPref.timeInterval = ".Application_Model_Preference::GetCalendarTimeInterval().";\n".
|
||||||
|
"calendarPref.weekStartDay = ".Application_Model_Preference::GetWeekStartDay().";\n".
|
||||||
|
"var calendarEvents = null;"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function eventFeedAction()
|
public function eventFeedAction()
|
||||||
|
@ -109,10 +118,28 @@ class ScheduleController extends Zend_Controller_Action
|
||||||
|
|
||||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||||
$user = new Application_Model_User($userInfo->id);
|
$user = new Application_Model_User($userInfo->id);
|
||||||
if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
|
$editable = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER));
|
||||||
$editable = true;
|
|
||||||
|
$events = &Application_Model_Show::getFullCalendarEvents($start, $end, $editable);
|
||||||
|
$this->view->events = $events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eventFeedPreloadAction()
|
||||||
|
{
|
||||||
|
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||||
|
$user = new Application_Model_User($userInfo->id);
|
||||||
|
$editable = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER));
|
||||||
|
|
||||||
|
$calendar_interval = Application_Model_Preference::GetCalendarTimeScale();
|
||||||
|
Logging::info($calendar_interval);
|
||||||
|
if ($calendar_interval == "agendaDay") {
|
||||||
|
list($start, $end) = Application_Model_Show::getStartEndCurrentDayView();
|
||||||
|
} else if ($calendar_interval == "agendaWeek") {
|
||||||
|
list($start, $end) = Application_Model_Show::getStartEndCurrentWeekView();
|
||||||
|
} else if ($calendar_interval == "month") {
|
||||||
|
list($start, $end) = Application_Model_Show::getStartEndCurrentMonthView();
|
||||||
} else {
|
} else {
|
||||||
$editable = false;
|
Logging::error("Invalid Calendar Interval '$calendar_interval'");
|
||||||
}
|
}
|
||||||
|
|
||||||
$events = &Application_Model_Show::getFullCalendarEvents($start, $end, $editable);
|
$events = &Application_Model_Show::getFullCalendarEvents($start, $end, $editable);
|
||||||
|
|
|
@ -110,7 +110,7 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
|
||||||
{
|
{
|
||||||
$controller = strtolower($request->getControllerName());
|
$controller = strtolower($request->getControllerName());
|
||||||
|
|
||||||
if (in_array($controller, array("api", "auth"))) {
|
if (in_array($controller, array("api", "auth", "locale"))) {
|
||||||
|
|
||||||
$this->setRoleName("G");
|
$this->setRoleName("G");
|
||||||
} elseif (!Zend_Auth::getInstance()->hasIdentity()) {
|
} elseif (!Zend_Auth::getInstance()->hasIdentity()) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Application_Form_RegisterAirtime extends Zend_Form
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
$this->setAction(Application_Common_OsPath::getBaseDir().'/Showbuilder');
|
$this->setAction(Application_Common_OsPath::getBaseDir().'Showbuilder');
|
||||||
$this->setMethod('post');
|
$this->setMethod('post');
|
||||||
|
|
||||||
$country_list = Application_Model_Preference::GetCountryList();
|
$country_list = Application_Model_Preference::GetCountryList();
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<div class="personal-block solo">
|
<div class="personal-block solo">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a id="current-user" href=<?php echo $baseUrl . "User/edit-user"?>><span class="name"><?php echo $this->loggedInAs()?></span></a> | <a href=<?php echo $baseUrl . "Login/logout"?>><?php echo _("Logout")?></a>
|
<a id="current-user" href=<?php echo $baseUrl . "User/edit-user"?>><span class="name"><?php echo $this->escape($this->loggedInAs()); ?></span></a> | <a href=<?php echo $baseUrl . "Login/logout"?>><?php echo _("Logout")?></a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,6 +32,8 @@ class Logging {
|
||||||
{
|
{
|
||||||
if (is_array($p_msg) || is_object($p_msg)) {
|
if (is_array($p_msg) || is_object($p_msg)) {
|
||||||
return print_r($p_msg, true);
|
return print_r($p_msg, true);
|
||||||
|
} else if (is_bool($p_msg)) {
|
||||||
|
return $p_msg ? "true" : "false";
|
||||||
} else {
|
} else {
|
||||||
return $p_msg;
|
return $p_msg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -731,6 +731,10 @@ SQL;
|
||||||
'replay_gain' => $replay_gain,
|
'replay_gain' => $replay_gain,
|
||||||
'independent_event' => $independent_event,
|
'independent_event' => $independent_event,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($schedule_item['cue_in'] > $schedule_item['cue_out']) {
|
||||||
|
$schedule_item['cue_in'] = $schedule_item['cue_out'];
|
||||||
|
}
|
||||||
self::appendScheduleItem($data, $start, $schedule_item);
|
self::appendScheduleItem($data, $start, $schedule_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,7 +945,6 @@ SQL;
|
||||||
self::createScheduledEvents($data, $range_start, $range_end);
|
self::createScheduledEvents($data, $range_start, $range_end);
|
||||||
|
|
||||||
self::foldData($data["media"]);
|
self::foldData($data["media"]);
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,13 +136,17 @@ class Application_Model_Scheduler
|
||||||
|
|
||||||
if ($type === "audioclip") {
|
if ($type === "audioclip") {
|
||||||
$file = CcFilesQuery::create()->findPK($id, $this->con);
|
$file = CcFilesQuery::create()->findPK($id, $this->con);
|
||||||
|
$storedFile = new Application_Model_StoredFile($file->getDbId());
|
||||||
|
|
||||||
if (is_null($file) || !$file->visible()) {
|
if (is_null($file) || !$file->visible()) {
|
||||||
throw new Exception(_("A selected File does not exist!"));
|
throw new Exception(_("A selected File does not exist!"));
|
||||||
} else {
|
} else {
|
||||||
$data = $this->fileInfo;
|
$data = $this->fileInfo;
|
||||||
$data["id"] = $id;
|
$data["id"] = $id;
|
||||||
$data["cliplength"] = $file->getDbLength();
|
$data["cliplength"] = $storedFile->getRealClipLength(
|
||||||
|
$file->getDbCuein(),
|
||||||
|
$file->getDbCueout());
|
||||||
|
|
||||||
$data["cuein"] = $file->getDbCuein();
|
$data["cuein"] = $file->getDbCuein();
|
||||||
$data["cueout"] = $file->getDbCueout();
|
$data["cueout"] = $file->getDbCueout();
|
||||||
|
|
||||||
|
@ -438,7 +442,6 @@ class Application_Model_Scheduler
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($schedFiles as $file) {
|
foreach ($schedFiles as $file) {
|
||||||
|
|
||||||
$endTimeDT = $this->findEndTime($nextStartDT, $file['cliplength']);
|
$endTimeDT = $this->findEndTime($nextStartDT, $file['cliplength']);
|
||||||
|
|
||||||
//item existed previously and is being moved.
|
//item existed previously and is being moved.
|
||||||
|
|
|
@ -1750,14 +1750,15 @@ SQL;
|
||||||
$interval = $p_start->diff($p_end);
|
$interval = $p_start->diff($p_end);
|
||||||
$days = $interval->format('%a');
|
$days = $interval->format('%a');
|
||||||
$shows = Application_Model_Show::getShows($p_start, $p_end);
|
$shows = Application_Model_Show::getShows($p_start, $p_end);
|
||||||
$nowEpoch = time();
|
|
||||||
$content_count = Application_Model_ShowInstance::getContentCount(
|
$content_count = Application_Model_ShowInstance::getContentCount(
|
||||||
$p_start, $p_end);
|
$p_start, $p_end);
|
||||||
$isFull = Application_Model_ShowInstance::getIsFull($p_start, $p_end);
|
$isFull = Application_Model_ShowInstance::getIsFull($p_start, $p_end);
|
||||||
$timezone = date_default_timezone_get();
|
$timezone = date_default_timezone_get();
|
||||||
|
$current_timezone = new DateTimeZone($timezone);
|
||||||
$utc = new DateTimeZone("UTC");
|
$utc = new DateTimeZone("UTC");
|
||||||
|
$now = new DateTime("now", $utc);
|
||||||
|
|
||||||
foreach ($shows as $show) {
|
foreach ($shows as &$show) {
|
||||||
$options = array();
|
$options = array();
|
||||||
|
|
||||||
//only bother calculating percent for week or day view.
|
//only bother calculating percent for week or day view.
|
||||||
|
@ -1767,7 +1768,6 @@ SQL;
|
||||||
|
|
||||||
if (isset($show["parent_starts"])) {
|
if (isset($show["parent_starts"])) {
|
||||||
$parentStartsDT = new DateTime($show["parent_starts"], $utc);
|
$parentStartsDT = new DateTime($show["parent_starts"], $utc);
|
||||||
$parentStartsEpoch = intval($parentStartsDT->format("U"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$startsDT = DateTime::createFromFormat("Y-m-d G:i:s",
|
$startsDT = DateTime::createFromFormat("Y-m-d G:i:s",
|
||||||
|
@ -1775,35 +1775,53 @@ SQL;
|
||||||
$endsDT = DateTime::createFromFormat("Y-m-d G:i:s",
|
$endsDT = DateTime::createFromFormat("Y-m-d G:i:s",
|
||||||
$show["ends"], $utc);
|
$show["ends"], $utc);
|
||||||
|
|
||||||
$startsEpochStr = $startsDT->format("U");
|
|
||||||
$endsEpochStr = $endsDT->format("U");
|
|
||||||
|
|
||||||
$startsEpoch = intval($startsEpochStr);
|
|
||||||
$endsEpoch = intval($endsEpochStr);
|
|
||||||
|
|
||||||
$startsDT->setTimezone(new DateTimeZone($timezone));
|
|
||||||
$endsDT->setTimezone(new DateTimeZone($timezone));
|
|
||||||
|
|
||||||
if( $p_editable ) {
|
if( $p_editable ) {
|
||||||
if ($show["record"] && $nowEpoch > $startsEpoch) {
|
if ($show["record"] && $now > $startsDT) {
|
||||||
$options["editable"] = false;
|
$options["editable"] = false;
|
||||||
} elseif ($show["rebroadcast"] &&
|
} elseif ($show["rebroadcast"] &&
|
||||||
$nowEpoch > $parentStartsEpoch) {
|
$now > $parentStartsDT) {
|
||||||
$options["editable"] = false;
|
$options["editable"] = false;
|
||||||
} elseif ($nowEpoch < $endsEpoch) {
|
} elseif ($now < $endsDT) {
|
||||||
$options["editable"] = true;
|
$options["editable"] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$startsDT->setTimezone($current_timezone);
|
||||||
|
$endsDT->setTimezone($current_timezone);
|
||||||
|
|
||||||
$options["show_empty"] = (array_key_exists($show['instance_id'],
|
$options["show_empty"] = (array_key_exists($show['instance_id'],
|
||||||
$content_count)) ? 0 : 1;
|
$content_count)) ? 0 : 1;
|
||||||
|
|
||||||
$options["show_partial_filled"] = !$isFull[$show['instance_id']];
|
$options["show_partial_filled"] = !$isFull[$show['instance_id']];
|
||||||
|
|
||||||
$events[] = &self::makeFullCalendarEvent($show, $options,
|
$event = array();
|
||||||
$startsDT, $endsDT, $startsEpochStr, $endsEpochStr);
|
|
||||||
|
$event["id"] = intval($show["instance_id"]);
|
||||||
|
$event["title"] = $show["name"];
|
||||||
|
$event["start"] = $startsDT->format("Y-m-d H:i:s");
|
||||||
|
$event["end"] = $endsDT->format("Y-m-d H:i:s");
|
||||||
|
$event["allDay"] = false;
|
||||||
|
$event["showId"] = intval($show["show_id"]);
|
||||||
|
$event["record"] = intval($show["record"]);
|
||||||
|
$event["rebroadcast"] = intval($show["rebroadcast"]);
|
||||||
|
$event["soundcloud_id"] = is_null($show["soundcloud_id"])
|
||||||
|
? -1 : $show["soundcloud_id"];
|
||||||
|
|
||||||
|
//event colouring
|
||||||
|
if ($show["color"] != "") {
|
||||||
|
$event["textColor"] = "#".$show["color"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($show["background_color"] != "") {
|
||||||
|
$event["color"] = "#".$show["background_color"];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($options as $key => $value) {
|
||||||
|
$event[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$events[] = $event;
|
||||||
|
}
|
||||||
return $events;
|
return $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1820,7 +1838,7 @@ SQL;
|
||||||
return $percent;
|
return $percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function &makeFullCalendarEvent(&$show, $options=array(), $startDateTime, $endDateTime, $startsEpoch, $endsEpoch)
|
/* private static function &makeFullCalendarEvent(&$show, $options=array(), $startDateTime, $endDateTime, $startsEpoch, $endsEpoch)
|
||||||
{
|
{
|
||||||
$event = array();
|
$event = array();
|
||||||
|
|
||||||
|
@ -1851,7 +1869,7 @@ SQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $event;
|
return $event;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/* Takes in a UTC DateTime object.
|
/* Takes in a UTC DateTime object.
|
||||||
* Converts this to local time, since cc_show days
|
* Converts this to local time, since cc_show days
|
||||||
|
@ -2158,4 +2176,42 @@ SQL;
|
||||||
}
|
}
|
||||||
return $assocArray;
|
return $assocArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getStartEndCurrentMonthView() {
|
||||||
|
$first_day_of_calendar_month_view = mktime(0, 0, 0, date("n"), 1);
|
||||||
|
$weekStart = Application_Model_Preference::GetWeekStartDay();
|
||||||
|
while (date('w', $first_day_of_calendar_month_view) != $weekStart) {
|
||||||
|
$first_day_of_calendar_month_view -= 60*60*24;
|
||||||
|
}
|
||||||
|
$last_day_of_calendar_view = $first_day_of_calendar_month_view + 3600*24*42;
|
||||||
|
|
||||||
|
$start = new DateTime("@".$first_day_of_calendar_month_view);
|
||||||
|
$end = new DateTime("@".$last_day_of_calendar_view);
|
||||||
|
|
||||||
|
return array($start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getStartEndCurrentWeekView() {
|
||||||
|
$first_day_of_calendar_week_view = mktime(0, 0, 0, date("n"), date("j"));
|
||||||
|
$weekStart = Application_Model_Preference::GetWeekStartDay();
|
||||||
|
while (date('w', $first_day_of_calendar_week_view) != $weekStart) {
|
||||||
|
$first_day_of_calendar_week_view -= 60*60*24;
|
||||||
|
}
|
||||||
|
$last_day_of_calendar_view = $first_day_of_calendar_week_view + 3600*24*7;
|
||||||
|
|
||||||
|
$start = new DateTime("@".$first_day_of_calendar_week_view);
|
||||||
|
$end = new DateTime("@".$last_day_of_calendar_view);
|
||||||
|
|
||||||
|
return array($start, $end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getStartEndCurrentDayView() {
|
||||||
|
$today = mktime(0, 0, 0, date("n"), date("j"));
|
||||||
|
$tomorrow = $today + 3600*24;
|
||||||
|
|
||||||
|
$start = new DateTime("@".$today);
|
||||||
|
$end = new DateTime("@".$tomorrow);
|
||||||
|
|
||||||
|
return array($start, $end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,7 @@ class Application_Model_ShowBuilder
|
||||||
$row["endDate"] = $showEndDT->format("Y-m-d");
|
$row["endDate"] = $showEndDT->format("Y-m-d");
|
||||||
$row["endTime"] = $showEndDT->format("H:i");
|
$row["endTime"] = $showEndDT->format("H:i");
|
||||||
$row["duration"] = floatval($showEndDT->format("U.u")) - floatval($showStartDT->format("U.u"));
|
$row["duration"] = floatval($showEndDT->format("U.u")) - floatval($showStartDT->format("U.u"));
|
||||||
$row["title"] = $p_item["show_name"];
|
$row["title"] = htmlspecialchars($p_item["show_name"]);
|
||||||
$row["instance"] = intval($p_item["si_id"]);
|
$row["instance"] = intval($p_item["si_id"]);
|
||||||
$row["image"] = '';
|
$row["image"] = '';
|
||||||
|
|
||||||
|
|
|
@ -1310,6 +1310,14 @@ SQL;
|
||||||
|
|
||||||
return $updateIsScheduled;
|
return $updateIsScheduled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRealClipLength($p_cuein, $p_cueout) {
|
||||||
|
$sql = "SELECT :cueout::INTERVAL - :cuein::INTERVAL";
|
||||||
|
|
||||||
|
return Application_Common_Database::prepareAndExecute($sql, array(
|
||||||
|
':cueout' => $p_cueout,
|
||||||
|
':cuein' => $p_cuein), 'column');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeleteScheduledFileException extends Exception {}
|
class DeleteScheduledFileException extends Exception {}
|
||||||
|
|
|
@ -214,18 +214,6 @@ class Application_Model_Systemstatus
|
||||||
{
|
{
|
||||||
$partions = array();
|
$partions = array();
|
||||||
|
|
||||||
if (isset($_SERVER['AIRTIME_SRV'])) {
|
|
||||||
//connect to DB and find how much total space user has allocated.
|
|
||||||
$totalSpace = Application_Model_Preference::GetDiskQuota();
|
|
||||||
|
|
||||||
$storPath = Application_Model_MusicDir::getStorDir()->getDirectory();
|
|
||||||
|
|
||||||
list($usedSpace,) = preg_split("/[\s]+/", exec("du -bs $storPath"));
|
|
||||||
|
|
||||||
$partitions[$totalSpace]->totalSpace = $totalSpace;
|
|
||||||
$partitions[$totalSpace]->totalFreeSpace = $totalSpace - $usedSpace;
|
|
||||||
Logging::info($partitions[$totalSpace]->totalFreeSpace);
|
|
||||||
} else {
|
|
||||||
/* First lets get all the watched directories. Then we can group them
|
/* First lets get all the watched directories. Then we can group them
|
||||||
* into the same partitions by comparing the partition sizes. */
|
* into the same partitions by comparing the partition sizes. */
|
||||||
$musicDirs = Application_Model_MusicDir::getWatchedDirs();
|
$musicDirs = Application_Model_MusicDir::getWatchedDirs();
|
||||||
|
@ -243,7 +231,6 @@ class Application_Model_Systemstatus
|
||||||
|
|
||||||
$partitions[$totalSpace]->dirs[] = $md->getDirectory();
|
$partitions[$totalSpace]->dirs[] = $md->getDirectory();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($partitions);
|
return array_values($partitions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -335,6 +335,8 @@ class Application_Model_User
|
||||||
} else {
|
} else {
|
||||||
$record['delete'] = "";
|
$record['delete'] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$record = array_map('htmlspecialchars', $record);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<h2><? echo sprintf(_("%s's Settings"), $this->currentUser) ?></h2>
|
<h2><? echo sprintf(_("%s's Settings"), $this->escape($this->currentUser)) ?></h2>
|
||||||
<div id="current-user-container">
|
<div id="current-user-container">
|
||||||
<form id="current-user-form" class="edit-user-global" method="post" enctype="application/x-www-form-urlencoded">
|
<form id="current-user-form" class="edit-user-global" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
<dl class="zend_form">
|
<dl class="zend_form">
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<?php if($this->element->getElement('storageFolder')->hasErrors()) : ?>
|
<?php if($this->element->getElement('storageFolder')->hasErrors()) : ?>
|
||||||
<ul class='errors'>
|
<ul class='errors'>
|
||||||
<?php foreach($this->element->getElement('storageFolder')->getMessages() as $error): ?>
|
<?php foreach($this->element->getElement('storageFolder')->getMessages() as $error): ?>
|
||||||
<li><?php echo $error; ?></li>
|
<li><?php echo $this->escape($error); ?></li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</ul>
|
</ul>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
<?php if($this->element->getElement('watchedFolder')->hasErrors()) : ?>
|
<?php if($this->element->getElement('watchedFolder')->hasErrors()) : ?>
|
||||||
<ul class='errors'>
|
<ul class='errors'>
|
||||||
<?php foreach($this->element->getElement('watchedFolder')->getMessages() as $error): ?>
|
<?php foreach($this->element->getElement('watchedFolder')->getMessages() as $error): ?>
|
||||||
<li><?php echo $error; ?></li>
|
<li><?php echo $this->escape($error); ?></li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</ul>
|
</ul>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
|
@ -42,7 +42,7 @@ if (isset($this->obj)) {
|
||||||
<input id='obj_type' type='hidden' value='playlist'></input>
|
<input id='obj_type' type='hidden' value='playlist'></input>
|
||||||
<div class="playlist_title">
|
<div class="playlist_title">
|
||||||
<h3 id="obj_name">
|
<h3 id="obj_name">
|
||||||
<a id="playlist_name_display" contenteditable="true"><?php echo $this->obj->getName(); ?></a>
|
<a id="playlist_name_display" contenteditable="true"><?php echo $this->escape($this->obj->getName()); ?></a>
|
||||||
</h3>
|
</h3>
|
||||||
<h4 id="obj_length"><?php echo $this->length; ?></h4>
|
<h4 id="obj_length"><?php echo $this->length; ?></h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$(window).load(function(){
|
$(window).load(function(){
|
||||||
$("#username").focus();
|
$("#username").focus();
|
||||||
$("#locale").val($.cookie("airtime_locale")!== null?$.cookie("airtime_locale"):'en_CA');
|
$("#locale").val($.cookie("airtime_locale")!== null?$.cookie("airtime_locale"):$.cookie("default_airtime_locale"));
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
|
@ -111,6 +111,7 @@ $(document).ready(function() {
|
||||||
$.post(url, {format: "json", data: data}, function(data){
|
$.post(url, {format: "json", data: data}, function(data){
|
||||||
var json = $.parseJSON(data);
|
var json = $.parseJSON(data);
|
||||||
$('#content').empty().append(json.html);
|
$('#content').empty().append(json.html);
|
||||||
|
$.cookie("default_airtime_locale", $('#locale').val(), {path: '/'});
|
||||||
setTimeout(removeSuccessMsg, 5000);
|
setTimeout(removeSuccessMsg, 5000);
|
||||||
showErrorSections();
|
showErrorSections();
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,7 +28,7 @@ function rebuildStreamURL(ele){
|
||||||
}else{
|
}else{
|
||||||
streamurl = "http://"+host+":"+port+"/"
|
streamurl = "http://"+host+":"+port+"/"
|
||||||
}
|
}
|
||||||
div.find("#stream_url").html(streamurl)
|
div.find("#stream_url").text(streamurl)
|
||||||
}
|
}
|
||||||
function restrictOggBitrate(ele, on){
|
function restrictOggBitrate(ele, on){
|
||||||
var div = ele.closest("div")
|
var div = ele.closest("div")
|
||||||
|
|
|
@ -37,7 +37,7 @@ function createDateInput(el, onSelect) {
|
||||||
dayNamesMin: i18n_days_short,
|
dayNamesMin: i18n_days_short,
|
||||||
closeText: $.i18n._('Close'),
|
closeText: $.i18n._('Close'),
|
||||||
//showButtonPanel: true,
|
//showButtonPanel: true,
|
||||||
firstDay: weekStart
|
firstDay: calendarPref.weekStart
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ function setAddShowEvents() {
|
||||||
dayNamesMin: i18n_days_short,
|
dayNamesMin: i18n_days_short,
|
||||||
closeText: 'Close',
|
closeText: 'Close',
|
||||||
showButtonPanel: true,
|
showButtonPanel: true,
|
||||||
firstDay: weekStart
|
firstDay: calendarPref.weekStart
|
||||||
});
|
});
|
||||||
form.find('input[name^="add_show_rebroadcast_time"]').timepicker({
|
form.find('input[name^="add_show_rebroadcast_time"]').timepicker({
|
||||||
amPmText: ['', ''],
|
amPmText: ['', ''],
|
||||||
|
|
|
@ -326,19 +326,34 @@ function eventResize( event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, vie
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function preloadEventFeed () {
|
||||||
|
var url = baseUrl+'Schedule/event-feed-preload';
|
||||||
|
var d = new Date();
|
||||||
|
|
||||||
|
$.post(url, {format: "json", cachep: d.getTime()}, function(json){
|
||||||
|
calendarEvents = json.events;
|
||||||
|
createFullCalendar({calendarInit: calendarPref});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialLoad = true;
|
||||||
function getFullCalendarEvents(start, end, callback) {
|
function getFullCalendarEvents(start, end, callback) {
|
||||||
|
|
||||||
|
if (initialLoad) {
|
||||||
|
initialLoad = false;
|
||||||
|
callback(calendarEvents);
|
||||||
|
} else {
|
||||||
var url, start_date, end_date;
|
var url, start_date, end_date;
|
||||||
|
|
||||||
start_date = makeTimeStamp(start);
|
start_date = makeTimeStamp(start);
|
||||||
end_date = makeTimeStamp(end);
|
end_date = makeTimeStamp(end);
|
||||||
|
|
||||||
url = baseUrl+'Schedule/event-feed';
|
url = baseUrl+'Schedule/event-feed';
|
||||||
|
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
|
|
||||||
$.post(url, {format: "json", start: start_date, end: end_date, cachep: d.getTime()}, function(json){
|
$.post(url, {format: "json", start: start_date, end: end_date, cachep: d.getTime()}, function(json){
|
||||||
callback(json.events);
|
callback(json.events);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSCUploadStatus(){
|
function checkSCUploadStatus(){
|
||||||
|
@ -541,6 +556,7 @@ function alertShowErrorAndReload(){
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preloadEventFeed();
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
setInterval( "checkSCUploadStatus()", 5000 );
|
setInterval( "checkSCUploadStatus()", 5000 );
|
||||||
setInterval( "getCurrentShow()", 5000 );
|
setInterval( "getCurrentShow()", 5000 );
|
||||||
|
|
|
@ -328,9 +328,6 @@ function alertShowErrorAndReload(){
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$.ajax({ url: baseUrl+"Api/calendar-init/format/json", dataType:"json", success:createFullCalendar
|
|
||||||
, error:function(jqXHR, textStatus, errorThrown){}});
|
|
||||||
|
|
||||||
setInterval(checkCalendarSCUploadStatus, 5000);
|
setInterval(checkCalendarSCUploadStatus, 5000);
|
||||||
|
|
||||||
$.contextMenu({
|
$.contextMenu({
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
2.3.0 - Jan 21st, 2013
|
2.3.0 - Jan 21st, 2013
|
||||||
* New features
|
* New features
|
||||||
* Localization (Brazilian, Chinese, Czech, English, French, German, Italian, Korean, Portugese, Russian, Spanish)
|
* Localization (Chinese, Czech, English, French, German, Italian, Korean,
|
||||||
|
Portuguese, Russian, Spanish)
|
||||||
* User management page for non-admin users
|
* User management page for non-admin users
|
||||||
* Listener statistics (Icecast/Shoutcast)
|
* Listener statistics (Icecast/Shoutcast)
|
||||||
* Airtime no longer requires Apache document root
|
* Airtime no longer requires Apache document root
|
||||||
|
|
|
@ -15,6 +15,8 @@ INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('s3_admin_pas
|
||||||
UPDATE cc_music_dirs SET directory = directory || '/' where id in (select id from cc_music_dirs where substr(directory, length(directory)) != '/');
|
UPDATE cc_music_dirs SET directory = directory || '/' where id in (select id from cc_music_dirs where substr(directory, length(directory)) != '/');
|
||||||
UPDATE cc_files SET filepath = substring(filepath from 2) where id in (select id from cc_files where substring(filepath from 1 for 1) = '/');
|
UPDATE cc_files SET filepath = substring(filepath from 2) where id in (select id from cc_files where substring(filepath from 1 for 1) = '/');
|
||||||
|
|
||||||
|
UPDATE cc_files SET cueout = length where cueout = '00:00:00';
|
||||||
|
|
||||||
INSERT INTO cc_pref("keystr", "valstr") VALUES('locale', 'en_CA');
|
INSERT INTO cc_pref("keystr", "valstr") VALUES('locale', 'en_CA');
|
||||||
|
|
||||||
INSERT INTO cc_pref("subjid", "keystr", "valstr") VALUES(1, 'user_locale', 'en_CA');
|
INSERT INTO cc_pref("subjid", "keystr", "valstr") VALUES(1, 'user_locale', 'en_CA');
|
||||||
|
|
|
@ -73,17 +73,26 @@ class ApcUrl(object):
|
||||||
else: return self.base_url
|
else: return self.base_url
|
||||||
|
|
||||||
class ApiRequest(object):
|
class ApiRequest(object):
|
||||||
def __init__(self, name, url):
|
def __init__(self, name, url, logger=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.url = url
|
self.url = url
|
||||||
self.__req = None
|
self.__req = None
|
||||||
|
if logger is None: self.logger = logging
|
||||||
|
else: self.logger = logger
|
||||||
def __call__(self,_post_data=None, **kwargs):
|
def __call__(self,_post_data=None, **kwargs):
|
||||||
# TODO : get rid of god damn urllib and replace everything with
|
# TODO : get rid of god damn urllib and replace everything with
|
||||||
# grequests or requests at least
|
# grequests or requests at least
|
||||||
final_url = self.url.params(**kwargs).url()
|
final_url = self.url.params(**kwargs).url()
|
||||||
if _post_data is not None: _post_data = urllib.urlencode(_post_data)
|
if _post_data is not None: _post_data = urllib.urlencode(_post_data)
|
||||||
|
try:
|
||||||
req = urllib2.Request(final_url, _post_data)
|
req = urllib2.Request(final_url, _post_data)
|
||||||
response = urllib2.urlopen(req).read()
|
response = urllib2.urlopen(req).read()
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error('Exception: %s', e)
|
||||||
|
import traceback
|
||||||
|
top = traceback.format_exc()
|
||||||
|
self.logger.error("traceback: %s", top)
|
||||||
|
response = ""
|
||||||
# Ghetto hack for now because we don't the content type we are getting
|
# Ghetto hack for now because we don't the content type we are getting
|
||||||
# (Pointless to look at mime since it's not being set correctly always)
|
# (Pointless to look at mime since it's not being set correctly always)
|
||||||
try: return json.loads(response)
|
try: return json.loads(response)
|
||||||
|
@ -168,9 +177,9 @@ class AirtimeApiClient(object):
|
||||||
|
|
||||||
def get_schedule(self):
|
def get_schedule(self):
|
||||||
# TODO : properly refactor this routine
|
# TODO : properly refactor this routine
|
||||||
# For now thre return type is a little fucked for compatibility reasons
|
# For now the return type is a little fucked for compatibility reasons
|
||||||
try: return (True, self.services.export_url())
|
try: return (True, self.services.export_url())
|
||||||
except: (False, "")
|
except: return (False, None)
|
||||||
|
|
||||||
def notify_liquidsoap_started(self):
|
def notify_liquidsoap_started(self):
|
||||||
return self.services.notify_liquidsoap_started()
|
return self.services.notify_liquidsoap_started()
|
||||||
|
|
|
@ -28,10 +28,10 @@ start () {
|
||||||
|
|
||||||
stop () {
|
stop () {
|
||||||
monit unmonitor airtime-liquidsoap >/dev/null 2>&1
|
monit unmonitor airtime-liquidsoap >/dev/null 2>&1
|
||||||
/usr/lib/airtime/airtime_virtualenv/bin/python /usr/lib/airtime/pypo/bin/liquidsoap_scripts/liquidsoap_prepare_terminate.py
|
#send term signal after 10 seconds
|
||||||
|
timeout 10 /usr/lib/airtime/airtime_virtualenv/bin/python /usr/lib/airtime/pypo/bin/liquidsoap_scripts/liquidsoap_prepare_terminate.py
|
||||||
# Send TERM after 5 seconds, wait at most 30 seconds.
|
# Send TERM after 5 seconds, wait at most 30 seconds.
|
||||||
start-stop-daemon --stop --oknodo --retry 5 --quiet --pidfile $PIDFILE
|
start-stop-daemon --stop --oknodo --retry=TERM/10/KILL/5 --quiet --pidfile $PIDFILE
|
||||||
rm -f $PIDFILE
|
rm -f $PIDFILE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -195,28 +195,81 @@ def check_dj_client(user,password) =
|
||||||
hd == "True"
|
hd == "True"
|
||||||
end
|
end
|
||||||
|
|
||||||
def append_dj_inputs(master_harbor_input_port, master_harbor_input_mount_point, dj_harbor_input_port, dj_harbor_input_mount_point, s) =
|
def append_dj_inputs(master_harbor_input_port,
|
||||||
if master_harbor_input_port != 0 and master_harbor_input_mount_point != "" and dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then
|
master_harbor_input_mount_point,
|
||||||
master_dj = mksafe(audio_to_stereo(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client,
|
dj_harbor_input_port,
|
||||||
max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect)))
|
dj_harbor_input_mount_point,
|
||||||
dj_live = mksafe(audio_to_stereo(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client,
|
s) =
|
||||||
max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect)))
|
if master_harbor_input_port != 0
|
||||||
|
and master_harbor_input_mount_point != ""
|
||||||
|
and dj_harbor_input_port != 0
|
||||||
|
and dj_harbor_input_mount_point != "" then
|
||||||
|
|
||||||
|
master_dj = mksafe(
|
||||||
|
audio_to_stereo(
|
||||||
|
input.harbor(id="master_harbor",
|
||||||
|
master_harbor_input_mount_point,
|
||||||
|
port=master_harbor_input_port,
|
||||||
|
auth=check_master_dj_client,
|
||||||
|
max=40.,
|
||||||
|
on_connect=master_dj_connect,
|
||||||
|
on_disconnect=master_dj_disconnect)))
|
||||||
|
|
||||||
|
dj_live = mksafe(
|
||||||
|
audio_to_stereo(
|
||||||
|
input.harbor(id="live_dj_harbor",
|
||||||
|
dj_harbor_input_mount_point,
|
||||||
|
port=dj_harbor_input_port,
|
||||||
|
auth=check_dj_client,
|
||||||
|
max=40.,
|
||||||
|
on_connect=live_dj_connect,
|
||||||
|
on_disconnect=live_dj_disconnect)))
|
||||||
|
|
||||||
ignore(output.dummy(master_dj, fallible=true))
|
ignore(output.dummy(master_dj, fallible=true))
|
||||||
ignore(output.dummy(dj_live, fallible=true))
|
ignore(output.dummy(dj_live, fallible=true))
|
||||||
switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition, transition], [({!master_dj_enabled},master_dj), ({!live_dj_enabled},dj_live), ({true}, s)])
|
|
||||||
|
switch(id="master_dj_switch",
|
||||||
|
track_sensitive=false,
|
||||||
|
transitions=[transition, transition, transition],
|
||||||
|
[({!master_dj_enabled},master_dj),
|
||||||
|
({!live_dj_enabled},dj_live),
|
||||||
|
({true}, s)])
|
||||||
|
|
||||||
elsif master_harbor_input_port != 0 and master_harbor_input_mount_point != "" then
|
elsif master_harbor_input_port != 0 and master_harbor_input_mount_point != "" then
|
||||||
master_dj = mksafe(audio_to_stereo(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client,
|
master_dj = mksafe(
|
||||||
max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect)))
|
audio_to_stereo(
|
||||||
|
input.harbor(id="master_harbor",
|
||||||
|
master_harbor_input_mount_point,
|
||||||
|
port=master_harbor_input_port,
|
||||||
|
auth=check_master_dj_client,
|
||||||
|
max=40.,
|
||||||
|
on_connect=master_dj_connect,
|
||||||
|
on_disconnect=master_dj_disconnect)))
|
||||||
|
|
||||||
ignore(output.dummy(master_dj, fallible=true))
|
ignore(output.dummy(master_dj, fallible=true))
|
||||||
|
|
||||||
switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!master_dj_enabled},master_dj), ({true}, s)])
|
switch(id="master_dj_switch",
|
||||||
|
track_sensitive=false,
|
||||||
|
transitions=[transition, transition],
|
||||||
|
[({!master_dj_enabled},master_dj), ({true}, s)])
|
||||||
|
|
||||||
elsif dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then
|
elsif dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then
|
||||||
dj_live = mksafe(audio_to_stereo(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client,
|
dj_live = mksafe(
|
||||||
max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect)))
|
audio_to_stereo(
|
||||||
|
input.harbor(id="live_dj_harbor",
|
||||||
|
dj_harbor_input_mount_point,
|
||||||
|
port=dj_harbor_input_port,
|
||||||
|
auth=check_dj_client,
|
||||||
|
max=40.,
|
||||||
|
on_connect=live_dj_connect,
|
||||||
|
on_disconnect=live_dj_disconnect)))
|
||||||
|
|
||||||
ignore(output.dummy(dj_live, fallible=true))
|
ignore(output.dummy(dj_live, fallible=true))
|
||||||
switch(id="live_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!live_dj_enabled},dj_live), ({true}, s)])
|
|
||||||
|
switch(id="live_dj_switch",
|
||||||
|
track_sensitive=false,
|
||||||
|
transitions=[transition, transition],
|
||||||
|
[({!live_dj_enabled},dj_live), ({true}, s)])
|
||||||
else
|
else
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
|
|
||||||
check process airtime-playout
|
check process airtime-playout
|
||||||
with pidfile "/var/run/airtime-playout.pid"
|
with pidfile "/var/run/airtime-playout.pid"
|
||||||
start program = "/etc/init.d/airtime-playout monit-restart" with timeout 5 seconds
|
start program = "/etc/init.d/airtime-playout start" with timeout 5 seconds
|
||||||
stop program = "/etc/init.d/airtime-playout stop"
|
stop program = "/etc/init.d/airtime-playout stop"
|
||||||
|
|
|
@ -18,7 +18,8 @@ from std_err_override import LogWriter
|
||||||
from configobj import ConfigObj
|
from configobj import ConfigObj
|
||||||
|
|
||||||
# configure logging
|
# configure logging
|
||||||
logging.config.fileConfig("logging.cfg")
|
logging_cfg = os.path.join(os.path.dirname(__file__), "logging.cfg")
|
||||||
|
logging.config.fileConfig(logging_cfg)
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
LogWriter.override_std_err(logger)
|
LogWriter.override_std_err(logger)
|
||||||
|
|
||||||
|
@ -132,9 +133,10 @@ class PypoFetch(Thread):
|
||||||
elif(sourcename == "live_dj"):
|
elif(sourcename == "live_dj"):
|
||||||
command += "live_dj_harbor.kick\n"
|
command += "live_dj_harbor.kick\n"
|
||||||
|
|
||||||
lock.acquire()
|
|
||||||
try:
|
try:
|
||||||
|
lock.acquire()
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
logger.info(command)
|
||||||
tn.write(command)
|
tn.write(command)
|
||||||
tn.write('exit\n')
|
tn.write('exit\n')
|
||||||
tn.read_all()
|
tn.read_all()
|
||||||
|
@ -143,6 +145,24 @@ class PypoFetch(Thread):
|
||||||
finally:
|
finally:
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def telnet_send(logger, lock, commands):
|
||||||
|
try:
|
||||||
|
lock.acquire()
|
||||||
|
|
||||||
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
for i in commands:
|
||||||
|
logger.info(i)
|
||||||
|
tn.write(i)
|
||||||
|
|
||||||
|
tn.write('exit\n')
|
||||||
|
tn.read_all()
|
||||||
|
except Exception, e:
|
||||||
|
logger.error(str(e))
|
||||||
|
finally:
|
||||||
|
lock.release()
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def switch_source(logger, lock, sourcename, status):
|
def switch_source(logger, lock, sourcename, status):
|
||||||
logger.debug('Switching source: %s to "%s" status', sourcename, status)
|
logger.debug('Switching source: %s to "%s" status', sourcename, status)
|
||||||
|
@ -159,16 +179,26 @@ class PypoFetch(Thread):
|
||||||
else:
|
else:
|
||||||
command += "stop\n"
|
command += "stop\n"
|
||||||
|
|
||||||
lock.acquire()
|
PypoFetch.telnet_send(logger, lock, [command])
|
||||||
try:
|
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
|
||||||
tn.write(command)
|
#TODO: Merge this with switch_source
|
||||||
tn.write('exit\n')
|
def switch_source_temp(self, sourcename, status):
|
||||||
tn.read_all()
|
self.logger.debug('Switching source: %s to "%s" status', sourcename, status)
|
||||||
except Exception, e:
|
command = "streams."
|
||||||
logger.error(str(e))
|
if sourcename == "master_dj":
|
||||||
finally:
|
command += "master_dj_"
|
||||||
lock.release()
|
elif sourcename == "live_dj":
|
||||||
|
command += "live_dj_"
|
||||||
|
elif sourcename == "scheduled_play":
|
||||||
|
command += "scheduled_play_"
|
||||||
|
|
||||||
|
if status == "on":
|
||||||
|
command += "start\n"
|
||||||
|
else:
|
||||||
|
command += "stop\n"
|
||||||
|
|
||||||
|
return command
|
||||||
|
|
||||||
"""
|
"""
|
||||||
grabs some information that are needed to be set on bootstrap time
|
grabs some information that are needed to be set on bootstrap time
|
||||||
|
@ -179,19 +209,25 @@ class PypoFetch(Thread):
|
||||||
info = self.api_client.get_bootstrap_info()
|
info = self.api_client.get_bootstrap_info()
|
||||||
if info is None:
|
if info is None:
|
||||||
self.logger.error('Unable to get bootstrap info.. Exiting pypo...')
|
self.logger.error('Unable to get bootstrap info.. Exiting pypo...')
|
||||||
sys.exit(1)
|
|
||||||
else:
|
else:
|
||||||
self.logger.debug('info:%s', info)
|
self.logger.debug('info:%s', info)
|
||||||
|
commands = []
|
||||||
for k, v in info['switch_status'].iteritems():
|
for k, v in info['switch_status'].iteritems():
|
||||||
self.switch_source(self.logger, self.telnet_lock, k, v)
|
commands.append(self.switch_source_temp(k, v))
|
||||||
self.update_liquidsoap_stream_format(info['stream_label'])
|
|
||||||
self.update_liquidsoap_station_name(info['station_name'])
|
stream_format = info['stream_label']
|
||||||
self.update_liquidsoap_transition_fade(info['transition_fade'])
|
station_name = info['station_name']
|
||||||
|
fade = info['transition_fade']
|
||||||
|
|
||||||
|
commands.append(('vars.stream_metadata_type %s\n' % stream_format).encode('utf-8'))
|
||||||
|
commands.append(('vars.station_name %s\n' % station_name).encode('utf-8'))
|
||||||
|
commands.append(('vars.default_dj_fade %s\n' % fade).encode('utf-8'))
|
||||||
|
PypoFetch.telnet_send(self.logger, self.telnet_lock, commands)
|
||||||
|
|
||||||
def restart_liquidsoap(self):
|
def restart_liquidsoap(self):
|
||||||
|
|
||||||
self.telnet_lock.acquire()
|
|
||||||
try:
|
try:
|
||||||
|
self.telnet_lock.acquire()
|
||||||
self.logger.info("Restarting Liquidsoap")
|
self.logger.info("Restarting Liquidsoap")
|
||||||
subprocess.call('/etc/init.d/airtime-liquidsoap restart', shell=True)
|
subprocess.call('/etc/init.d/airtime-liquidsoap restart', shell=True)
|
||||||
|
|
||||||
|
@ -217,7 +253,7 @@ class PypoFetch(Thread):
|
||||||
self.set_bootstrap_variables()
|
self.set_bootstrap_variables()
|
||||||
#get the most up to date schedule, which will #initiate the process
|
#get the most up to date schedule, which will #initiate the process
|
||||||
#of making sure Liquidsoap is playing the schedule
|
#of making sure Liquidsoap is playing the schedule
|
||||||
self.manual_schedule_fetch()
|
self.persistent_manual_schedule_fetch(max_attempts=5)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
|
|
||||||
|
@ -322,16 +358,21 @@ class PypoFetch(Thread):
|
||||||
This function updates the bootup time variable in Liquidsoap script
|
This function updates the bootup time variable in Liquidsoap script
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.telnet_lock.acquire()
|
|
||||||
try:
|
try:
|
||||||
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
# update the boot up time of Liquidsoap. Since Liquidsoap is not restarting,
|
# update the boot up time of Liquidsoap. Since Liquidsoap is not restarting,
|
||||||
# we are manually adjusting the bootup time variable so the status msg will get
|
# we are manually adjusting the bootup time variable so the status msg will get
|
||||||
# updated.
|
# updated.
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
boot_up_time_command = "vars.bootup_time " + str(current_time) + "\n"
|
boot_up_time_command = "vars.bootup_time " + str(current_time) + "\n"
|
||||||
|
self.logger.info(boot_up_time_command)
|
||||||
tn.write(boot_up_time_command)
|
tn.write(boot_up_time_command)
|
||||||
tn.write("streams.connection_status\n")
|
|
||||||
|
connection_status = "streams.connection_status\n"
|
||||||
|
self.logger.info(connection_status)
|
||||||
|
tn.write(connection_status)
|
||||||
|
|
||||||
tn.write('exit\n')
|
tn.write('exit\n')
|
||||||
|
|
||||||
output = tn.read_all()
|
output = tn.read_all()
|
||||||
|
@ -356,6 +397,7 @@ class PypoFetch(Thread):
|
||||||
if(status == "true"):
|
if(status == "true"):
|
||||||
self.api_client.notify_liquidsoap_status("OK", stream_id, str(fake_time))
|
self.api_client.notify_liquidsoap_status("OK", stream_id, str(fake_time))
|
||||||
|
|
||||||
|
|
||||||
def update_liquidsoap_stream_format(self, stream_format):
|
def update_liquidsoap_stream_format(self, stream_format):
|
||||||
# Push stream metadata to liquidsoap
|
# Push stream metadata to liquidsoap
|
||||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||||
|
@ -395,8 +437,8 @@ class PypoFetch(Thread):
|
||||||
self.logger.info(LS_HOST)
|
self.logger.info(LS_HOST)
|
||||||
self.logger.info(LS_PORT)
|
self.logger.info(LS_PORT)
|
||||||
|
|
||||||
self.telnet_lock.acquire()
|
|
||||||
try:
|
try:
|
||||||
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
command = ('vars.station_name %s\n' % station_name).encode('utf-8')
|
command = ('vars.station_name %s\n' % station_name).encode('utf-8')
|
||||||
self.logger.info(command)
|
self.logger.info(command)
|
||||||
|
@ -488,10 +530,20 @@ class PypoFetch(Thread):
|
||||||
self.process_schedule(self.schedule_data)
|
self.process_schedule(self.schedule_data)
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
def persistent_manual_schedule_fetch(self, max_attempts=1):
|
||||||
|
success = False
|
||||||
|
num_attempts = 0
|
||||||
|
while not success and num_attempts < max_attempts:
|
||||||
|
success = self.manual_schedule_fetch()
|
||||||
|
num_attempts += 1
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
# Bootstrap: since we are just starting up, we need to grab the
|
# Bootstrap: since we are just starting up, we need to grab the
|
||||||
# most recent schedule. After that we can just wait for updates.
|
# most recent schedule. After that we can just wait for updates.
|
||||||
success = self.manual_schedule_fetch()
|
success = self.persistent_manual_schedule_fetch(max_attempts=5)
|
||||||
if success:
|
if success:
|
||||||
self.logger.info("Bootstrap schedule received: %s", self.schedule_data)
|
self.logger.info("Bootstrap schedule received: %s", self.schedule_data)
|
||||||
self.set_bootstrap_variables()
|
self.set_bootstrap_variables()
|
||||||
|
@ -519,7 +571,7 @@ class PypoFetch(Thread):
|
||||||
self.handle_message(message)
|
self.handle_message(message)
|
||||||
except Empty, e:
|
except Empty, e:
|
||||||
self.logger.info("Queue timeout. Fetching schedule manually")
|
self.logger.info("Queue timeout. Fetching schedule manually")
|
||||||
self.manual_schedule_fetch()
|
self.persistent_manual_schedule_fetch(max_attempts=5)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
import traceback
|
import traceback
|
||||||
top = traceback.format_exc()
|
top = traceback.format_exc()
|
||||||
|
|
|
@ -43,7 +43,7 @@ parser.add_option("-t", "--time", help="Liquidsoap boot up time", action="store"
|
||||||
parser.add_option("-x", "--source-name", help="source connection name", metavar="source_name")
|
parser.add_option("-x", "--source-name", help="source connection name", metavar="source_name")
|
||||||
parser.add_option("-y", "--source-status", help="source connection status", metavar="source_status")
|
parser.add_option("-y", "--source-status", help="source connection status", metavar="source_status")
|
||||||
parser.add_option("-w", "--webstream", help="JSON metadata associated with webstream", metavar="json_data")
|
parser.add_option("-w", "--webstream", help="JSON metadata associated with webstream", metavar="json_data")
|
||||||
parser.add_option("-n", "--liquidsoap-started", help="notify liquidsoap started", metavar="json_data", action="store_true", default=True)
|
parser.add_option("-n", "--liquidsoap-started", help="notify liquidsoap started", metavar="json_data", action="store_true", default=False)
|
||||||
|
|
||||||
|
|
||||||
# parse options
|
# parse options
|
||||||
|
|
|
@ -9,6 +9,7 @@ import logging.config
|
||||||
import telnetlib
|
import telnetlib
|
||||||
import calendar
|
import calendar
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
from pypofetch import PypoFetch
|
from pypofetch import PypoFetch
|
||||||
|
|
||||||
from Queue import Empty
|
from Queue import Empty
|
||||||
|
@ -21,7 +22,8 @@ from configobj import ConfigObj
|
||||||
|
|
||||||
|
|
||||||
# configure logging
|
# configure logging
|
||||||
logging.config.fileConfig("logging.cfg")
|
logging_cfg = os.path.join(os.path.dirname(__file__), "logging.cfg")
|
||||||
|
logging.config.fileConfig(logging_cfg)
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
LogWriter.override_std_err(logger)
|
LogWriter.override_std_err(logger)
|
||||||
|
|
||||||
|
@ -249,7 +251,7 @@ class PypoPush(Thread):
|
||||||
self.start_web_stream_buffer(current_item)
|
self.start_web_stream_buffer(current_item)
|
||||||
self.start_web_stream(current_item)
|
self.start_web_stream(current_item)
|
||||||
if is_file(current_item):
|
if is_file(current_item):
|
||||||
self.modify_cue_point(file_chain[0])
|
file_chain = self.modify_first_link_cue_point(file_chain)
|
||||||
self.push_to_liquidsoap(file_chain)
|
self.push_to_liquidsoap(file_chain)
|
||||||
#we've changed the queue, so let's refetch it
|
#we've changed the queue, so let's refetch it
|
||||||
liquidsoap_queue_approx = self.get_queue_items_from_liquidsoap()
|
liquidsoap_queue_approx = self.get_queue_items_from_liquidsoap()
|
||||||
|
@ -279,7 +281,7 @@ class PypoPush(Thread):
|
||||||
|
|
||||||
chain_to_push = file_chain[problem_at_iteration:]
|
chain_to_push = file_chain[problem_at_iteration:]
|
||||||
if len(chain_to_push) > 0:
|
if len(chain_to_push) > 0:
|
||||||
self.modify_cue_point(chain_to_push[0])
|
chain_to_push = self.modify_first_link_cue_point(chain_to_push)
|
||||||
self.push_to_liquidsoap(chain_to_push)
|
self.push_to_liquidsoap(chain_to_push)
|
||||||
|
|
||||||
|
|
||||||
|
@ -363,6 +365,18 @@ class PypoPush(Thread):
|
||||||
original_cue_in_td = timedelta(seconds=float(link['cue_in']))
|
original_cue_in_td = timedelta(seconds=float(link['cue_in']))
|
||||||
link['cue_in'] = self.date_interval_to_seconds(original_cue_in_td) + diff_sec
|
link['cue_in'] = self.date_interval_to_seconds(original_cue_in_td) + diff_sec
|
||||||
|
|
||||||
|
def modify_first_link_cue_point(self, chain):
|
||||||
|
if not len(chain):
|
||||||
|
return []
|
||||||
|
|
||||||
|
first_link = chain[0]
|
||||||
|
|
||||||
|
self.modify_cue_point(first_link)
|
||||||
|
if float(first_link['cue_in']) >= float(first_link['cue_out']):
|
||||||
|
chain = chain [1:]
|
||||||
|
|
||||||
|
return chain
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Returns two chains, original chain and current_chain. current_chain is a subset of
|
Returns two chains, original chain and current_chain. current_chain is a subset of
|
||||||
original_chain but can also be equal to original chain.
|
original_chain but can also be equal to original chain.
|
||||||
|
|
18
python_apps/pypo/tests/run_tests.sh
Executable file
18
python_apps/pypo/tests/run_tests.sh
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
which py.test
|
||||||
|
pytest_exist=$?
|
||||||
|
|
||||||
|
if [ "$pytest_exist" != "0" ]; then
|
||||||
|
echo "Need to have py.test installed. Exiting..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT=`readlink -f $0`
|
||||||
|
# Absolute directory this script is in
|
||||||
|
SCRIPTPATH=`dirname $SCRIPT`
|
||||||
|
|
||||||
|
export PYTHONPATH=$PYTHONPATH:$SCRIPTPATH/..:$SCRIPTPATH/../..
|
||||||
|
|
||||||
|
py.test
|
||||||
|
|
26
python_apps/pypo/tests/test_modify_cue_in.py
Normal file
26
python_apps/pypo/tests/test_modify_cue_in.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from pypopush import PypoPush
|
||||||
|
from threading import Lock
|
||||||
|
from Queue import Queue
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
pypoPush_q = Queue()
|
||||||
|
telnet_lock = Lock()
|
||||||
|
|
||||||
|
pp = PypoPush(pypoPush_q, telnet_lock)
|
||||||
|
|
||||||
|
def test_modify_cue_in():
|
||||||
|
link = pp.modify_first_link_cue_point([])
|
||||||
|
assert len(link) == 0
|
||||||
|
|
||||||
|
min_ago = datetime.datetime.utcnow() - datetime.timedelta(minutes = 1)
|
||||||
|
link = [{"start":min_ago.strftime("%Y-%m-%d-%H-%M-%S"),
|
||||||
|
"cue_in":"0", "cue_out":"30"}]
|
||||||
|
link = pp.modify_first_link_cue_point(link)
|
||||||
|
assert len(link) == 0
|
||||||
|
|
||||||
|
link = [{"start":min_ago.strftime("%Y-%m-%d-%H-%M-%S"),
|
||||||
|
"cue_in":"0", "cue_out":"70"}]
|
||||||
|
link = pp.modify_first_link_cue_point(link)
|
||||||
|
assert len(link) == 1
|
||||||
|
|
|
@ -170,6 +170,8 @@ def WatchAddAction(option, opt, value, parser):
|
||||||
print "%s added to watched folder list successfully" % path
|
print "%s added to watched folder list successfully" % path
|
||||||
else:
|
else:
|
||||||
print "Adding a watched folder failed: %s" % res['msg']['error']
|
print "Adding a watched folder failed: %s" % res['msg']['error']
|
||||||
|
print "This error most likely caused by wrong permissions"
|
||||||
|
print "Try fixing this error by chmodding the parent directory(ies)"
|
||||||
else:
|
else:
|
||||||
print "Given path is not a directory: %s" % path
|
print "Given path is not a directory: %s" % path
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue