Merge branch 'devel' of dev.sourcefabric.org:airtime into devel

This commit is contained in:
Daniel 2012-03-01 15:35:21 -05:00
commit f8e7d6eae3
57 changed files with 2514 additions and 2373 deletions

View File

@ -27,6 +27,7 @@ class ApiController extends Zend_Controller_Action
->addActionContext('live-chat', 'json') ->addActionContext('live-chat', 'json')
->addActionContext('update-file-system-mount', 'json') ->addActionContext('update-file-system-mount', 'json')
->addActionContext('handle-watched-dir-missing', 'json') ->addActionContext('handle-watched-dir-missing', 'json')
->addActionContext('rabbitmq-do-push', 'json')
->initContext(); ->initContext();
} }
@ -276,17 +277,19 @@ class ApiController extends Zend_Controller_Action
$api_key = $this->_getParam('api_key'); $api_key = $this->_getParam('api_key');
/*
if(!in_array($api_key, $CC_CONFIG["apiKey"])) if(!in_array($api_key, $CC_CONFIG["apiKey"]))
{ {
header('HTTP/1.0 401 Unauthorized'); header('HTTP/1.0 401 Unauthorized');
print 'You are not allowed to access this resource. '; print 'You are not allowed to access this resource. ';
exit; exit;
} }
* */
PEAR::setErrorHandling(PEAR_ERROR_RETURN); PEAR::setErrorHandling(PEAR_ERROR_RETURN);
$result = Application_Model_Schedule::GetScheduledPlaylists(); $data = Application_Model_Schedule::GetScheduledPlaylists();
echo json_encode($result); echo json_encode($data, JSON_FORCE_OBJECT);
} }
public function notifyMediaItemStartPlayAction() public function notifyMediaItemStartPlayAction()
@ -316,6 +319,7 @@ class ApiController extends Zend_Controller_Action
} }
} }
/*
public function notifyScheduleGroupPlayAction() public function notifyScheduleGroupPlayAction()
{ {
global $CC_CONFIG; global $CC_CONFIG;
@ -355,6 +359,7 @@ class ApiController extends Zend_Controller_Action
exit; exit;
} }
} }
*/
public function recordedShowsAction() public function recordedShowsAction()
{ {
@ -901,5 +906,26 @@ class ApiController extends Zend_Controller_Action
$dir = base64_decode($request->getParam('dir')); $dir = base64_decode($request->getParam('dir'));
Application_Model_MusicDir::removeWatchedDir($dir, false); Application_Model_MusicDir::removeWatchedDir($dir, false);
} }
/* This action is for use by our dev scripts, that make
* a change to the database and we want rabbitmq to send
* out a message to pypo that a potential change has been made. */
public function rabbitmqDoPushAction(){
global $CC_CONFIG;
$request = $this->getRequest();
$api_key = $request->getParam('api_key');
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
{
header('HTTP/1.0 401 Unauthorized');
print 'You are not allowed to access this resource.';
exit;
}
Logging::log("Notifying RabbitMQ to send message to pypo");
Application_Model_RabbitMq::PushSchedule();
}
} }

View File

@ -55,7 +55,9 @@ class LibraryController extends Zend_Controller_Action
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.TableTools.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.TableTools.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/main_library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headLink()->appendStylesheet($baseUrl.'/css/media_library.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'/css/media_library.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);

View File

@ -197,7 +197,7 @@ class PlaylistController extends Zend_Controller_Action
public function addItemsAction() public function addItemsAction()
{ {
$ids = $this->_getParam('ids'); $ids = $this->_getParam('ids', array());
$ids = (!is_array($ids)) ? array($ids) : $ids; $ids = (!is_array($ids)) ? array($ids) : $ids;
$afterItem = $this->_getParam('afterItem', null); $afterItem = $this->_getParam('afterItem', null);
$addType = $this->_getParam('type', 'after'); $addType = $this->_getParam('type', 'after');

View File

@ -59,6 +59,29 @@ class ScheduleController extends Zend_Controller_Action
$this->view->headLink()->appendStylesheet($baseUrl.'/css/add-show.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'/css/add-show.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
//Start Show builder JS/CSS requirements
$this->view->headScript()->appendFile($baseUrl.'/js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/js/jquery.dataTables.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.pluginAPI.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.fnSetFilteringDelay.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColVis.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.TableTools.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($this->view->baseUrl('/js/airtime/library/events/library_showbuilder.js?'.$CC_CONFIG['airtime_version']),'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headLink()->appendStylesheet($baseUrl.'/css/media_library.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'/css/datatables/css/ColVis.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'/css/datatables/css/ColReorder.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'/css/TableTools.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'/css/showbuilder.css?'.$CC_CONFIG['airtime_version']);
//End Show builder JS/CSS requirements
Application_Model_Schedule::createNewFormSections($this->view); Application_Model_Schedule::createNewFormSections($this->view);
$userInfo = Zend_Auth::getInstance()->getStorage()->read(); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
@ -78,10 +101,12 @@ 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))) if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
$editable = true; $editable = true;
else }
else {
$editable = false; $editable = false;
}
$this->view->events = Application_Model_Show::getFullCalendarEvents($start, $end, $editable); $this->view->events = Application_Model_Show::getFullCalendarEvents($start, $end, $editable);
} }
@ -95,19 +120,19 @@ 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))) { if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
try{ try {
$showInstance = new Application_Model_ShowInstance($showInstanceId); $showInstance = new Application_Model_ShowInstance($showInstanceId);
}catch(Exception $e){ } catch (Exception $e){
$this->view->show_error = true; $this->view->show_error = true;
return false; return false;
} }
$error = $showInstance->moveShow($deltaDay, $deltaMin); $error = $showInstance->moveShow($deltaDay, $deltaMin);
} }
if(isset($error)) if (isset($error)) {
$this->view->error = $error; $this->view->error = $error;
}
} }
public function resizeShowAction() public function resizeShowAction()
@ -200,7 +225,7 @@ class ScheduleController extends Zend_Controller_Action
&& !$instance->isRebroadcast()) { && !$instance->isRebroadcast()) {
$menu["schedule"] = array("name"=> "Add / Remove Content", $menu["schedule"] = array("name"=> "Add / Remove Content",
"url" => "/showbuilder/index/"); "url" => "/showbuilder/builder-dialog/");
$menu["clear"] = array("name"=> "Remove All Content", "icon" => "delete", $menu["clear"] = array("name"=> "Remove All Content", "icon" => "delete",
"url" => "/schedule/clear-show"); "url" => "/schedule/clear-show");

View File

@ -9,6 +9,7 @@ class ShowbuilderController extends Zend_Controller_Action
$ajaxContext->addActionContext('schedule-move', 'json') $ajaxContext->addActionContext('schedule-move', 'json')
->addActionContext('schedule-add', 'json') ->addActionContext('schedule-add', 'json')
->addActionContext('schedule-remove', 'json') ->addActionContext('schedule-remove', 'json')
->addActionContext('builder-dialog', 'json')
->addActionContext('builder-feed', 'json') ->addActionContext('builder-feed', 'json')
->initContext(); ->initContext();
} }
@ -53,11 +54,40 @@ class ShowbuilderController extends Zend_Controller_Action
$this->view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); $this->view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds");
$this->view->headScript()->appendFile($baseUrl.'/js/timepicker/jquery.ui.timepicker.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/timepicker/jquery.ui.timepicker.js','text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/builder.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/builder.js','text/javascript');
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/main_builder.js','text/javascript');
$this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.ui.timepicker.css'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.ui.timepicker.css');
$this->view->headLink()->appendStylesheet($baseUrl.'/css/showbuilder.css'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/showbuilder.css');
} }
public function builderDialogAction() {
$request = $this->getRequest();
$id = $request->getParam("id");
$instance = CcShowInstancesQuery::create()->findPK($id);
if (is_null($instance)) {
$this->view->error = "show does not exist";
return;
}
$start = $instance->getDbStarts(null);
$start->setTimezone(new DateTimeZone(date_default_timezone_get()));
$end = $instance->getDbEnds(null);
$end->setTimezone(new DateTimeZone(date_default_timezone_get()));
$show_name = $instance->getCcShow()->getDbName();
$start_time = $start->format("Y-m-d H:i:s");
$end_time = $end->format("Y-m-d H:i:s");
$this->view->title = "{$show_name}: {$start_time} - {$end_time}";
$this->view->start = $instance->getDbStarts("U");
$this->view->end = $instance->getDbEnds("U");
$this->view->dialog = $this->view->render('showbuilder/builderDialog.phtml');
}
public function builderFeedAction() { public function builderFeedAction() {
$request = $this->getRequest(); $request = $this->getRequest();

View File

@ -6,7 +6,7 @@ class Zend_Filter_ImageSize implements Zend_Filter_Interface {
throw new Zend_Filter_Exception('Image does not exist: ' . $value); throw new Zend_Filter_Exception('Image does not exist: ' . $value);
} }
$image = imageCreateFromString(file_get_contents($value)); $image = imagecreatefromstring(file_get_contents($value));
if (false === $image) { if (false === $image) {
throw new Zend_Filter_Exception('Can\'t load image: ' . $value); throw new Zend_Filter_Exception('Can\'t load image: ' . $value);
} }

View File

@ -29,7 +29,7 @@ class Application_Model_RabbitMq
$EXCHANGE = 'airtime-pypo'; $EXCHANGE = 'airtime-pypo';
$channel->exchange_declare($EXCHANGE, 'direct', false, true); $channel->exchange_declare($EXCHANGE, 'direct', false, true);
$data = json_encode($md); $data = json_encode($md, JSON_FORCE_OBJECT);
$msg = new AMQPMessage($data, array('content_type' => 'text/plain')); $msg = new AMQPMessage($data, array('content_type' => 'text/plain'));
$channel->basic_publish($msg, $EXCHANGE); $channel->basic_publish($msg, $EXCHANGE);

View File

@ -46,87 +46,6 @@ class Application_Model_Schedule {
} }
/**
* Returns array indexed by:
* "playlistId"/"playlist_id" (aliases to the same thing)
* "start"/"starts" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
* "end"/"ends" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
* "group_id"/"id" (aliases to the same thing)
* "clip_length" (for audio clips this is the length of the audio clip,
* for playlists this is the length of the entire playlist)
* "name" (playlist only)
* "creator" (playlist only)
* "file_id" (audioclip only)
* "count" (number of items in the playlist, always 1 for audioclips.
* Note that playlists with one item will also have count = 1.
*
* @param string $p_fromDateTime
* In the format YYYY-MM-DD HH:MM:SS.nnnnnn
* @param string $p_toDateTime
* In the format YYYY-MM-DD HH:MM:SS.nnnnnn
* @param boolean $p_playlistsOnly
* Retrieve playlists as a single item.
* @return array
* Returns empty array if nothing found
*/
public static function GetItems($p_currentDateTime, $p_toDateTime, $p_playlistsOnly = true)
{
global $CC_CONFIG, $CC_DBC;
$rows = array();
if (!$p_playlistsOnly) {
$sql = "SELECT * FROM ".$CC_CONFIG["scheduleTable"]
." WHERE (starts >= TIMESTAMP '$p_currentDateTime') "
." AND (ends <= TIMESTAMP '$p_toDateTime')";
$rows = $CC_DBC->GetAll($sql);
foreach ($rows as &$row) {
$row["count"] = "1";
$row["playlistId"] = $row["playlist_id"];
$row["start"] = $row["starts"];
$row["end"] = $row["ends"];
$row["id"] = $row["group_id"];
}
} else {
$sql = "SELECT MIN(pt.creator) AS creator,"
." st.group_id,"
." SUM(st.clip_length) AS clip_length,"
." MIN(st.file_id) AS file_id,"
." COUNT(*) as count,"
." MIN(st.playlist_id) AS playlist_id,"
." MIN(st.starts) AS starts,"
." MAX(st.ends) AS ends,"
." MIN(sh.name) AS show_name,"
." MIN(si.starts) AS show_start,"
." MAX(si.ends) AS show_end"
." FROM $CC_CONFIG[scheduleTable] as st"
." LEFT JOIN $CC_CONFIG[playListTable] as pt"
." ON st.playlist_id = pt.id"
." LEFT JOIN $CC_CONFIG[showInstances] as si"
." ON st.instance_id = si.id"
." LEFT JOIN $CC_CONFIG[showTable] as sh"
." ON si.show_id = sh.id"
//The next line ensures we only get songs that haven't ended yet
." WHERE (st.ends >= TIMESTAMP '$p_currentDateTime')"
." AND (st.ends <= TIMESTAMP '$p_toDateTime')"
//next line makes sure that we aren't returning items that
//are past the show's scheduled timeslot.
." AND (st.starts < si.ends)"
." GROUP BY st.group_id"
." ORDER BY starts";
$rows = $CC_DBC->GetAll($sql);
if (!PEAR::isError($rows)) {
foreach ($rows as &$row) {
$row["playlistId"] = $row["playlist_id"];
$row["start"] = $row["starts"];
$row["end"] = $row["ends"];
$row["id"] = $row["group_id"];
}
}
}
return $rows;
}
/** /**
* Returns data related to the scheduled items. * Returns data related to the scheduled items.
* *
@ -318,6 +237,7 @@ class Application_Model_Schedule {
sched.starts AS sched_starts, sched.ends AS sched_ends, sched.id AS sched_id, sched.starts AS sched_starts, sched.ends AS sched_ends, sched.id AS sched_id,
sched.cue_in AS cue_in, sched.cue_out AS cue_out, sched.cue_in AS cue_in, sched.cue_out AS cue_out,
sched.fade_in AS fade_in, sched.fade_out AS fade_out, sched.fade_in AS fade_in, sched.fade_out AS fade_out,
sched.status AS sched_status,
ft.track_title AS file_track_title, ft.artist_name AS file_artist_name, ft.track_title AS file_track_title, ft.artist_name AS file_artist_name,
ft.album_title AS file_album_title, ft.length AS file_length ft.album_title AS file_album_title, ft.length AS file_length
@ -476,6 +396,120 @@ class Application_Model_Schedule {
return $diff; return $diff;
} }
/**
* Returns array indexed by:
* "playlistId"/"playlist_id" (aliases to the same thing)
* "start"/"starts" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
* "end"/"ends" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
* "group_id"/"id" (aliases to the same thing)
* "clip_length" (for audio clips this is the length of the audio clip,
* for playlists this is the length of the entire playlist)
* "name" (playlist only)
* "creator" (playlist only)
* "file_id" (audioclip only)
* "count" (number of items in the playlist, always 1 for audioclips.
* Note that playlists with one item will also have count = 1.
*
* @param string $p_fromDateTime
* In the format YYYY-MM-DD HH:MM:SS.nnnnnn
* @param string $p_toDateTime
* In the format YYYY-MM-DD HH:MM:SS.nnnnnn
* @param boolean $p_playlistsOnly
* Retrieve playlists as a single item.
* @return array
* Returns null if nothing found
*/
public static function GetItems($p_currentDateTime, $p_toDateTime) {
global $CC_CONFIG, $CC_DBC;
$rows = array();
$sql = "SELECT st.file_id AS file_id,"
." st.id as id,"
." st.starts AS start,"
." st.ends AS end,"
." st.cue_in AS cue_in,"
." st.cue_out AS cue_out,"
." st.fade_in AS fade_in,"
." st.fade_out AS fade_out,"
." si.starts as show_start,"
." si.ends as show_end"
." FROM $CC_CONFIG[scheduleTable] as st"
." LEFT JOIN $CC_CONFIG[showInstances] as si"
." ON st.instance_id = si.id"
." ORDER BY start";
Logging::log($sql);
$rows = $CC_DBC->GetAll($sql);
if (PEAR::isError($rows)) {
return null;
}
return $rows;
}
public static function GetScheduledPlaylists($p_fromDateTime = null, $p_toDateTime = null){
global $CC_CONFIG, $CC_DBC;
/* if $p_fromDateTime and $p_toDateTime function parameters are null, then set range
* from "now" to "now + 24 hours". */
if (is_null($p_fromDateTime)) {
$t1 = new DateTime("@".time());
$range_start = $t1->format("Y-m-d H:i:s");
} else {
$range_start = Application_Model_Schedule::PypoTimeToAirtimeTime($p_fromDateTime);
}
if (is_null($p_fromDateTime)) {
$t2 = new DateTime("@".time());
$t2->add(new DateInterval("PT24H"));
$range_end = $t2->format("Y-m-d H:i:s");
} else {
$range_end = Application_Model_Schedule::PypoTimeToAirtimeTime($p_toDateTime);
}
// Scheduler wants everything in a playlist
$items = Application_Model_Schedule::GetItems($range_start, $range_end);
$data = array();
$utcTimeZone = new DateTimeZone("UTC");
$data["status"] = array();
$data["media"] = array();
foreach ($items as $item){
$storedFile = Application_Model_StoredFile::Recall($item["file_id"]);
$uri = $storedFile->getFileUrlUsingConfigAddress();
$showEndDateTime = new DateTime($item["show_end"], $utcTimeZone);
$trackEndDateTime = new DateTime($item["end"], $utcTimeZone);
/* Note: cue_out and end are always the same. */
/* TODO: Not all tracks will have "show_end" */
if ($trackEndDateTime->getTimestamp() > $showEndDateTime->getTimestamp()){
$diff = $trackEndDateTime->getTimestamp() - $showEndDateTime->getTimestamp();
//assuming ends takes cue_out into assumption
$item["cue_out"] = $item["cue_out"] - $diff;
}
$start = Application_Model_Schedule::AirtimeTimeToPypoTime($item["start"]);
$data["media"][$start] = array(
'id' => $storedFile->getGunid(),
'row_id' => $item["id"],
'uri' => $uri,
'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]),
'fade_out' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_out"]),
'cue_in' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_in"]),
'cue_out' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_out"]),
'start' => $start,
'end' => Application_Model_Schedule::AirtimeTimeToPypoTime($item["end"])
);
}
return $data;
}
/** /**
* Export the schedule in json formatted for pypo (the liquidsoap scheduler) * Export the schedule in json formatted for pypo (the liquidsoap scheduler)
@ -485,7 +519,7 @@ class Application_Model_Schedule {
* @param string $p_toDateTime * @param string $p_toDateTime
* In the format "YYYY-MM-DD-HH-mm-SS" * In the format "YYYY-MM-DD-HH-mm-SS"
*/ */
public static function GetScheduledPlaylists($p_fromDateTime = null, $p_toDateTime = null) public static function GetScheduledPlaylistsOld($p_fromDateTime = null, $p_toDateTime = null)
{ {
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
@ -546,7 +580,6 @@ class Application_Model_Schedule {
$starts = Application_Model_Schedule::AirtimeTimeToPypoTime($item["starts"]); $starts = Application_Model_Schedule::AirtimeTimeToPypoTime($item["starts"]);
$medias[$starts] = array( $medias[$starts] = array(
'row_id' => $item["id"],
'id' => $storedFile->getGunid(), 'id' => $storedFile->getGunid(),
'uri' => $uri, 'uri' => $uri,
'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]), 'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]),
@ -554,7 +587,6 @@ class Application_Model_Schedule {
'fade_cross' => 0, 'fade_cross' => 0,
'cue_in' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_in"]), 'cue_in' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_in"]),
'cue_out' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_out"]), 'cue_out' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_out"]),
'export_source' => 'scheduler',
'start' => $starts, 'start' => $starts,
'end' => Application_Model_Schedule::AirtimeTimeToPypoTime($item["ends"]) 'end' => Application_Model_Schedule::AirtimeTimeToPypoTime($item["ends"])
); );

View File

@ -229,6 +229,15 @@ class Application_Model_Scheduler {
} }
} }
//update the status flag in cc_schedule.
$instances = CcShowInstancesQuery::create()
->filterByPrimaryKeys($affectedShowInstances)
->find($this->con);
foreach ($instances as $instance) {
$instance->updateScheduleStatus($this->con);
}
//update the last scheduled timestamp. //update the last scheduled timestamp.
CcShowInstancesQuery::create() CcShowInstancesQuery::create()
->filterByPrimaryKeys($affectedShowInstances) ->filterByPrimaryKeys($affectedShowInstances)
@ -383,11 +392,20 @@ class Application_Model_Scheduler {
} }
} }
foreach($showInstances as $instance) { foreach ($showInstances as $instance) {
$this->removeGaps($instance); $this->removeGaps($instance);
} }
} }
//update the status flag in cc_schedule.
$instances = CcShowInstancesQuery::create()
->filterByPrimaryKeys($showInstances)
->find($this->con);
foreach ($instances as $instance) {
$instance->updateScheduleStatus($this->con);
}
//update the last scheduled timestamp. //update the last scheduled timestamp.
CcShowInstancesQuery::create() CcShowInstancesQuery::create()
->filterByPrimaryKeys($showInstances) ->filterByPrimaryKeys($showInstances)

View File

@ -125,10 +125,12 @@ class Application_Model_Show {
} }
$hours = $deltaMin/60; $hours = $deltaMin/60;
if($hours > 0) if ($hours > 0) {
$hours = floor($hours); $hours = floor($hours);
else }
else {
$hours = ceil($hours); $hours = ceil($hours);
}
$mins = abs($deltaMin%60); $mins = abs($deltaMin%60);
@ -149,6 +151,28 @@ class Application_Model_Show {
//do both the queries at once. //do both the queries at once.
$CC_DBC->query($sql); $CC_DBC->query($sql);
$con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME);
$con->beginTransaction();
try {
//update the status flag in cc_schedule.
$instances = CcShowInstancesQuery::create()
->filterByDbStarts($current_timestamp, Criteria::GREATER_EQUAL)
->filterByDbShowId($this->_showId)
->find($con);
foreach ($instances as $instance) {
$instance->updateScheduleStatus();
}
$con->commit();
}
catch (Exception $e) {
$con->rollback();
Logging::log("Couldn't update schedule status.");
Logging::log($e->getMessage());
}
Application_Model_RabbitMq::PushSchedule(); Application_Model_RabbitMq::PushSchedule();
} }
@ -1043,6 +1067,33 @@ class Application_Model_Show {
} }
} }
if ($data['add_show_id'] != -1) {
$con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME);
$con->beginTransaction();
//current timesamp in UTC.
$current_timestamp = gmdate("Y-m-d H:i:s");
try {
//update the status flag in cc_schedule.
$instances = CcShowInstancesQuery::create()
->filterByDbStarts($current_timestamp, Criteria::GREATER_EQUAL)
->filterByDbShowId($data['add_show_id'])
->find($con);
foreach ($instances as $instance) {
$instance->updateScheduleStatus();
}
$con->commit();
}
catch (Exception $e) {
$con->rollback();
Logging::log("Couldn't update schedule status.");
Logging::log($e->getMessage());
}
}
Application_Model_Show::populateShowUntil($showId); Application_Model_Show::populateShowUntil($showId);
Application_Model_RabbitMq::PushSchedule(); Application_Model_RabbitMq::PushSchedule();
return $showId; return $showId;
@ -1491,7 +1542,7 @@ class Application_Model_Show {
$events = array(); $events = array();
$interval = $start->diff($end); $interval = $start->diff($end);
$days = $interval->format('%a'); $days = $interval->format('%a');
$shows = Application_Model_Show::getShows($start, $end); $shows = Application_Model_Show::getShows($start, $end);
@ -1508,10 +1559,9 @@ class Application_Model_Show {
if ($editable && (strtotime($today_timestamp) < strtotime($show["starts"]))) { if ($editable && (strtotime($today_timestamp) < strtotime($show["starts"]))) {
$options["editable"] = true; $options["editable"] = true;
$events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
} else {
$events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
} }
$events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
} }
return $events; return $events;
@ -1521,10 +1571,6 @@ class Application_Model_Show {
{ {
$event = array(); $event = array();
if($show["rebroadcast"]) {
$event["disableResizing"] = true;
}
$startDateTime = new DateTime($show["starts"], new DateTimeZone("UTC")); $startDateTime = new DateTime($show["starts"], new DateTimeZone("UTC"));
$startDateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); $startDateTime->setTimezone(new DateTimeZone(date_default_timezone_get()));
@ -1538,29 +1584,27 @@ class Application_Model_Show {
$event["end"] = $endDateTime->format("Y-m-d H:i:s"); $event["end"] = $endDateTime->format("Y-m-d H:i:s");
$event["endUnix"] = $endDateTime->format("U"); $event["endUnix"] = $endDateTime->format("U");
$event["allDay"] = false; $event["allDay"] = false;
//$event["description"] = $show["description"];
$event["showId"] = intval($show["show_id"]); $event["showId"] = intval($show["show_id"]);
$event["record"] = intval($show["record"]); $event["record"] = intval($show["record"]);
$event["rebroadcast"] = intval($show["rebroadcast"]); $event["rebroadcast"] = intval($show["rebroadcast"]);
// get soundcloud_id // get soundcloud_id
if(!is_null($show["file_id"])){ if (!is_null($show["file_id"])){
$file = Application_Model_StoredFile::Recall($show["file_id"]); $file = Application_Model_StoredFile::Recall($show["file_id"]);
$soundcloud_id = $file->getSoundCloudId(); $soundcloud_id = $file->getSoundCloudId();
}else{
$soundcloud_id = null;
} }
$event["soundcloud_id"] = (is_null($soundcloud_id) ? -1 : $soundcloud_id);
$event["soundcloud_id"] = isset($soundcloud_id) ? $soundcloud_id : -1;
//event colouring //event colouring
if($show["color"] != "") { if ($show["color"] != "") {
$event["textColor"] = "#".$show["color"]; $event["textColor"] = "#".$show["color"];
} }
if($show["background_color"] != "") { if ($show["background_color"] != "") {
$event["color"] = "#".$show["background_color"]; $event["color"] = "#".$show["background_color"];
} }
foreach($options as $key=>$value) { foreach ($options as $key => $value) {
$event[$key] = $value; $event[$key] = $value;
} }

View File

@ -1,6 +1,7 @@
<?php <?php
require_once 'formatters/LengthFormatter.php'; require_once 'formatters/LengthFormatter.php';
require_once 'formatters/TimeFilledFormatter.php';
class Application_Model_ShowBuilder { class Application_Model_ShowBuilder {
@ -30,7 +31,8 @@ class Application_Model_ShowBuilder {
"cuein" => "", "cuein" => "",
"cueout" => "", "cueout" => "",
"fadein" => "", "fadein" => "",
"fadeout" => "" "fadeout" => "",
"current" => false,
); );
/* /*
@ -47,37 +49,14 @@ class Application_Model_ShowBuilder {
$this->epoch_now = time(); $this->epoch_now = time();
} }
private function formatTimeFilled($p_sec) { //check to see if this row should be editable.
$formatted = "";
$sign = ($p_sec < 0) ? "-" : "+";
$time = Application_Model_Playlist::secondsToPlaylistTime(abs($p_sec));
Logging::log("time is: ".$time);
$info = explode(":", $time);
$formatted .= $sign;
if (intval($info[0]) > 0) {
$info[0] = ltrim($info[0], "0");
$formatted .= " {$info[0]}h";
}
if (intval($info[1]) > 0) {
$info[1] = ltrim($info[1], "0");
$formatted .= " {$info[1]}m";
}
if (intval($info[2]) > 0) {
$sec = round($info[2], 0);
$formatted .= " {$sec}s";
}
return $formatted;
}
private function isAllowed($p_item, &$row) { private function isAllowed($p_item, &$row) {
//cannot schedule in a recorded show.
if (intval($p_item["si_record"]) === 1) {
return;
}
$showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC")); $showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC"));
//can only schedule the show if it hasn't started and you are allowed. //can only schedule the show if it hasn't started and you are allowed.
@ -86,27 +65,10 @@ class Application_Model_ShowBuilder {
} }
} }
//information about whether a track is inside|boundary|outside a show.
private function getItemStatus($p_item, &$row) { private function getItemStatus($p_item, &$row) {
$showEndDT = new DateTime($p_item["si_ends"]); $row["status"] = intval($p_item["sched_status"]);
$schedStartDT = new DateTime($p_item["sched_starts"]);
$schedEndDT = new DateTime($p_item["sched_ends"]);
$showEndEpoch = intval($showEndDT->format("U"));
$schedStartEpoch = intval($schedStartDT->format("U"));
$schedEndEpoch = intval($schedEndDT->format("U"));
if ($schedEndEpoch < $showEndEpoch) {
$status = 0; //item will playout in full
}
else if ($schedStartEpoch < $showEndEpoch && $schedEndEpoch > $showEndEpoch) {
$status = 1; //item is on boundry
}
else {
$status = 2; //item is overscheduled won't play.
}
$row["status"] = $status;
} }
private function getRowTimestamp($p_item, &$row) { private function getRowTimestamp($p_item, &$row) {
@ -121,6 +83,16 @@ class Application_Model_ShowBuilder {
$row["timestamp"] = $ts; $row["timestamp"] = $ts;
} }
private function isCurrent($p_epochItemStart, $p_epochItemEnd) {
$current = false;
if ($this->epoch_now >= $p_epochItemStart && $this->epoch_now < $p_epochItemEnd) {
$current = true;
}
return $current;
}
private function makeHeaderRow($p_item) { private function makeHeaderRow($p_item) {
$row = $this->defaultRowArray; $row = $this->defaultRowArray;
@ -148,8 +120,8 @@ class Application_Model_ShowBuilder {
private function makeScheduledItemRow($p_item) { private function makeScheduledItemRow($p_item) {
$row = $this->defaultRowArray; $row = $this->defaultRowArray;
$this->isAllowed($p_item, $row);
$this->getRowTimestamp($p_item, $row); $this->getRowTimestamp($p_item, $row);
$this->isAllowed($p_item, $row);
if (isset($p_item["sched_starts"])) { if (isset($p_item["sched_starts"])) {
@ -157,9 +129,19 @@ class Application_Model_ShowBuilder {
$schedStartDT->setTimezone(new DateTimeZone($this->timezone)); $schedStartDT->setTimezone(new DateTimeZone($this->timezone));
$schedEndDT = new DateTime($p_item["sched_ends"], new DateTimeZone("UTC")); $schedEndDT = new DateTime($p_item["sched_ends"], new DateTimeZone("UTC"));
$schedEndDT->setTimezone(new DateTimeZone($this->timezone)); $schedEndDT->setTimezone(new DateTimeZone($this->timezone));
$showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
$this->getItemStatus($p_item, $row); $this->getItemStatus($p_item, $row);
$startsEpoch = intval($schedStartDT->format("U"));
$endsEpoch = intval($schedEndDT->format("U"));
$showEndEpoch = intval($showEndDT->format("U"));
//don't want an overbooked item to stay marked as current.
if ($this->isCurrent($startsEpoch, min($endsEpoch, $showEndEpoch))) {
$row["current"] = true;
}
$row["id"] = intval($p_item["sched_id"]); $row["id"] = intval($p_item["sched_id"]);
$row["instance"] = intval($p_item["si_id"]); $row["instance"] = intval($p_item["si_id"]);
$row["starts"] = $schedStartDT->format("H:i:s"); $row["starts"] = $schedStartDT->format("H:i:s");
@ -179,7 +161,10 @@ class Application_Model_ShowBuilder {
$this->contentDT = $schedEndDT; $this->contentDT = $schedEndDT;
} }
//show is empty //show is empty or is a special kind of show (recording etc)
else if (intval($p_item["si_record"]) === 1) {
$row["record"] = true;
}
else { else {
$row["empty"] = true; $row["empty"] = true;
@ -193,7 +178,6 @@ class Application_Model_ShowBuilder {
private function makeFooterRow($p_item) { private function makeFooterRow($p_item) {
$row = $this->defaultRowArray; $row = $this->defaultRowArray;
$this->isAllowed($p_item, $row);
$row["footer"] = true; $row["footer"] = true;
$showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC")); $showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
@ -201,7 +185,9 @@ class Application_Model_ShowBuilder {
$runtime = bcsub($contentDT->format("U.u"), $showEndDT->format("U.u"), 6); $runtime = bcsub($contentDT->format("U.u"), $showEndDT->format("U.u"), 6);
$row["runtime"] = $runtime; $row["runtime"] = $runtime;
$row["fRuntime"] = $this->formatTimeFilled($runtime);
$timeFilled = new TimeFilledFormatter($runtime);
$row["fRuntime"] = $timeFilled->format();
return $row; return $row;
} }

View File

@ -617,14 +617,14 @@ class Application_Model_ShowInstance {
public function getTimeScheduledSecs() public function getTimeScheduledSecs()
{ {
$time_filled = $this->getTimeScheduled(); $time_filled = $this->getTimeScheduled();
return Application_Model_Schedule::WallTimeToMillisecs($time_filled) / 1000; return Application_Model_Playlist::playlistTimeToSeconds($time_filled);
} }
public function getDurationSecs() public function getDurationSecs()
{ {
$ends = $this->getShowInstanceEnd(null); $ends = $this->getShowInstanceEnd(null);
$starts = $this->getShowInstanceStart(null); $starts = $this->getShowInstanceStart(null);
return $ends->format('U') - $starts->format('U'); return intval($ends->format('U')) - intval($starts->format('U'));
} }
public function getPercentScheduled() public function getPercentScheduled()

View File

@ -35,39 +35,6 @@ class CcPlaylistcontents extends BaseCcPlaylistcontents {
return parent::getDbFadeout($format); return parent::getDbFadeout($format);
} }
/**
* Just changing the default format to return subseconds
*
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/
public function getDbCuein($format = 'H:i:s.u')
{
return parent::getDbCuein($format);
}
/**
* Just changing the default format to return subseconds
*
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/
public function getDbCueout($format = 'H:i:s.u')
{
return parent::getDbCueout($format);
}
/**
* Just changing the default format to return subseconds
*
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/
public function getDbCliplength($format = 'H:i:s.u')
{
return parent::getDbCliplength($format);
}
/** /**
* *
* @param String in format SS.uuuuuu, Datetime, or DateTime accepted string. * @param String in format SS.uuuuuu, Datetime, or DateTime accepted string.
@ -124,88 +91,4 @@ class CcPlaylistcontents extends BaseCcPlaylistcontents {
return $this; return $this;
} // setDbFadeout() } // setDbFadeout()
/**
* Sets the value of [cuein] column to a normalized version of the date/time value specified.
*
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support)
*/
public function setDbCuein($v)
{
if ($v instanceof DateTime) {
$dt = $v;
}
else {
try {
$dt = new DateTime($v);
}
catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
}
$this->cuein = $dt->format('H:i:s.u');
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
return $this;
} // setDbCuein()
/**
* Sets the value of [cueout] column to a normalized version of the date/time value specified.
*
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support)
*/
public function setDbCueout($v)
{
if ($v instanceof DateTime) {
$dt = $v;
}
else {
try {
$dt = new DateTime($v);
}
catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
}
$this->cueout = $dt->format('H:i:s.u');
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
return $this;
} // setDbCueout()
/**
* Sets the value of [cliplength] column to a normalized version of the date/time value specified.
*
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support)
*/
public function setDbCliplength($v)
{
if ($v instanceof DateTime) {
$dt = $v;
}
else {
try {
$dt = new DateTime($v);
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
}
$this->cliplength = $dt->format('H:i:s.u');
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
return $this;
} // setDbCliplength()
} // CcPlaylistcontents } // CcPlaylistcontents

View File

@ -15,11 +15,6 @@
*/ */
class CcSchedule extends BaseCcSchedule { class CcSchedule extends BaseCcSchedule {
public function getDbClipLength($format = 'H:i:s.u')
{
return parent::getDbClipLength($format);
}
/** /**
* Get the [optionally formatted] temporal [starts] column value. * Get the [optionally formatted] temporal [starts] column value.
* *
@ -104,28 +99,6 @@ class CcSchedule extends BaseCcSchedule {
return parent::getDbFadeout($format); return parent::getDbFadeout($format);
} }
/**
* Just changing the default format to return subseconds
*
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/
public function getDbCueIn($format = 'H:i:s.u')
{
return parent::getDbCuein($format);
}
/**
* Just changing the default format to return subseconds
*
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/
public function getDbCueOut($format = 'H:i:s.u')
{
return parent::getDbCueout($format);
}
/** /**
* *
* @param String in format SS.uuuuuu, Datetime, or DateTime accepted string. * @param String in format SS.uuuuuu, Datetime, or DateTime accepted string.
@ -182,89 +155,6 @@ class CcSchedule extends BaseCcSchedule {
return $this; return $this;
} // setDbFadeout() } // setDbFadeout()
/**
* Sets the value of [cuein] column to a normalized version of the date/time value specified.
*
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support)
*/
public function setDbCueIn($v)
{
if ($v instanceof DateTime) {
$dt = $v;
}
else {
try {
$dt = new DateTime($v);
}
catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
}
$this->cue_in = $dt->format('H:i:s.u');
$this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
return $this;
} // setDbCuein()
/**
* Sets the value of [cueout] column to a normalized version of the date/time value specified.
*
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support)
*/
public function setDbCueout($v)
{
if ($v instanceof DateTime) {
$dt = $v;
}
else {
try {
$dt = new DateTime($v);
}
catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
}
$this->cue_out = $dt->format('H:i:s.u');
$this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
return $this;
} // setDbCueout()
/**
* Sets the value of [cliplength] column to a normalized version of the date/time value specified.
*
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support)
*/
public function setDbClipLength($v)
{
if ($v instanceof DateTime) {
$dt = $v;
}
else {
try {
$dt = new DateTime($v);
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
}
$this->clip_length = $dt->format('H:i:s.u');
$this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
return $this;
} // setDbCliplength()
/** /**
* Sets the value of [starts] column to a normalized version of the date/time value specified. * Sets the value of [starts] column to a normalized version of the date/time value specified.
* *

View File

@ -107,4 +107,33 @@ class CcShowInstances extends BaseCcShowInstances {
return $dt->format($format); return $dt->format($format);
} }
} }
//post save hook to update the cc_schedule status column for the tracks in the show.
public function updateScheduleStatus(PropelPDO $con) {
Logging::log("in post save for showinstances");
//scheduled track is in the show
CcScheduleQuery::create()
->filterByDbInstanceId($this->id)
->filterByDbEnds($this->ends, Criteria::LESS_EQUAL)
->update(array('DbStatus' => 1), $con);
Logging::log("updating status for in show items.");
//scheduled track is a boundary track
CcScheduleQuery::create()
->filterByDbInstanceId($this->id)
->filterByDbStarts($this->ends, Criteria::LESS_THAN)
->filterByDbEnds($this->ends, Criteria::GREATER_THAN)
->update(array('DbStatus' => 2), $con);
//scheduled track is overbooked.
CcScheduleQuery::create()
->filterByDbInstanceId($this->id)
->filterByDbStarts($this->ends, Criteria::GREATER_THAN)
->update(array('DbStatus' => 0), $con);
}
} // CcShowInstances } // CcShowInstances

View File

@ -42,9 +42,9 @@ class CcPlaylistcontentsTableMap extends TableMap {
$this->addForeignKey('PLAYLIST_ID', 'DbPlaylistId', 'INTEGER', 'cc_playlist', 'ID', false, null, null); $this->addForeignKey('PLAYLIST_ID', 'DbPlaylistId', 'INTEGER', 'cc_playlist', 'ID', false, null, null);
$this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null); $this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null);
$this->addColumn('POSITION', 'DbPosition', 'INTEGER', false, null, null); $this->addColumn('POSITION', 'DbPosition', 'INTEGER', false, null, null);
$this->addColumn('CLIPLENGTH', 'DbCliplength', 'TIME', false, null, '00:00:00'); $this->addColumn('CLIPLENGTH', 'DbCliplength', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('CUEIN', 'DbCuein', 'TIME', false, null, '00:00:00'); $this->addColumn('CUEIN', 'DbCuein', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('CUEOUT', 'DbCueout', 'TIME', false, null, '00:00:00'); $this->addColumn('CUEOUT', 'DbCueout', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('FADEIN', 'DbFadein', 'TIME', false, null, '00:00:00'); $this->addColumn('FADEIN', 'DbFadein', 'TIME', false, null, '00:00:00');
$this->addColumn('FADEOUT', 'DbFadeout', 'TIME', false, null, '00:00:00'); $this->addColumn('FADEOUT', 'DbFadeout', 'TIME', false, null, '00:00:00');
// validators // validators

View File

@ -42,13 +42,14 @@ class CcScheduleTableMap extends TableMap {
$this->addColumn('STARTS', 'DbStarts', 'TIMESTAMP', true, null, null); $this->addColumn('STARTS', 'DbStarts', 'TIMESTAMP', true, null, null);
$this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', true, null, null); $this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', true, null, null);
$this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null); $this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null);
$this->addColumn('CLIP_LENGTH', 'DbClipLength', 'TIME', false, null, '00:00:00'); $this->addColumn('CLIP_LENGTH', 'DbClipLength', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('FADE_IN', 'DbFadeIn', 'TIME', false, null, '00:00:00'); $this->addColumn('FADE_IN', 'DbFadeIn', 'TIME', false, null, '00:00:00');
$this->addColumn('FADE_OUT', 'DbFadeOut', 'TIME', false, null, '00:00:00'); $this->addColumn('FADE_OUT', 'DbFadeOut', 'TIME', false, null, '00:00:00');
$this->addColumn('CUE_IN', 'DbCueIn', 'TIME', false, null, '00:00:00'); $this->addColumn('CUE_IN', 'DbCueIn', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('CUE_OUT', 'DbCueOut', 'TIME', false, null, '00:00:00'); $this->addColumn('CUE_OUT', 'DbCueOut', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('MEDIA_ITEM_PLAYED', 'DbMediaItemPlayed', 'BOOLEAN', false, null, false); $this->addColumn('MEDIA_ITEM_PLAYED', 'DbMediaItemPlayed', 'BOOLEAN', false, null, false);
$this->addForeignKey('INSTANCE_ID', 'DbInstanceId', 'INTEGER', 'cc_show_instances', 'ID', true, null, null); $this->addForeignKey('INSTANCE_ID', 'DbInstanceId', 'INTEGER', 'cc_show_instances', 'ID', true, null, null);
$this->addColumn('STATUS', 'DbStatus', 'SMALLINT', true, null, 1);
// validators // validators
} // initialize() } // initialize()

View File

@ -176,102 +176,33 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
} }
/** /**
* Get the [optionally formatted] temporal [cliplength] column value. * Get the [cliplength] column value.
* *
* * @return string
* @param string $format The date/time format string (either date()-style or strftime()-style).
* If format is NULL, then the raw DateTime object will be returned.
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/ */
public function getDbCliplength($format = '%X') public function getDbCliplength()
{ {
if ($this->cliplength === null) { return $this->cliplength;
return null;
}
try {
$dt = new DateTime($this->cliplength);
} catch (Exception $x) {
throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cliplength, true), $x);
}
if ($format === null) {
// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
return $dt;
} elseif (strpos($format, '%') !== false) {
return strftime($format, $dt->format('U'));
} else {
return $dt->format($format);
}
} }
/** /**
* Get the [optionally formatted] temporal [cuein] column value. * Get the [cuein] column value.
* *
* * @return string
* @param string $format The date/time format string (either date()-style or strftime()-style).
* If format is NULL, then the raw DateTime object will be returned.
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/ */
public function getDbCuein($format = '%X') public function getDbCuein()
{ {
if ($this->cuein === null) { return $this->cuein;
return null;
}
try {
$dt = new DateTime($this->cuein);
} catch (Exception $x) {
throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cuein, true), $x);
}
if ($format === null) {
// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
return $dt;
} elseif (strpos($format, '%') !== false) {
return strftime($format, $dt->format('U'));
} else {
return $dt->format($format);
}
} }
/** /**
* Get the [optionally formatted] temporal [cueout] column value. * Get the [cueout] column value.
* *
* * @return string
* @param string $format The date/time format string (either date()-style or strftime()-style).
* If format is NULL, then the raw DateTime object will be returned.
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/ */
public function getDbCueout($format = '%X') public function getDbCueout()
{ {
if ($this->cueout === null) { return $this->cueout;
return null;
}
try {
$dt = new DateTime($this->cueout);
} catch (Exception $x) {
throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cueout, true), $x);
}
if ($format === null) {
// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
return $dt;
} elseif (strpos($format, '%') !== false) {
return strftime($format, $dt->format('U'));
} else {
return $dt->format($format);
}
} }
/** /**
@ -429,151 +360,61 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
} // setDbPosition() } // setDbPosition()
/** /**
* Sets the value of [cliplength] column to a normalized version of the date/time value specified. * Set the value of [cliplength] column.
* *
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will * @param string $v new value
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support) * @return CcPlaylistcontents The current object (for fluent API support)
*/ */
public function setDbCliplength($v) public function setDbCliplength($v)
{ {
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now') if ($v !== null) {
// -- which is unexpected, to say the least. $v = (string) $v;
if ($v === null || $v === '') {
$dt = null;
} elseif ($v instanceof DateTime) {
$dt = $v;
} else {
// some string/numeric value passed; we normalize that so that we can
// validate it.
try {
if (is_numeric($v)) { // if it's a unix timestamp
$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
// We have to explicitly specify and then change the time zone because of a
// DateTime bug: http://bugs.php.net/bug.php?id=43003
$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
} else {
$dt = new DateTime($v);
}
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
} }
if ( $this->cliplength !== null || $dt !== null ) { if ($this->cliplength !== $v || $this->isNew()) {
// (nested ifs are a little easier to read in this case) $this->cliplength = $v;
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
$currNorm = ($this->cliplength !== null && $tmpDt = new DateTime($this->cliplength)) ? $tmpDt->format('H:i:s') : null; }
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
if ( ($currNorm !== $newNorm) // normalized values don't match
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
)
{
$this->cliplength = ($dt ? $dt->format('H:i:s') : null);
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
}
} // if either are not null
return $this; return $this;
} // setDbCliplength() } // setDbCliplength()
/** /**
* Sets the value of [cuein] column to a normalized version of the date/time value specified. * Set the value of [cuein] column.
* *
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will * @param string $v new value
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support) * @return CcPlaylistcontents The current object (for fluent API support)
*/ */
public function setDbCuein($v) public function setDbCuein($v)
{ {
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now') if ($v !== null) {
// -- which is unexpected, to say the least. $v = (string) $v;
if ($v === null || $v === '') {
$dt = null;
} elseif ($v instanceof DateTime) {
$dt = $v;
} else {
// some string/numeric value passed; we normalize that so that we can
// validate it.
try {
if (is_numeric($v)) { // if it's a unix timestamp
$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
// We have to explicitly specify and then change the time zone because of a
// DateTime bug: http://bugs.php.net/bug.php?id=43003
$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
} else {
$dt = new DateTime($v);
}
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
} }
if ( $this->cuein !== null || $dt !== null ) { if ($this->cuein !== $v || $this->isNew()) {
// (nested ifs are a little easier to read in this case) $this->cuein = $v;
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
$currNorm = ($this->cuein !== null && $tmpDt = new DateTime($this->cuein)) ? $tmpDt->format('H:i:s') : null; }
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
if ( ($currNorm !== $newNorm) // normalized values don't match
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
)
{
$this->cuein = ($dt ? $dt->format('H:i:s') : null);
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
}
} // if either are not null
return $this; return $this;
} // setDbCuein() } // setDbCuein()
/** /**
* Sets the value of [cueout] column to a normalized version of the date/time value specified. * Set the value of [cueout] column.
* *
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will * @param string $v new value
* be treated as NULL for temporal objects.
* @return CcPlaylistcontents The current object (for fluent API support) * @return CcPlaylistcontents The current object (for fluent API support)
*/ */
public function setDbCueout($v) public function setDbCueout($v)
{ {
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now') if ($v !== null) {
// -- which is unexpected, to say the least. $v = (string) $v;
if ($v === null || $v === '') {
$dt = null;
} elseif ($v instanceof DateTime) {
$dt = $v;
} else {
// some string/numeric value passed; we normalize that so that we can
// validate it.
try {
if (is_numeric($v)) { // if it's a unix timestamp
$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
// We have to explicitly specify and then change the time zone because of a
// DateTime bug: http://bugs.php.net/bug.php?id=43003
$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
} else {
$dt = new DateTime($v);
}
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
} }
if ( $this->cueout !== null || $dt !== null ) { if ($this->cueout !== $v || $this->isNew()) {
// (nested ifs are a little easier to read in this case) $this->cueout = $v;
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
$currNorm = ($this->cueout !== null && $tmpDt = new DateTime($this->cueout)) ? $tmpDt->format('H:i:s') : null; }
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
if ( ($currNorm !== $newNorm) // normalized values don't match
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
)
{
$this->cueout = ($dt ? $dt->format('H:i:s') : null);
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
}
} // if either are not null
return $this; return $this;
} // setDbCueout() } // setDbCueout()

View File

@ -282,29 +282,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
/** /**
* Filter the query on the cliplength column * Filter the query on the cliplength column
* *
* @param string|array $dbCliplength The value to use as filter. * @param string $dbCliplength The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue) * Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
* *
* @return CcPlaylistcontentsQuery The current query, for fluid interface * @return CcPlaylistcontentsQuery The current query, for fluid interface
*/ */
public function filterByDbCliplength($dbCliplength = null, $comparison = null) public function filterByDbCliplength($dbCliplength = null, $comparison = null)
{ {
if (is_array($dbCliplength)) { if (null === $comparison) {
$useMinMax = false; if (is_array($dbCliplength)) {
if (isset($dbCliplength['min'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbCliplength['max'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN; $comparison = Criteria::IN;
} elseif (preg_match('/[\%\*]/', $dbCliplength)) {
$dbCliplength = str_replace('*', '%', $dbCliplength);
$comparison = Criteria::LIKE;
} }
} }
return $this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength, $comparison); return $this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength, $comparison);
@ -313,29 +304,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
/** /**
* Filter the query on the cuein column * Filter the query on the cuein column
* *
* @param string|array $dbCuein The value to use as filter. * @param string $dbCuein The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue) * Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
* *
* @return CcPlaylistcontentsQuery The current query, for fluid interface * @return CcPlaylistcontentsQuery The current query, for fluid interface
*/ */
public function filterByDbCuein($dbCuein = null, $comparison = null) public function filterByDbCuein($dbCuein = null, $comparison = null)
{ {
if (is_array($dbCuein)) { if (null === $comparison) {
$useMinMax = false; if (is_array($dbCuein)) {
if (isset($dbCuein['min'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbCuein['max'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN; $comparison = Criteria::IN;
} elseif (preg_match('/[\%\*]/', $dbCuein)) {
$dbCuein = str_replace('*', '%', $dbCuein);
$comparison = Criteria::LIKE;
} }
} }
return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein, $comparison); return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein, $comparison);
@ -344,29 +326,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
/** /**
* Filter the query on the cueout column * Filter the query on the cueout column
* *
* @param string|array $dbCueout The value to use as filter. * @param string $dbCueout The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue) * Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
* *
* @return CcPlaylistcontentsQuery The current query, for fluid interface * @return CcPlaylistcontentsQuery The current query, for fluid interface
*/ */
public function filterByDbCueout($dbCueout = null, $comparison = null) public function filterByDbCueout($dbCueout = null, $comparison = null)
{ {
if (is_array($dbCueout)) { if (null === $comparison) {
$useMinMax = false; if (is_array($dbCueout)) {
if (isset($dbCueout['min'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbCueout['max'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN; $comparison = Criteria::IN;
} elseif (preg_match('/[\%\*]/', $dbCueout)) {
$dbCueout = str_replace('*', '%', $dbCueout);
$comparison = Criteria::LIKE;
} }
} }
return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout, $comparison); return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout, $comparison);

View File

@ -96,6 +96,13 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
*/ */
protected $instance_id; protected $instance_id;
/**
* The value for the status field.
* Note: this column has a database default value of: 1
* @var int
*/
protected $status;
/** /**
* @var CcShowInstances * @var CcShowInstances
*/ */
@ -137,6 +144,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
$this->cue_in = '00:00:00'; $this->cue_in = '00:00:00';
$this->cue_out = '00:00:00'; $this->cue_out = '00:00:00';
$this->media_item_played = false; $this->media_item_played = false;
$this->status = 1;
} }
/** /**
@ -236,36 +244,13 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
} }
/** /**
* Get the [optionally formatted] temporal [clip_length] column value. * Get the [clip_length] column value.
* *
* * @return string
* @param string $format The date/time format string (either date()-style or strftime()-style).
* If format is NULL, then the raw DateTime object will be returned.
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/ */
public function getDbClipLength($format = '%X') public function getDbClipLength()
{ {
if ($this->clip_length === null) { return $this->clip_length;
return null;
}
try {
$dt = new DateTime($this->clip_length);
} catch (Exception $x) {
throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->clip_length, true), $x);
}
if ($format === null) {
// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
return $dt;
} elseif (strpos($format, '%') !== false) {
return strftime($format, $dt->format('U'));
} else {
return $dt->format($format);
}
} }
/** /**
@ -335,69 +320,23 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
} }
/** /**
* Get the [optionally formatted] temporal [cue_in] column value. * Get the [cue_in] column value.
* *
* * @return string
* @param string $format The date/time format string (either date()-style or strftime()-style).
* If format is NULL, then the raw DateTime object will be returned.
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/ */
public function getDbCueIn($format = '%X') public function getDbCueIn()
{ {
if ($this->cue_in === null) { return $this->cue_in;
return null;
}
try {
$dt = new DateTime($this->cue_in);
} catch (Exception $x) {
throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cue_in, true), $x);
}
if ($format === null) {
// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
return $dt;
} elseif (strpos($format, '%') !== false) {
return strftime($format, $dt->format('U'));
} else {
return $dt->format($format);
}
} }
/** /**
* Get the [optionally formatted] temporal [cue_out] column value. * Get the [cue_out] column value.
* *
* * @return string
* @param string $format The date/time format string (either date()-style or strftime()-style).
* If format is NULL, then the raw DateTime object will be returned.
* @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
* @throws PropelException - if unable to parse/validate the date/time value.
*/ */
public function getDbCueOut($format = '%X') public function getDbCueOut()
{ {
if ($this->cue_out === null) { return $this->cue_out;
return null;
}
try {
$dt = new DateTime($this->cue_out);
} catch (Exception $x) {
throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cue_out, true), $x);
}
if ($format === null) {
// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
return $dt;
} elseif (strpos($format, '%') !== false) {
return strftime($format, $dt->format('U'));
} else {
return $dt->format($format);
}
} }
/** /**
@ -420,6 +359,16 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
return $this->instance_id; return $this->instance_id;
} }
/**
* Get the [status] column value.
*
* @return int
*/
public function getDbStatus()
{
return $this->status;
}
/** /**
* Set the value of [id] column. * Set the value of [id] column.
* *
@ -563,51 +512,21 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
} // setDbFileId() } // setDbFileId()
/** /**
* Sets the value of [clip_length] column to a normalized version of the date/time value specified. * Set the value of [clip_length] column.
* *
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will * @param string $v new value
* be treated as NULL for temporal objects.
* @return CcSchedule The current object (for fluent API support) * @return CcSchedule The current object (for fluent API support)
*/ */
public function setDbClipLength($v) public function setDbClipLength($v)
{ {
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now') if ($v !== null) {
// -- which is unexpected, to say the least. $v = (string) $v;
if ($v === null || $v === '') {
$dt = null;
} elseif ($v instanceof DateTime) {
$dt = $v;
} else {
// some string/numeric value passed; we normalize that so that we can
// validate it.
try {
if (is_numeric($v)) { // if it's a unix timestamp
$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
// We have to explicitly specify and then change the time zone because of a
// DateTime bug: http://bugs.php.net/bug.php?id=43003
$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
} else {
$dt = new DateTime($v);
}
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
} }
if ( $this->clip_length !== null || $dt !== null ) { if ($this->clip_length !== $v || $this->isNew()) {
// (nested ifs are a little easier to read in this case) $this->clip_length = $v;
$this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
$currNorm = ($this->clip_length !== null && $tmpDt = new DateTime($this->clip_length)) ? $tmpDt->format('H:i:s') : null; }
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
if ( ($currNorm !== $newNorm) // normalized values don't match
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
)
{
$this->clip_length = ($dt ? $dt->format('H:i:s') : null);
$this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
}
} // if either are not null
return $this; return $this;
} // setDbClipLength() } // setDbClipLength()
@ -713,101 +632,41 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
} // setDbFadeOut() } // setDbFadeOut()
/** /**
* Sets the value of [cue_in] column to a normalized version of the date/time value specified. * Set the value of [cue_in] column.
* *
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will * @param string $v new value
* be treated as NULL for temporal objects.
* @return CcSchedule The current object (for fluent API support) * @return CcSchedule The current object (for fluent API support)
*/ */
public function setDbCueIn($v) public function setDbCueIn($v)
{ {
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now') if ($v !== null) {
// -- which is unexpected, to say the least. $v = (string) $v;
if ($v === null || $v === '') {
$dt = null;
} elseif ($v instanceof DateTime) {
$dt = $v;
} else {
// some string/numeric value passed; we normalize that so that we can
// validate it.
try {
if (is_numeric($v)) { // if it's a unix timestamp
$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
// We have to explicitly specify and then change the time zone because of a
// DateTime bug: http://bugs.php.net/bug.php?id=43003
$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
} else {
$dt = new DateTime($v);
}
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
} }
if ( $this->cue_in !== null || $dt !== null ) { if ($this->cue_in !== $v || $this->isNew()) {
// (nested ifs are a little easier to read in this case) $this->cue_in = $v;
$this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
$currNorm = ($this->cue_in !== null && $tmpDt = new DateTime($this->cue_in)) ? $tmpDt->format('H:i:s') : null; }
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
if ( ($currNorm !== $newNorm) // normalized values don't match
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
)
{
$this->cue_in = ($dt ? $dt->format('H:i:s') : null);
$this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
}
} // if either are not null
return $this; return $this;
} // setDbCueIn() } // setDbCueIn()
/** /**
* Sets the value of [cue_out] column to a normalized version of the date/time value specified. * Set the value of [cue_out] column.
* *
* @param mixed $v string, integer (timestamp), or DateTime value. Empty string will * @param string $v new value
* be treated as NULL for temporal objects.
* @return CcSchedule The current object (for fluent API support) * @return CcSchedule The current object (for fluent API support)
*/ */
public function setDbCueOut($v) public function setDbCueOut($v)
{ {
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now') if ($v !== null) {
// -- which is unexpected, to say the least. $v = (string) $v;
if ($v === null || $v === '') {
$dt = null;
} elseif ($v instanceof DateTime) {
$dt = $v;
} else {
// some string/numeric value passed; we normalize that so that we can
// validate it.
try {
if (is_numeric($v)) { // if it's a unix timestamp
$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
// We have to explicitly specify and then change the time zone because of a
// DateTime bug: http://bugs.php.net/bug.php?id=43003
$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
} else {
$dt = new DateTime($v);
}
} catch (Exception $x) {
throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
}
} }
if ( $this->cue_out !== null || $dt !== null ) { if ($this->cue_out !== $v || $this->isNew()) {
// (nested ifs are a little easier to read in this case) $this->cue_out = $v;
$this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
$currNorm = ($this->cue_out !== null && $tmpDt = new DateTime($this->cue_out)) ? $tmpDt->format('H:i:s') : null; }
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
if ( ($currNorm !== $newNorm) // normalized values don't match
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
)
{
$this->cue_out = ($dt ? $dt->format('H:i:s') : null);
$this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
}
} // if either are not null
return $this; return $this;
} // setDbCueOut() } // setDbCueOut()
@ -856,6 +715,26 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
return $this; return $this;
} // setDbInstanceId() } // setDbInstanceId()
/**
* Set the value of [status] column.
*
* @param int $v new value
* @return CcSchedule The current object (for fluent API support)
*/
public function setDbStatus($v)
{
if ($v !== null) {
$v = (int) $v;
}
if ($this->status !== $v || $this->isNew()) {
$this->status = $v;
$this->modifiedColumns[] = CcSchedulePeer::STATUS;
}
return $this;
} // setDbStatus()
/** /**
* Indicates whether the columns in this object are only set to default values. * Indicates whether the columns in this object are only set to default values.
* *
@ -890,6 +769,10 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
return false; return false;
} }
if ($this->status !== 1) {
return false;
}
// otherwise, everything was equal, so return TRUE // otherwise, everything was equal, so return TRUE
return true; return true;
} // hasOnlyDefaultValues() } // hasOnlyDefaultValues()
@ -923,6 +806,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
$this->cue_out = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null; $this->cue_out = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null;
$this->media_item_played = ($row[$startcol + 9] !== null) ? (boolean) $row[$startcol + 9] : null; $this->media_item_played = ($row[$startcol + 9] !== null) ? (boolean) $row[$startcol + 9] : null;
$this->instance_id = ($row[$startcol + 10] !== null) ? (int) $row[$startcol + 10] : null; $this->instance_id = ($row[$startcol + 10] !== null) ? (int) $row[$startcol + 10] : null;
$this->status = ($row[$startcol + 11] !== null) ? (int) $row[$startcol + 11] : null;
$this->resetModified(); $this->resetModified();
$this->setNew(false); $this->setNew(false);
@ -931,7 +815,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
$this->ensureConsistency(); $this->ensureConsistency();
} }
return $startcol + 11; // 11 = CcSchedulePeer::NUM_COLUMNS - CcSchedulePeer::NUM_LAZY_LOAD_COLUMNS). return $startcol + 12; // 12 = CcSchedulePeer::NUM_COLUMNS - CcSchedulePeer::NUM_LAZY_LOAD_COLUMNS).
} catch (Exception $e) { } catch (Exception $e) {
throw new PropelException("Error populating CcSchedule object", $e); throw new PropelException("Error populating CcSchedule object", $e);
@ -1310,6 +1194,9 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
case 10: case 10:
return $this->getDbInstanceId(); return $this->getDbInstanceId();
break; break;
case 11:
return $this->getDbStatus();
break;
default: default:
return null; return null;
break; break;
@ -1345,6 +1232,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
$keys[8] => $this->getDbCueOut(), $keys[8] => $this->getDbCueOut(),
$keys[9] => $this->getDbMediaItemPlayed(), $keys[9] => $this->getDbMediaItemPlayed(),
$keys[10] => $this->getDbInstanceId(), $keys[10] => $this->getDbInstanceId(),
$keys[11] => $this->getDbStatus(),
); );
if ($includeForeignObjects) { if ($includeForeignObjects) {
if (null !== $this->aCcShowInstances) { if (null !== $this->aCcShowInstances) {
@ -1417,6 +1305,9 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
case 10: case 10:
$this->setDbInstanceId($value); $this->setDbInstanceId($value);
break; break;
case 11:
$this->setDbStatus($value);
break;
} // switch() } // switch()
} }
@ -1452,6 +1343,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
if (array_key_exists($keys[8], $arr)) $this->setDbCueOut($arr[$keys[8]]); if (array_key_exists($keys[8], $arr)) $this->setDbCueOut($arr[$keys[8]]);
if (array_key_exists($keys[9], $arr)) $this->setDbMediaItemPlayed($arr[$keys[9]]); if (array_key_exists($keys[9], $arr)) $this->setDbMediaItemPlayed($arr[$keys[9]]);
if (array_key_exists($keys[10], $arr)) $this->setDbInstanceId($arr[$keys[10]]); if (array_key_exists($keys[10], $arr)) $this->setDbInstanceId($arr[$keys[10]]);
if (array_key_exists($keys[11], $arr)) $this->setDbStatus($arr[$keys[11]]);
} }
/** /**
@ -1474,6 +1366,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
if ($this->isColumnModified(CcSchedulePeer::CUE_OUT)) $criteria->add(CcSchedulePeer::CUE_OUT, $this->cue_out); if ($this->isColumnModified(CcSchedulePeer::CUE_OUT)) $criteria->add(CcSchedulePeer::CUE_OUT, $this->cue_out);
if ($this->isColumnModified(CcSchedulePeer::MEDIA_ITEM_PLAYED)) $criteria->add(CcSchedulePeer::MEDIA_ITEM_PLAYED, $this->media_item_played); if ($this->isColumnModified(CcSchedulePeer::MEDIA_ITEM_PLAYED)) $criteria->add(CcSchedulePeer::MEDIA_ITEM_PLAYED, $this->media_item_played);
if ($this->isColumnModified(CcSchedulePeer::INSTANCE_ID)) $criteria->add(CcSchedulePeer::INSTANCE_ID, $this->instance_id); if ($this->isColumnModified(CcSchedulePeer::INSTANCE_ID)) $criteria->add(CcSchedulePeer::INSTANCE_ID, $this->instance_id);
if ($this->isColumnModified(CcSchedulePeer::STATUS)) $criteria->add(CcSchedulePeer::STATUS, $this->status);
return $criteria; return $criteria;
} }
@ -1545,6 +1438,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
$copyObj->setDbCueOut($this->cue_out); $copyObj->setDbCueOut($this->cue_out);
$copyObj->setDbMediaItemPlayed($this->media_item_played); $copyObj->setDbMediaItemPlayed($this->media_item_played);
$copyObj->setDbInstanceId($this->instance_id); $copyObj->setDbInstanceId($this->instance_id);
$copyObj->setDbStatus($this->status);
$copyObj->setNew(true); $copyObj->setNew(true);
$copyObj->setDbId(NULL); // this is a auto-increment column, so set to default value $copyObj->setDbId(NULL); // this is a auto-increment column, so set to default value
@ -1706,6 +1600,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
$this->cue_out = null; $this->cue_out = null;
$this->media_item_played = null; $this->media_item_played = null;
$this->instance_id = null; $this->instance_id = null;
$this->status = null;
$this->alreadyInSave = false; $this->alreadyInSave = false;
$this->alreadyInValidation = false; $this->alreadyInValidation = false;
$this->clearAllReferences(); $this->clearAllReferences();

View File

@ -26,7 +26,7 @@ abstract class BaseCcSchedulePeer {
const TM_CLASS = 'CcScheduleTableMap'; const TM_CLASS = 'CcScheduleTableMap';
/** The total number of columns. */ /** The total number of columns. */
const NUM_COLUMNS = 11; const NUM_COLUMNS = 12;
/** The number of lazy-loaded columns. */ /** The number of lazy-loaded columns. */
const NUM_LAZY_LOAD_COLUMNS = 0; const NUM_LAZY_LOAD_COLUMNS = 0;
@ -64,6 +64,9 @@ abstract class BaseCcSchedulePeer {
/** the column name for the INSTANCE_ID field */ /** the column name for the INSTANCE_ID field */
const INSTANCE_ID = 'cc_schedule.INSTANCE_ID'; const INSTANCE_ID = 'cc_schedule.INSTANCE_ID';
/** the column name for the STATUS field */
const STATUS = 'cc_schedule.STATUS';
/** /**
* An identiy map to hold any loaded instances of CcSchedule objects. * An identiy map to hold any loaded instances of CcSchedule objects.
* This must be public so that other peer classes can access this when hydrating from JOIN * This must be public so that other peer classes can access this when hydrating from JOIN
@ -80,12 +83,12 @@ abstract class BaseCcSchedulePeer {
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
*/ */
private static $fieldNames = array ( private static $fieldNames = array (
BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbFileId', 'DbClipLength', 'DbFadeIn', 'DbFadeOut', 'DbCueIn', 'DbCueOut', 'DbMediaItemPlayed', 'DbInstanceId', ), BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbFileId', 'DbClipLength', 'DbFadeIn', 'DbFadeOut', 'DbCueIn', 'DbCueOut', 'DbMediaItemPlayed', 'DbInstanceId', 'DbStatus', ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbFileId', 'dbClipLength', 'dbFadeIn', 'dbFadeOut', 'dbCueIn', 'dbCueOut', 'dbMediaItemPlayed', 'dbInstanceId', ), BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbFileId', 'dbClipLength', 'dbFadeIn', 'dbFadeOut', 'dbCueIn', 'dbCueOut', 'dbMediaItemPlayed', 'dbInstanceId', 'dbStatus', ),
BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::FILE_ID, self::CLIP_LENGTH, self::FADE_IN, self::FADE_OUT, self::CUE_IN, self::CUE_OUT, self::MEDIA_ITEM_PLAYED, self::INSTANCE_ID, ), BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::FILE_ID, self::CLIP_LENGTH, self::FADE_IN, self::FADE_OUT, self::CUE_IN, self::CUE_OUT, self::MEDIA_ITEM_PLAYED, self::INSTANCE_ID, self::STATUS, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'FILE_ID', 'CLIP_LENGTH', 'FADE_IN', 'FADE_OUT', 'CUE_IN', 'CUE_OUT', 'MEDIA_ITEM_PLAYED', 'INSTANCE_ID', ), BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'FILE_ID', 'CLIP_LENGTH', 'FADE_IN', 'FADE_OUT', 'CUE_IN', 'CUE_OUT', 'MEDIA_ITEM_PLAYED', 'INSTANCE_ID', 'STATUS', ),
BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'file_id', 'clip_length', 'fade_in', 'fade_out', 'cue_in', 'cue_out', 'media_item_played', 'instance_id', ), BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'file_id', 'clip_length', 'fade_in', 'fade_out', 'cue_in', 'cue_out', 'media_item_played', 'instance_id', 'status', ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ) BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, )
); );
/** /**
@ -95,12 +98,12 @@ abstract class BaseCcSchedulePeer {
* e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0 * e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0
*/ */
private static $fieldKeys = array ( private static $fieldKeys = array (
BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbFileId' => 3, 'DbClipLength' => 4, 'DbFadeIn' => 5, 'DbFadeOut' => 6, 'DbCueIn' => 7, 'DbCueOut' => 8, 'DbMediaItemPlayed' => 9, 'DbInstanceId' => 10, ), BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbFileId' => 3, 'DbClipLength' => 4, 'DbFadeIn' => 5, 'DbFadeOut' => 6, 'DbCueIn' => 7, 'DbCueOut' => 8, 'DbMediaItemPlayed' => 9, 'DbInstanceId' => 10, 'DbStatus' => 11, ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbFileId' => 3, 'dbClipLength' => 4, 'dbFadeIn' => 5, 'dbFadeOut' => 6, 'dbCueIn' => 7, 'dbCueOut' => 8, 'dbMediaItemPlayed' => 9, 'dbInstanceId' => 10, ), BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbFileId' => 3, 'dbClipLength' => 4, 'dbFadeIn' => 5, 'dbFadeOut' => 6, 'dbCueIn' => 7, 'dbCueOut' => 8, 'dbMediaItemPlayed' => 9, 'dbInstanceId' => 10, 'dbStatus' => 11, ),
BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::FILE_ID => 3, self::CLIP_LENGTH => 4, self::FADE_IN => 5, self::FADE_OUT => 6, self::CUE_IN => 7, self::CUE_OUT => 8, self::MEDIA_ITEM_PLAYED => 9, self::INSTANCE_ID => 10, ), BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::FILE_ID => 3, self::CLIP_LENGTH => 4, self::FADE_IN => 5, self::FADE_OUT => 6, self::CUE_IN => 7, self::CUE_OUT => 8, self::MEDIA_ITEM_PLAYED => 9, self::INSTANCE_ID => 10, self::STATUS => 11, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'FILE_ID' => 3, 'CLIP_LENGTH' => 4, 'FADE_IN' => 5, 'FADE_OUT' => 6, 'CUE_IN' => 7, 'CUE_OUT' => 8, 'MEDIA_ITEM_PLAYED' => 9, 'INSTANCE_ID' => 10, ), BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'FILE_ID' => 3, 'CLIP_LENGTH' => 4, 'FADE_IN' => 5, 'FADE_OUT' => 6, 'CUE_IN' => 7, 'CUE_OUT' => 8, 'MEDIA_ITEM_PLAYED' => 9, 'INSTANCE_ID' => 10, 'STATUS' => 11, ),
BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'file_id' => 3, 'clip_length' => 4, 'fade_in' => 5, 'fade_out' => 6, 'cue_in' => 7, 'cue_out' => 8, 'media_item_played' => 9, 'instance_id' => 10, ), BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'file_id' => 3, 'clip_length' => 4, 'fade_in' => 5, 'fade_out' => 6, 'cue_in' => 7, 'cue_out' => 8, 'media_item_played' => 9, 'instance_id' => 10, 'status' => 11, ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ) BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, )
); );
/** /**
@ -183,6 +186,7 @@ abstract class BaseCcSchedulePeer {
$criteria->addSelectColumn(CcSchedulePeer::CUE_OUT); $criteria->addSelectColumn(CcSchedulePeer::CUE_OUT);
$criteria->addSelectColumn(CcSchedulePeer::MEDIA_ITEM_PLAYED); $criteria->addSelectColumn(CcSchedulePeer::MEDIA_ITEM_PLAYED);
$criteria->addSelectColumn(CcSchedulePeer::INSTANCE_ID); $criteria->addSelectColumn(CcSchedulePeer::INSTANCE_ID);
$criteria->addSelectColumn(CcSchedulePeer::STATUS);
} else { } else {
$criteria->addSelectColumn($alias . '.ID'); $criteria->addSelectColumn($alias . '.ID');
$criteria->addSelectColumn($alias . '.STARTS'); $criteria->addSelectColumn($alias . '.STARTS');
@ -195,6 +199,7 @@ abstract class BaseCcSchedulePeer {
$criteria->addSelectColumn($alias . '.CUE_OUT'); $criteria->addSelectColumn($alias . '.CUE_OUT');
$criteria->addSelectColumn($alias . '.MEDIA_ITEM_PLAYED'); $criteria->addSelectColumn($alias . '.MEDIA_ITEM_PLAYED');
$criteria->addSelectColumn($alias . '.INSTANCE_ID'); $criteria->addSelectColumn($alias . '.INSTANCE_ID');
$criteria->addSelectColumn($alias . '.STATUS');
} }
} }

View File

@ -17,6 +17,7 @@
* @method CcScheduleQuery orderByDbCueOut($order = Criteria::ASC) Order by the cue_out column * @method CcScheduleQuery orderByDbCueOut($order = Criteria::ASC) Order by the cue_out column
* @method CcScheduleQuery orderByDbMediaItemPlayed($order = Criteria::ASC) Order by the media_item_played column * @method CcScheduleQuery orderByDbMediaItemPlayed($order = Criteria::ASC) Order by the media_item_played column
* @method CcScheduleQuery orderByDbInstanceId($order = Criteria::ASC) Order by the instance_id column * @method CcScheduleQuery orderByDbInstanceId($order = Criteria::ASC) Order by the instance_id column
* @method CcScheduleQuery orderByDbStatus($order = Criteria::ASC) Order by the status column
* *
* @method CcScheduleQuery groupByDbId() Group by the id column * @method CcScheduleQuery groupByDbId() Group by the id column
* @method CcScheduleQuery groupByDbStarts() Group by the starts column * @method CcScheduleQuery groupByDbStarts() Group by the starts column
@ -29,6 +30,7 @@
* @method CcScheduleQuery groupByDbCueOut() Group by the cue_out column * @method CcScheduleQuery groupByDbCueOut() Group by the cue_out column
* @method CcScheduleQuery groupByDbMediaItemPlayed() Group by the media_item_played column * @method CcScheduleQuery groupByDbMediaItemPlayed() Group by the media_item_played column
* @method CcScheduleQuery groupByDbInstanceId() Group by the instance_id column * @method CcScheduleQuery groupByDbInstanceId() Group by the instance_id column
* @method CcScheduleQuery groupByDbStatus() Group by the status column
* *
* @method CcScheduleQuery leftJoin($relation) Adds a LEFT JOIN clause to the query * @method CcScheduleQuery leftJoin($relation) Adds a LEFT JOIN clause to the query
* @method CcScheduleQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query * @method CcScheduleQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query
@ -56,6 +58,7 @@
* @method CcSchedule findOneByDbCueOut(string $cue_out) Return the first CcSchedule filtered by the cue_out column * @method CcSchedule findOneByDbCueOut(string $cue_out) Return the first CcSchedule filtered by the cue_out column
* @method CcSchedule findOneByDbMediaItemPlayed(boolean $media_item_played) Return the first CcSchedule filtered by the media_item_played column * @method CcSchedule findOneByDbMediaItemPlayed(boolean $media_item_played) Return the first CcSchedule filtered by the media_item_played column
* @method CcSchedule findOneByDbInstanceId(int $instance_id) Return the first CcSchedule filtered by the instance_id column * @method CcSchedule findOneByDbInstanceId(int $instance_id) Return the first CcSchedule filtered by the instance_id column
* @method CcSchedule findOneByDbStatus(int $status) Return the first CcSchedule filtered by the status column
* *
* @method array findByDbId(int $id) Return CcSchedule objects filtered by the id column * @method array findByDbId(int $id) Return CcSchedule objects filtered by the id column
* @method array findByDbStarts(string $starts) Return CcSchedule objects filtered by the starts column * @method array findByDbStarts(string $starts) Return CcSchedule objects filtered by the starts column
@ -68,6 +71,7 @@
* @method array findByDbCueOut(string $cue_out) Return CcSchedule objects filtered by the cue_out column * @method array findByDbCueOut(string $cue_out) Return CcSchedule objects filtered by the cue_out column
* @method array findByDbMediaItemPlayed(boolean $media_item_played) Return CcSchedule objects filtered by the media_item_played column * @method array findByDbMediaItemPlayed(boolean $media_item_played) Return CcSchedule objects filtered by the media_item_played column
* @method array findByDbInstanceId(int $instance_id) Return CcSchedule objects filtered by the instance_id column * @method array findByDbInstanceId(int $instance_id) Return CcSchedule objects filtered by the instance_id column
* @method array findByDbStatus(int $status) Return CcSchedule objects filtered by the status column
* *
* @package propel.generator.airtime.om * @package propel.generator.airtime.om
*/ */
@ -290,29 +294,20 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
/** /**
* Filter the query on the clip_length column * Filter the query on the clip_length column
* *
* @param string|array $dbClipLength The value to use as filter. * @param string $dbClipLength The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue) * Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
* *
* @return CcScheduleQuery The current query, for fluid interface * @return CcScheduleQuery The current query, for fluid interface
*/ */
public function filterByDbClipLength($dbClipLength = null, $comparison = null) public function filterByDbClipLength($dbClipLength = null, $comparison = null)
{ {
if (is_array($dbClipLength)) { if (null === $comparison) {
$useMinMax = false; if (is_array($dbClipLength)) {
if (isset($dbClipLength['min'])) {
$this->addUsingAlias(CcSchedulePeer::CLIP_LENGTH, $dbClipLength['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbClipLength['max'])) {
$this->addUsingAlias(CcSchedulePeer::CLIP_LENGTH, $dbClipLength['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN; $comparison = Criteria::IN;
} elseif (preg_match('/[\%\*]/', $dbClipLength)) {
$dbClipLength = str_replace('*', '%', $dbClipLength);
$comparison = Criteria::LIKE;
} }
} }
return $this->addUsingAlias(CcSchedulePeer::CLIP_LENGTH, $dbClipLength, $comparison); return $this->addUsingAlias(CcSchedulePeer::CLIP_LENGTH, $dbClipLength, $comparison);
@ -383,29 +378,20 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
/** /**
* Filter the query on the cue_in column * Filter the query on the cue_in column
* *
* @param string|array $dbCueIn The value to use as filter. * @param string $dbCueIn The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue) * Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
* *
* @return CcScheduleQuery The current query, for fluid interface * @return CcScheduleQuery The current query, for fluid interface
*/ */
public function filterByDbCueIn($dbCueIn = null, $comparison = null) public function filterByDbCueIn($dbCueIn = null, $comparison = null)
{ {
if (is_array($dbCueIn)) { if (null === $comparison) {
$useMinMax = false; if (is_array($dbCueIn)) {
if (isset($dbCueIn['min'])) {
$this->addUsingAlias(CcSchedulePeer::CUE_IN, $dbCueIn['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbCueIn['max'])) {
$this->addUsingAlias(CcSchedulePeer::CUE_IN, $dbCueIn['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN; $comparison = Criteria::IN;
} elseif (preg_match('/[\%\*]/', $dbCueIn)) {
$dbCueIn = str_replace('*', '%', $dbCueIn);
$comparison = Criteria::LIKE;
} }
} }
return $this->addUsingAlias(CcSchedulePeer::CUE_IN, $dbCueIn, $comparison); return $this->addUsingAlias(CcSchedulePeer::CUE_IN, $dbCueIn, $comparison);
@ -414,29 +400,20 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
/** /**
* Filter the query on the cue_out column * Filter the query on the cue_out column
* *
* @param string|array $dbCueOut The value to use as filter. * @param string $dbCueOut The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue) * Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
* *
* @return CcScheduleQuery The current query, for fluid interface * @return CcScheduleQuery The current query, for fluid interface
*/ */
public function filterByDbCueOut($dbCueOut = null, $comparison = null) public function filterByDbCueOut($dbCueOut = null, $comparison = null)
{ {
if (is_array($dbCueOut)) { if (null === $comparison) {
$useMinMax = false; if (is_array($dbCueOut)) {
if (isset($dbCueOut['min'])) {
$this->addUsingAlias(CcSchedulePeer::CUE_OUT, $dbCueOut['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbCueOut['max'])) {
$this->addUsingAlias(CcSchedulePeer::CUE_OUT, $dbCueOut['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN; $comparison = Criteria::IN;
} elseif (preg_match('/[\%\*]/', $dbCueOut)) {
$dbCueOut = str_replace('*', '%', $dbCueOut);
$comparison = Criteria::LIKE;
} }
} }
return $this->addUsingAlias(CcSchedulePeer::CUE_OUT, $dbCueOut, $comparison); return $this->addUsingAlias(CcSchedulePeer::CUE_OUT, $dbCueOut, $comparison);
@ -490,6 +467,37 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
return $this->addUsingAlias(CcSchedulePeer::INSTANCE_ID, $dbInstanceId, $comparison); return $this->addUsingAlias(CcSchedulePeer::INSTANCE_ID, $dbInstanceId, $comparison);
} }
/**
* Filter the query on the status column
*
* @param int|array $dbStatus The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
*
* @return CcScheduleQuery The current query, for fluid interface
*/
public function filterByDbStatus($dbStatus = null, $comparison = null)
{
if (is_array($dbStatus)) {
$useMinMax = false;
if (isset($dbStatus['min'])) {
$this->addUsingAlias(CcSchedulePeer::STATUS, $dbStatus['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbStatus['max'])) {
$this->addUsingAlias(CcSchedulePeer::STATUS, $dbStatus['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN;
}
}
return $this->addUsingAlias(CcSchedulePeer::STATUS, $dbStatus, $comparison);
}
/** /**
* Filter the query by a related CcShowInstances object * Filter the query by a related CcShowInstances object
* *

View File

@ -0,0 +1,46 @@
<?php
class TimeFilledFormatter {
/**
* @string seconds
*/
private $_seconds;
/*
* @param string $seconds
*/
public function __construct($seconds)
{
$this->_seconds = $seconds;
}
public function format()
{
$formatted = "";
$sign = ($this->_seconds < 0) ? "-" : "+";
$time = Application_Model_Playlist::secondsToPlaylistTime(abs($this->_seconds));
Logging::log("time is: ".$time);
$info = explode(":", $time);
$formatted .= $sign;
if (intval($info[0]) > 0) {
$info[0] = ltrim($info[0], "0");
$formatted .= " {$info[0]}h";
}
if (intval($info[1]) > 0) {
$info[1] = ltrim($info[1], "0");
$formatted .= " {$info[1]}m";
}
if (intval($info[2]) > 0) {
$sec = round($info[2], 0);
$formatted .= " {$sec}s";
}
return $formatted;
}
}

View File

@ -1 +0,0 @@
<br /><br /><center>View script for controller <b>Library</b> and script/action name <b>delete</b></center>

View File

@ -1,8 +0,0 @@
<?php // libraryTablePartial.phtml ?>
<tr id="<?php echo substr($this->ftype, 0, 2) ?>_<?php echo $this->id ?>">
<td><?php echo $this->track_title ?></td>
<td><?php echo $this->artist_name ?></td>
<td><?php echo $this->album_title ?></td>
<td><?php echo $this->track_number ?></td>
<td><?php echo $this->length ?></td>
</tr>

View File

@ -1 +0,0 @@
<br /><br /><center>View script for controller <b>Library</b> and script/action name <b>search</b></center>

View File

@ -1,3 +0,0 @@
<?php
echo $this->partialLoop('library/libraryTablePartial.phtml', $this->files);

View File

@ -4,11 +4,11 @@ if (count($items)) : ?>
<?php $i = 0; ?> <?php $i = 0; ?>
<?php foreach($items as $item) : ?> <?php foreach($items as $item) : ?>
<li class="ui-state-default" id="spl_<?php echo $item["id"] ?>" unqid="<?php echo $item["CcFiles"]["gunid"]."_".$i; ?>"> <li class="ui-state-default" id="spl_<?php echo $item["id"] ?>" unqid="<?php echo $item["CcFiles"]["gunid"]."_".$item["id"]; ?>">
<div class="list-item-container"> <div class="list-item-container">
<a href="javascript:void(0);" class="big_play" <a href="javascript:void(0);" class="big_play"
onclick="audioPreview('<?php echo $item["CcFiles"]["gunid"].".".pathinfo($item["CcFiles"]["filepath"], PATHINFO_EXTENSION);?>', onclick="audioPreview('<?php echo $item["CcFiles"]["gunid"].".".pathinfo($item["CcFiles"]["filepath"], PATHINFO_EXTENSION);?>',
'spl_<?php echo $i ?>')"><span class="ui-icon ui-icon-play"></span></a> 'spl_<?php echo $item["id"] ?>')"><span class="ui-icon ui-icon-play"></span></a>
<div class="text-row top"> <div class="text-row top">
<span class="spl_playlength"><?php echo $item["cliplength"] ?></span> <span class="spl_playlength"><?php echo $item["cliplength"] ?></span>

View File

@ -0,0 +1,9 @@
<div class="wrapper">
<div id="library_content" class="tabs ui-widget ui-widget-content block-shadow alpha-block padded">
<div id="import_status" style="display:none">File import in progress...</div>
<table id="library_display" cellpadding="0" cellspacing="0" class="datatable"></table>
</div>
<div id="show_builder" class="ui-widget ui-widget-content block-shadow omega-block padded">
<table id="show_builder_table" cellpadding="0" cellspacing="0" class="datatable"></table>
</div>
</div>

View File

@ -237,9 +237,9 @@
<column name="playlist_id" phpName="DbPlaylistId" type="INTEGER" required="false"/> <column name="playlist_id" phpName="DbPlaylistId" type="INTEGER" required="false"/>
<column name="file_id" phpName="DbFileId" type="INTEGER" required="false"/> <column name="file_id" phpName="DbFileId" type="INTEGER" required="false"/>
<column name="position" phpName="DbPosition" type="INTEGER" required="false"/> <column name="position" phpName="DbPosition" type="INTEGER" required="false"/>
<column name="cliplength" phpName="DbCliplength" type="TIME" required="false" defaultValue="00:00:00"/> <column name="cliplength" phpName="DbCliplength" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="cuein" phpName="DbCuein" type="TIME" required="false" defaultValue="00:00:00"/> <column name="cuein" phpName="DbCuein" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="cueout" phpName="DbCueout" type="TIME" required="false" defaultValue="00:00:00"/> <column name="cueout" phpName="DbCueout" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="fadein" phpName="DbFadein" type="TIME" required="false" defaultValue="00:00:00"/> <column name="fadein" phpName="DbFadein" type="TIME" required="false" defaultValue="00:00:00"/>
<column name="fadeout" phpName="DbFadeout" type="TIME" required="false" defaultValue="00:00:00"/> <column name="fadeout" phpName="DbFadeout" type="TIME" required="false" defaultValue="00:00:00"/>
<foreign-key foreignTable="cc_files" name="cc_playlistcontents_file_id_fkey" onDelete="CASCADE"> <foreign-key foreignTable="cc_files" name="cc_playlistcontents_file_id_fkey" onDelete="CASCADE">
@ -273,13 +273,14 @@
<column name="starts" phpName="DbStarts" type="TIMESTAMP" required="true"/> <column name="starts" phpName="DbStarts" type="TIMESTAMP" required="true"/>
<column name="ends" phpName="DbEnds" type="TIMESTAMP" required="true"/> <column name="ends" phpName="DbEnds" type="TIMESTAMP" required="true"/>
<column name="file_id" phpName="DbFileId" type="INTEGER" required="false"/> <column name="file_id" phpName="DbFileId" type="INTEGER" required="false"/>
<column name="clip_length" phpName="DbClipLength" type="TIME" required="false" defaultValue="00:00:00"/> <column name="clip_length" phpName="DbClipLength" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="fade_in" phpName="DbFadeIn" type="TIME" required="false" defaultValue="00:00:00"/> <column name="fade_in" phpName="DbFadeIn" type="TIME" required="false" defaultValue="00:00:00"/>
<column name="fade_out" phpName="DbFadeOut" type="TIME" required="false" defaultValue="00:00:00"/> <column name="fade_out" phpName="DbFadeOut" type="TIME" required="false" defaultValue="00:00:00"/>
<column name="cue_in" phpName="DbCueIn" type="TIME" required="false" defaultValue="00:00:00"/> <column name="cue_in" phpName="DbCueIn" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="cue_out" phpName="DbCueOut" type="TIME" required="false" defaultValue="00:00:00"/> <column name="cue_out" phpName="DbCueOut" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="media_item_played" phpName="DbMediaItemPlayed" type="BOOLEAN" required="false" defaultValue="0"/> <column name="media_item_played" phpName="DbMediaItemPlayed" type="BOOLEAN" required="false" defaultValue="0"/>
<column name="instance_id" phpName="DbInstanceId" type="INTEGER" required="true"/> <column name="instance_id" phpName="DbInstanceId" type="INTEGER" required="true"/>
<column name="status" phpName="DbStatus" type="SMALLINT" required="true" defaultValue="1"/>
<!-- This foreign key is still useful even though it may seem we don't ever delete cc_show_instances anymore. <!-- This foreign key is still useful even though it may seem we don't ever delete cc_show_instances anymore.
We will do delete them in some cases (when editing a show and changing the repeating days of the week We will do delete them in some cases (when editing a show and changing the repeating days of the week
for example. \ for example. \

View File

@ -315,9 +315,9 @@ CREATE TABLE "cc_playlistcontents"
"playlist_id" INTEGER, "playlist_id" INTEGER,
"file_id" INTEGER, "file_id" INTEGER,
"position" INTEGER, "position" INTEGER,
"cliplength" TIME default '00:00:00', "cliplength" interval default '00:00:00',
"cuein" TIME default '00:00:00', "cuein" interval default '00:00:00',
"cueout" TIME default '00:00:00', "cueout" interval default '00:00:00',
"fadein" TIME default '00:00:00', "fadein" TIME default '00:00:00',
"fadeout" TIME default '00:00:00', "fadeout" TIME default '00:00:00',
PRIMARY KEY ("id") PRIMARY KEY ("id")
@ -364,13 +364,14 @@ CREATE TABLE "cc_schedule"
"starts" TIMESTAMP NOT NULL, "starts" TIMESTAMP NOT NULL,
"ends" TIMESTAMP NOT NULL, "ends" TIMESTAMP NOT NULL,
"file_id" INTEGER, "file_id" INTEGER,
"clip_length" TIME default '00:00:00', "clip_length" interval default '00:00:00',
"fade_in" TIME default '00:00:00', "fade_in" TIME default '00:00:00',
"fade_out" TIME default '00:00:00', "fade_out" TIME default '00:00:00',
"cue_in" TIME default '00:00:00', "cue_in" interval default '00:00:00',
"cue_out" TIME default '00:00:00', "cue_out" interval default '00:00:00',
"media_item_played" BOOLEAN default 'f', "media_item_played" BOOLEAN default 'f',
"instance_id" INTEGER NOT NULL, "instance_id" INTEGER NOT NULL,
"status" INT2 default 1 NOT NULL,
PRIMARY KEY ("id") PRIMARY KEY ("id")
); );

View File

@ -30,7 +30,7 @@ button.ColVis_Button::-moz-focus-inner {
div.ColVis_collectionBackground { div.ColVis_collectionBackground {
background-color: black; background-color: black;
z-index: 996; z-index: 1003;
} }
div.ColVis_collection { div.ColVis_collection {
@ -39,7 +39,7 @@ div.ColVis_collection {
background-color: #999; background-color: #999;
padding: 3px; padding: 3px;
border: 1px solid #ccc; border: 1px solid #ccc;
z-index: 998; z-index: 1005;
} }
div.ColVis_collection button.ColVis_Button { div.ColVis_collection button.ColVis_Button {
@ -51,7 +51,7 @@ div.ColVis_collection button.ColVis_Button {
div.ColVis_catcher { div.ColVis_catcher {
position: absolute; position: absolute;
z-index: 997; z-index: 1004;
} }
.disabled { .disabled {

View File

@ -79,3 +79,8 @@
.library_year { .library_year {
text-align: center; text-align: center;
} }
.library_sr,
.library_bitrate {
text-align: right;
}

View File

@ -457,7 +457,5 @@ div.helper li {
} }
li.spl_empty { li.spl_empty {
text-align: center;
height: 56px; height: 56px;
border:2px dashed black;
} }

View File

@ -34,4 +34,17 @@ tr.cursor-selected-row .marker {
.sb-over { .sb-over {
background-color:#ff3030; background-color:#ff3030;
}
.sb-now-playing {
background-color:#17eb25 !important;
}
.ui-dialog .wrapper {
margin: 0;
padding: 10px 0 0 0;
}
.ui-dialog .ui-buttonset {
margin-right: 0 !important;
} }

View File

@ -601,6 +601,7 @@ dl.inline-list dd {
} }
.dataTables_info { .dataTables_info {
float: left;
padding: 8px 0 0 8px; padding: 8px 0 0 8px;
font-size:12px; font-size:12px;
color:#555555; color:#555555;
@ -608,6 +609,7 @@ dl.inline-list dd {
} }
.dataTables_paginate { .dataTables_paginate {
float: right;
padding: 8px 0 8px 8px; padding: 8px 0 8px 8px;
} }
.dataTables_paginate .ui-button { .dataTables_paginate .ui-button {
@ -618,7 +620,7 @@ dl.inline-list dd {
} }
.dataTables_filter input { .dataTables_filter input {
background: url("images/search_auto_bg.png") no-repeat scroll 0 0 #DDDDDD; background: url("images/search_auto_bg.png") no-repeat scroll 0 0 #DDDDDD;
width: 60%; width: 55%;
border: 1px solid #5B5B5B; border: 1px solid #5B5B5B;
margin-left: -8px; margin-left: -8px;
padding: 4px 3px 4px 25px; padding: 4px 3px 4px 25px;

View File

@ -0,0 +1,29 @@
var AIRTIME = (function(AIRTIME){
var mod,
DEFAULT_CLASS = 'ui-button ui-state-default',
DISABLED_CLASS = 'ui-state-disabled';
if (AIRTIME.button === undefined) {
AIRTIME.button = {};
}
mod = AIRTIME.button;
mod.enableButton = function(c) {
var button = $("."+c).find("button");
if (button.hasClass(DISABLED_CLASS)) {
button.removeClass(DISABLED_CLASS);
}
};
mod.disableButton = function(c) {
var button = $("."+c).find("button");
if (!button.hasClass(DISABLED_CLASS)) {
button.addClass(DISABLED_CLASS);
}
};
return AIRTIME;
}(AIRTIME || {}));

View File

@ -7,6 +7,24 @@ var AIRTIME = (function(AIRTIME){
AIRTIME.library.events = {}; AIRTIME.library.events = {};
mod = AIRTIME.library.events; mod = AIRTIME.library.events;
mod.enableAddButtonCheck = function() {
var selected = $('#library_display tr[id ^= "au"] input[type=checkbox]').filter(":checked"),
sortable = $('#spl_sortable'),
check = false;
//make sure audioclips are selected and a playlist is currently open.
if (selected.length !== 0 && sortable.length !== 0) {
check = true;
}
if (check === true) {
AIRTIME.button.enableButton("library_group_add");
}
else {
AIRTIME.button.disableButton("library_group_add");
}
};
mod.fnRowCallback = function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { mod.fnRowCallback = function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
var $nRow = $(nRow); var $nRow = $(nRow);
@ -63,22 +81,22 @@ var AIRTIME = (function(AIRTIME){
*/ */
mod.setupLibraryToolbar = function( oLibTable ) { mod.setupLibraryToolbar = function( oLibTable ) {
var aButtons, var aButtons,
fnResetCol,
fnAddSelectedItems; fnAddSelectedItems;
fnAddSelectedItems = function() { fnAddSelectedItems = function() {
var oLibTT = TableTools.fnGetInstance('library_display'), var oLibTT = TableTools.fnGetInstance('library_display'),
aData = oLibTT.fnGetSelectedData(), aData = oLibTT.fnGetSelectedData(),
item, i,
temp, temp,
length,
aMediaIds = []; aMediaIds = [];
//process selected files/playlists. //process selected files/playlists.
for (item in aData) { for (i = 0, length = aData.length; i < length; i++) {
temp = aData[item]; temp = aData[i];
if (temp !== null && temp.hasOwnProperty('id') && temp.ftype === "audioclip") { if (temp.ftype === "audioclip") {
aMediaIds.push(temp.id); aMediaIds.push(temp.id);
} }
} }
AIRTIME.playlist.fnAddItems(aMediaIds, undefined, 'after'); AIRTIME.playlist.fnAddItems(aMediaIds, undefined, 'after');
@ -88,8 +106,8 @@ var AIRTIME = (function(AIRTIME){
//[1] = id //[1] = id
//[2] = enabled //[2] = enabled
//[3] = click event //[3] = click event
aButtons = [["Delete", "library_group_delete", true, AIRTIME.library.fnDeleteSelectedItems], aButtons = [["Delete", "library_group_delete", false, AIRTIME.library.fnDeleteSelectedItems],
["Add", "library_group_add", true, fnAddSelectedItems]]; ["Add", "library_group_add", false, fnAddSelectedItems]];
addToolBarButtonsLibrary(aButtons); addToolBarButtonsLibrary(aButtons);
}; };

View File

@ -8,6 +8,24 @@ var AIRTIME = (function(AIRTIME){
AIRTIME.library.events = {}; AIRTIME.library.events = {};
mod = AIRTIME.library.events; mod = AIRTIME.library.events;
mod.enableAddButtonCheck = function() {
var selected = $('#library_display tr input[type=checkbox]').filter(":checked"),
cursor = $('tr.cursor-selected-row'),
check = false;
//make sure library items are selected and a cursor is selected.
if (selected.length !== 0 && cursor.length !== 0) {
check = true;
}
if (check === true) {
AIRTIME.button.enableButton("library_group_add");
}
else {
AIRTIME.button.disableButton("library_group_add");
}
};
mod.fnRowCallback = function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { mod.fnRowCallback = function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
var $nRow = $(nRow); var $nRow = $(nRow);
@ -21,7 +39,6 @@ var AIRTIME = (function(AIRTIME){
$('#library_display tr:not(:first)').draggable({ $('#library_display tr:not(:first)').draggable({
helper: function(){ helper: function(){
var selected = $('#library_display tr:not(:first) input:checked').parents('tr'), var selected = $('#library_display tr:not(:first) input:checked').parents('tr'),
aItems = [],
container, container,
thead = $("#show_builder_table thead"), thead = $("#show_builder_table thead"),
colspan = thead.find("th").length, colspan = thead.find("th").length,
@ -34,10 +51,10 @@ var AIRTIME = (function(AIRTIME){
} }
if (selected.length === 1) { if (selected.length === 1) {
message = "Moving "+selected.length+" Item." message = "Moving "+selected.length+" Item.";
} }
else { else {
message = "Moving "+selected.length+" Items." message = "Moving "+selected.length+" Items.";
} }
container = $('<div/>').attr('id', 'draggingContainer') container = $('<div/>').attr('id', 'draggingContainer')
@ -61,8 +78,6 @@ var AIRTIME = (function(AIRTIME){
mod.setupLibraryToolbar = function(oLibTable) { mod.setupLibraryToolbar = function(oLibTable) {
var aButtons, var aButtons,
fnTest,
fnResetCol,
fnAddSelectedItems, fnAddSelectedItems,
fnAddSelectedItems = function() { fnAddSelectedItems = function() {
@ -75,7 +90,7 @@ var AIRTIME = (function(AIRTIME){
aSchedIds = []; aSchedIds = [];
//process selected files/playlists. //process selected files/playlists.
for (i=0, length = aData.length; i < length; i++) { for (i = 0, length = aData.length; i < length; i++) {
temp = aData[i]; temp = aData[i];
aMediaIds.push({"id": temp.id, "type": temp.ftype}); aMediaIds.push({"id": temp.id, "type": temp.ftype});
} }
@ -93,12 +108,13 @@ var AIRTIME = (function(AIRTIME){
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds); AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
}; };
//[0] = button text //[0] = button text
//[1] = id //[1] = id
//[2] = enabled //[2] = enabled
//[3] = click event //[3] = click event
aButtons = [["Delete", "library_group_delete", true, AIRTIME.library.fnDeleteSelectedItems], aButtons = [["Delete", "library_group_delete", false, AIRTIME.library.fnDeleteSelectedItems],
["Add", "library_group_add", true, fnAddSelectedItems]]; ["Add", "library_group_add", false, fnAddSelectedItems]];
addToolBarButtonsLibrary(aButtons); addToolBarButtonsLibrary(aButtons);
}; };

View File

@ -1,5 +1,6 @@
var AIRTIME = (function(AIRTIME){ var AIRTIME = (function(AIRTIME){
var mod; var mod,
libraryInit;
if (AIRTIME.library === undefined) { if (AIRTIME.library === undefined) {
AIRTIME.library = {}; AIRTIME.library = {};
@ -13,6 +14,10 @@ var AIRTIME = (function(AIRTIME){
$.post("/library/delete", $.post("/library/delete",
{"format": "json", "media": aMedia}, {"format": "json", "media": aMedia},
function(json){ function(json){
if (json.message !== undefined) {
alert(json.message);
}
oLibTT.fnSelectNone(); oLibTT.fnSelectNone();
oLibTable.fnDraw(); oLibTable.fnDraw();
}); });
@ -36,6 +41,408 @@ var AIRTIME = (function(AIRTIME){
AIRTIME.library.fnDeleteItems(aMedia); AIRTIME.library.fnDeleteItems(aMedia);
}; };
libraryInit = function() {
var oTable;
oTable = $('#library_display').dataTable( {
"aoColumns": [
/* Checkbox */ {"sTitle": "<input type='checkbox' name='pl_cb_all'>", "mDataProp": "checkbox", "bSortable": false, "bSearchable": false, "sWidth": "25px", "sClass": "library_checkbox"},
/* Type */ {"sTitle": "", "mDataProp": "image", "bSearchable": false, "sWidth": "25px", "sClass": "library_type", "iDataSort": 2},
/* ftype */ {"sTitle": "", "mDataProp": "ftype", "bSearchable": false, "bVisible": false},
/* Title */ {"sTitle": "Title", "mDataProp": "track_title", "sClass": "library_title"},
/* Creator */ {"sTitle": "Creator", "mDataProp": "artist_name", "sClass": "library_creator"},
/* Album */ {"sTitle": "Album", "mDataProp": "album_title", "sClass": "library_album"},
/* Genre */ {"sTitle": "Genre", "mDataProp": "genre", "sClass": "library_genre"},
/* Year */ {"sTitle": "Year", "mDataProp": "year", "sClass": "library_year", "sWidth": "60px"},
/* Length */ {"sTitle": "Length", "mDataProp": "length", "sClass": "library_length", "sWidth": "80px"},
/* Upload Time */ {"sTitle": "Uploaded", "mDataProp": "utime", "sClass": "library_upload_time"},
/* Last Modified */ {"sTitle": "Last Modified", "mDataProp": "mtime", "bVisible": false, "sClass": "library_modified_time"},
/* Track Number */ {"sTitle": "Track", "mDataProp": "track_number", "bSearchable": false, "bVisible": false, "sClass": "library_track"},
/* Mood */ {"sTitle": "Mood", "mDataProp": "mood", "bSearchable": false, "bVisible": false, "sClass": "library_mood"},
/* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"},
/* Composer */ {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer"},
/* Website */ {"sTitle": "Website", "mDataProp": "info_url", "bSearchable": false, "bVisible": false, "sClass": "library_url"},
/* Bit Rate */ {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate", "sWidth": "80px"},
/* Sample Rate */ {"sTitle": "Sample", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr", "sWidth": "80px"},
/* ISRC Number */ {"sTitle": "ISRC", "mDataProp": "isrc_number", "bSearchable": false, "bVisible": false, "sClass": "library_isrc"},
/* Encoded */ {"sTitle": "Encoded", "mDataProp": "encoded_by", "bSearchable": false, "bVisible": false, "sClass": "library_encoded"},
/* Label */ {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label"},
/* Copyright */ {"sTitle": "Copyright", "mDataProp": "copyright", "bSearchable": false, "bVisible": false, "sClass": "library_copyright"},
/* Mime */ {"sTitle": "Mime", "mDataProp": "mime", "bSearchable": false, "bVisible": false, "sClass": "library_mime"},
/* Language */ {"sTitle": "Language", "mDataProp": "language", "bSearchable": false, "bVisible": false, "sClass": "library_language"}
],
"bProcessing": true,
"bServerSide": true,
"bStateSave": true,
"fnStateSaveParams": function (oSettings, oData) {
//remove oData components we don't want to save.
delete oData.oSearch;
delete oData.aoSearchCols;
},
"fnStateSave": function (oSettings, oData) {
$.ajax({
url: "/usersettings/set-library-datatable",
type: "POST",
data: {settings : oData, format: "json"},
dataType: "json",
success: function(){},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
},
"fnStateLoad": function (oSettings) {
var o;
$.ajax({
url: "/usersettings/get-library-datatable",
type: "GET",
data: {format: "json"},
dataType: "json",
async: false,
success: function(json){
o = json.settings;
},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
return o;
},
"fnStateLoadParams": function (oSettings, oData) {
var i,
length,
a = oData.abVisCols;
//putting serialized data back into the correct js type to make
//sure everything works properly.
for (i = 0, length = a.length; i < length; i++) {
a[i] = (a[i] === "true") ? true : false;
}
a = oData.ColReorder;
for (i = 0, length = a.length; i < length; i++) {
a[i] = parseInt(a[i], 10);
}
oData.iEnd = parseInt(oData.iEnd, 10);
oData.iLength = parseInt(oData.iLength, 10);
oData.iStart = parseInt(oData.iStart, 10);
oData.iCreate = parseInt(oData.iCreate, 10);
},
"sAjaxSource": "/Library/contents",
"fnServerData": function ( sSource, aoData, fnCallback ) {
var type;
aoData.push( { name: "format", value: "json"} );
//push whether to search files/playlists or all.
type = $("#library_display_type").find("select").val();
type = (type === undefined) ? 0 : type;
aoData.push( { name: "type", value: type} );
$.ajax( {
"dataType": 'json',
"type": "GET",
"url": sSource,
"data": aoData,
"success": fnCallback
} );
},
"fnRowCallback": AIRTIME.library.events.fnRowCallback,
"fnCreatedRow": function( nRow, aData, iDataIndex ) {
//call the context menu so we can prevent the event from propagating.
$(nRow).find('td:not(.library_checkbox)').click(function(e){
$(this).contextMenu({x: e.pageX, y: e.pageY});
return false;
});
//add a tool tip to appear when the user clicks on the type icon.
$(nRow).find("td:not(:first, td>img)").qtip({
content: {
text: "Loading...",
title: {
text: aData.track_title
},
ajax: {
url: "/Library/get-file-meta-data",
type: "get",
data: ({format: "html", id : aData.id, type: aData.ftype}),
success: function(data, status) {
this.set('content.text', data);
}
}
},
position: {
target: 'event',
adjust: {
resize: true,
method: "flip flip"
},
my: 'left center',
at: 'right center',
viewport: $(window), // Keep the tooltip on-screen at all times
effect: false // Disable positioning animation
},
style: {
classes: "ui-tooltip-dark"
},
show: 'mousedown',
events: {
show: function(event, api) {
// Only show the tooltip if it was a right-click
if(event.originalEvent.button !== 2) {
event.preventDefault();
}
}
},
hide: 'mouseout'
});
},
"fnDrawCallback": AIRTIME.library.events.fnDrawCallback,
"fnHeaderCallback": function(nHead) {
$(nHead).find("input[type=checkbox]").attr("checked", false);
},
"aaSorting": [[3, 'asc']],
"sPaginationType": "full_numbers",
"bJQueryUI": true,
"bAutoWidth": false,
"oLanguage": {
"sSearch": ""
},
// R = ColReorder, C = ColVis, T = TableTools
"sDom": 'Rl<"#library_display_type">fr<"H"T<"library_toolbar"C>>t<"F"ip>',
"oTableTools": {
"sRowSelect": "multi",
"aButtons": [],
"fnRowSelected": function ( node ) {
var selected;
//seems to happen if everything is selected
if ( node === null) {
selected = oTable.find("input[type=checkbox]");
selected.attr("checked", true);
}
else {
$(node).find("input[type=checkbox]").attr("checked", true);
selected = oTable.find("input[type=checkbox]").filter(":checked");
}
//checking to enable buttons
AIRTIME.button.enableButton("library_group_delete");
AIRTIME.library.events.enableAddButtonCheck();
},
"fnRowDeselected": function ( node ) {
var selected;
//seems to happen if everything is deselected
if ( node === null) {
oTable.find("input[type=checkbox]").attr("checked", false);
selected = [];
}
else {
$(node).find("input[type=checkbox]").attr("checked", false);
selected = oTable.find("input[type=checkbox]").filter(":checked");
}
//checking to disable buttons
if (selected.length === 0) {
AIRTIME.button.disableButton("library_group_delete");
}
AIRTIME.library.events.enableAddButtonCheck();
}
},
"oColVis": {
"buttonText": "Show/Hide Columns",
"sAlign": "right",
"aiExclude": [0, 1, 2],
"sSize": "css"
},
"oColReorder": {
"iFixedColumns": 2
}
});
oTable.fnSetFilteringDelay(350);
AIRTIME.library.events.setupLibraryToolbar(oTable);
$("#library_display_type")
.addClass("dataTables_type")
.append('<select name="library_display_type" />')
.find("select")
.append('<option value="0">All</option>')
.append('<option value="1">Files</option>')
.append('<option value="2">Playlists</option>')
.end()
.change(function(ev){
oTable.fnDraw();
});
$('[name="pl_cb_all"]').click(function(){
var oTT = TableTools.fnGetInstance('library_display');
if ($(this).is(":checked")) {
oTT.fnSelectAll();
}
else {
oTT.fnSelectNone();
}
});
checkImportStatus();
setInterval( checkImportStatus, 5000 );
setInterval( checkSCUploadStatus, 5000 );
addQtipToSCIcons();
$.contextMenu({
selector: '#library_display td:not(.library_checkbox)',
trigger: "left",
ignoreRightClick: true,
build: function($el, e) {
var data, screen, items, callback, $tr;
$tr = $el.parent();
data = $tr.data("aData");
screen = $tr.data("screen");
function processMenuItems(oItems) {
//define an add to playlist callback.
if (oItems.pl_add !== undefined) {
callback = function() {
AIRTIME.playlist.fnAddItems([data.id], undefined, 'after');
};
oItems.pl_add.callback = callback;
}
//define an edit callback.
if (oItems.edit !== undefined) {
if (data.ftype === "audioclip") {
callback = function() {
document.location.href = oItems.edit.url;
};
}
else {
callback = function() {
AIRTIME.playlist.fnEdit(data.id);
};
}
oItems.edit.callback = callback;
}
//define a delete callback.
if (oItems.del !== undefined) {
//delete through the playlist controller, will reset
//playlist screen if this is the currently edited playlist.
if (data.ftype === "playlist" && screen === "playlist") {
callback = function() {
if (confirm('Are you sure you want to delete the selected item?')) {
AIRTIME.playlist.fnDelete(data.id);
}
};
}
else {
callback = function() {
var media = [];
if (confirm('Are you sure you want to delete the selected item?')) {
media.push({"id": data.id, "type": data.ftype});
$.post(oItems.del.url, {format: "json", media: media }, function(json){
var oTable;
if (json.message) {
alert(json.message);
}
oTable = $("#library_display").dataTable();
oTable.fnDeleteRow( $tr[0] );
});
}
};
}
oItems.del.callback = callback;
}
//define a download callback.
if (oItems.download !== undefined) {
callback = function() {
document.location.href = oItems.download.url;
};
oItems.download.callback = callback;
}
//add callbacks for Soundcloud menu items.
if (oItems.soundcloud !== undefined) {
var soundcloud = oItems.soundcloud.items;
//define an upload to soundcloud callback.
if (soundcloud.upload !== undefined) {
callback = function() {
$.post(soundcloud.upload.url, function(){
addProgressIcon(data.id);
});
};
soundcloud.upload.callback = callback;
}
//define a view on soundcloud callback
if (soundcloud.view !== undefined) {
callback = function() {
window.open(soundcloud.view.url);
};
soundcloud.view.callback = callback;
}
}
items = oItems;
}
request = $.ajax({
url: "/library/context-menu",
type: "GET",
data: {id : data.id, type: data.ftype, format: "json", "screen": screen},
dataType: "json",
async: false,
success: function(json){
processMenuItems(json.items);
}
});
return {
items: items
};
}
});
};
mod.libraryInit = libraryInit;
return AIRTIME; return AIRTIME;
}(AIRTIME || {})); }(AIRTIME || {}));
@ -43,42 +450,45 @@ var AIRTIME = (function(AIRTIME){
function addToolBarButtonsLibrary(aButtons) { function addToolBarButtonsLibrary(aButtons) {
var i, var i,
length = aButtons.length, length = aButtons.length,
libToolBar, libToolBar = $(".library_toolbar"),
html, html,
buttonClass = '', buttonClass = '',
DEFAULT_CLASS = 'ui-button ui-state-default', DEFAULT_CLASS = 'ui-button ui-state-default',
DISABLED_CLASS = 'ui-state-disabled'; DISABLED_CLASS = 'ui-state-disabled',
fn;
libToolBar = $(".library_toolbar"); for ( i = 0; i < length; i += 1 ) {
for ( i=0; i < length; i+=1 ) {
buttonClass = ''; buttonClass = '';
//add disabled class if not enabled. //add disabled class if not enabled.
if (aButtons[i][2] === false) { if (aButtons[i][2] === false) {
buttonClass+=DISABLED_CLASS; buttonClass += DISABLED_CLASS;
} }
html = '<div id="'+aButtons[i][1]+'" class="ColVis TableTools"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>'; html = '<div class="ColVis TableTools '+aButtons[i][1]+'"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>';
libToolBar.append(html); libToolBar.append(html);
libToolBar.find("#"+aButtons[i][1]).click(aButtons[i][3]);
//create a closure to preserve the state of i.
(function(index){
libToolBar.find("."+aButtons[index][1]).click(function(){
fn = function() {
var $button = $(this).find("button");
//only call the passed function if the button is enabled.
if (!$button.hasClass(DISABLED_CLASS)) {
aButtons[index][3]();
}
};
fn.call(this);
});
}(i));
} }
} }
function enableGroupBtn(btnId, func) {
btnId = '#' + btnId;
if ($(btnId).hasClass('ui-state-disabled')) {
$(btnId).removeClass('ui-state-disabled');
}
}
function disableGroupBtn(btnId) {
btnId = '#' + btnId;
if (!$(btnId).hasClass('ui-state-disabled')) {
$(btnId).addClass('ui-state-disabled');
}
}
function checkImportStatus(){ function checkImportStatus(){
$.getJSON('/Preference/is-import-in-progress', function(data){ $.getJSON('/Preference/is-import-in-progress', function(data){
var div = $('#import_status'); var div = $('#import_status');
@ -210,389 +620,4 @@ function addQtipToSCIcons(){
}); });
} }
}); });
} }
$(document).ready(function() {
var oTable;
oTable = $('#library_display').dataTable( {
"aoColumns": [
/* Checkbox */ {"sTitle": "<input type='checkbox' name='pl_cb_all'>", "mDataProp": "checkbox", "bSortable": false, "bSearchable": false, "sWidth": "25px", "sClass": "library_checkbox"},
/* Type */ {"sTitle": "", "mDataProp": "image", "bSearchable": false, "sWidth": "25px", "sClass": "library_type", "iDataSort": 2},
/* ftype */ {"sTitle": "", "mDataProp": "ftype", "bSearchable": false, "bVisible": false},
/* Title */ {"sTitle": "Title", "mDataProp": "track_title", "sClass": "library_title"},
/* Creator */ {"sTitle": "Creator", "mDataProp": "artist_name", "sClass": "library_creator"},
/* Album */ {"sTitle": "Album", "mDataProp": "album_title", "sClass": "library_album"},
/* Genre */ {"sTitle": "Genre", "mDataProp": "genre", "sClass": "library_genre"},
/* Year */ {"sTitle": "Year", "mDataProp": "year", "sClass": "library_year", "sWidth": "60px"},
/* Length */ {"sTitle": "Length", "mDataProp": "length", "sClass": "library_length", "sWidth": "80px"},
/* Upload Time */ {"sTitle": "Uploaded", "mDataProp": "utime", "sClass": "library_upload_time"},
/* Last Modified */ {"sTitle": "Last Modified", "mDataProp": "mtime", "bVisible": false, "sClass": "library_modified_time"},
/* Track Number */ {"sTitle": "Track", "mDataProp": "track_number", "bSearchable": false, "bVisible": false, "sClass": "library_track"},
/* Mood */ {"sTitle": "Mood", "mDataProp": "mood", "bSearchable": false, "bVisible": false, "sClass": "library_mood"},
/* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"},
/* Composer */ {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer"},
/* Website */ {"sTitle": "Website", "mDataProp": "info_url", "bSearchable": false, "bVisible": false, "sClass": "library_url"},
/* Bit Rate */ {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate"},
/* Sameple Rate */ {"sTitle": "Sample Rate", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr"},
/* ISRC Number */ {"sTitle": "ISRC", "mDataProp": "isrc_number", "bSearchable": false, "bVisible": false, "sClass": "library_isrc"},
/* Encoded */ {"sTitle": "Encoded", "mDataProp": "encoded_by", "bSearchable": false, "bVisible": false, "sClass": "library_encoded"},
/* Label */ {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label"},
/* Copyright */ {"sTitle": "Copyright", "mDataProp": "copyright", "bSearchable": false, "bVisible": false, "sClass": "library_copyright"},
/* Mime */ {"sTitle": "Mime", "mDataProp": "mime", "bSearchable": false, "bVisible": false, "sClass": "library_mime"},
/* Language */ {"sTitle": "Language", "mDataProp": "language", "bSearchable": false, "bVisible": false, "sClass": "library_language"}
],
"bProcessing": true,
"bServerSide": true,
"bStateSave": true,
"fnStateSaveParams": function (oSettings, oData) {
//remove oData components we don't want to save.
delete oData.oSearch;
delete oData.aoSearchCols;
},
"fnStateSave": function (oSettings, oData) {
$.ajax({
url: "/usersettings/set-library-datatable",
type: "POST",
data: {settings : oData, format: "json"},
dataType: "json",
success: function(){},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
},
"fnStateLoad": function (oSettings) {
var o;
$.ajax({
url: "/usersettings/get-library-datatable",
type: "GET",
data: {format: "json"},
dataType: "json",
async: false,
success: function(json){
o = json.settings;
},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
return o;
},
"fnStateLoadParams": function (oSettings, oData) {
var i,
length,
a = oData.abVisCols;
//putting serialized data back into the correct js type to make
//sure everything works properly.
for (i = 0, length = a.length; i < length; i++) {
a[i] = (a[i] === "true") ? true : false;
}
a = oData.ColReorder;
for (i = 0, length = a.length; i < length; i++) {
a[i] = parseInt(a[i], 10);
}
oData.iEnd = parseInt(oData.iEnd, 10);
oData.iLength = parseInt(oData.iLength, 10);
oData.iStart = parseInt(oData.iStart, 10);
oData.iCreate = parseInt(oData.iCreate, 10);
},
"sAjaxSource": "/Library/contents",
"fnServerData": function ( sSource, aoData, fnCallback ) {
var type;
aoData.push( { name: "format", value: "json"} );
//push whether to search files/playlists or all.
type = $("#library_display_type").find("select").val();
type = (type === undefined) ? 0 : type;
aoData.push( { name: "type", value: type} );
$.ajax( {
"dataType": 'json',
"type": "GET",
"url": sSource,
"data": aoData,
"success": fnCallback
} );
},
"fnRowCallback": AIRTIME.library.events.fnRowCallback,
"fnCreatedRow": function( nRow, aData, iDataIndex ) {
//call the context menu so we can prevent the event from propagating.
$(nRow).find('td:not(.library_checkbox)').click(function(e){
$(this).contextMenu({x: e.pageX, y: e.pageY});
return false;
});
//add a tool tip to appear when the user clicks on the type icon.
$(nRow).find("td:not(:first, td>img)").qtip({
content: {
text: "Loading...",
title: {
text: aData.track_title
},
ajax: {
url: "/Library/get-file-meta-data",
type: "get",
data: ({format: "html", id : aData.id, type: aData.ftype}),
success: function(data, status) {
this.set('content.text', data);
}
}
},
position: {
target: 'event',
adjust: {
resize: true,
method: "flip flip"
},
my: 'left center',
at: 'right center',
viewport: $(window), // Keep the tooltip on-screen at all times
effect: false // Disable positioning animation
},
style: {
classes: "ui-tooltip-dark"
},
show: 'mousedown',
events: {
show: function(event, api) {
// Only show the tooltip if it was a right-click
if(event.originalEvent.button !== 2) {
event.preventDefault();
}
}
},
hide: 'mouseout'
});
},
"fnDrawCallback": AIRTIME.library.events.fnDrawCallback,
"fnHeaderCallback": function(nHead) {
$(nHead).find("input[type=checkbox]").attr("checked", false);
},
"aaSorting": [[3, 'asc']],
"sPaginationType": "full_numbers",
"bJQueryUI": true,
"bAutoWidth": false,
"oLanguage": {
"sSearch": ""
},
// R = ColReorder, C = ColVis, T = TableTools
"sDom": 'Rl<"#library_display_type">fr<"H"T<"library_toolbar"C>>t<"F"ip>',
"oTableTools": {
"sRowSelect": "multi",
"aButtons": [],
"fnRowSelected": function ( node ) {
//seems to happen if everything is selected
if ( node === null) {
oTable.find("input[type=checkbox]").attr("checked", true);
}
else {
$(node).find("input[type=checkbox]").attr("checked", true);
}
},
"fnRowDeselected": function ( node ) {
//seems to happen if everything is deselected
if ( node === null) {
oTable.find("input[type=checkbox]").attr("checked", false);
}
else {
$(node).find("input[type=checkbox]").attr("checked", false);
}
}
},
"oColVis": {
"buttonText": "Show/Hide Columns",
"sAlign": "right",
"aiExclude": [0, 1, 2],
"sSize": "css"
},
"oColReorder": {
"iFixedColumns": 2
}
});
oTable.fnSetFilteringDelay(350);
AIRTIME.library.events.setupLibraryToolbar(oTable);
$("#library_display_type")
.addClass("dataTables_type")
.append('<select name="library_display_type" />')
.find("select")
.append('<option value="0">All</option>')
.append('<option value="1">Files</option>')
.append('<option value="2">Playlists</option>')
.end()
.change(function(ev){
oTable.fnDraw();
});
$('[name="pl_cb_all"]').click(function(){
var oTT = TableTools.fnGetInstance('library_display');
if ($(this).is(":checked")) {
oTT.fnSelectAll();
}
else {
oTT.fnSelectNone();
}
});
checkImportStatus();
setInterval( checkImportStatus, 5000 );
setInterval( checkSCUploadStatus, 5000 );
addQtipToSCIcons();
$.contextMenu({
selector: '#library_display td:not(.library_checkbox)',
trigger: "left",
ignoreRightClick: true,
build: function($el, e) {
var x, request, data, screen, items, callback, $tr;
$tr = $el.parent();
data = $tr.data("aData");
screen = $tr.data("screen");
function processMenuItems(oItems) {
//define an add to playlist callback.
if (oItems.pl_add !== undefined) {
callback = function() {
AIRTIME.playlist.fnAddItems([data.id], undefined, 'after');
};
oItems.pl_add.callback = callback;
}
//define an edit callback.
if (oItems.edit !== undefined) {
if (data.ftype === "audioclip") {
callback = function() {
document.location.href = oItems.edit.url;
};
}
else {
callback = function() {
AIRTIME.playlist.fnEdit(data.id);
};
}
oItems.edit.callback = callback;
}
//define a delete callback.
if (oItems.del !== undefined) {
//delete through the playlist controller, will reset
//playlist screen if this is the currently edited playlist.
if (data.ftype === "playlist" && screen === "playlist") {
callback = function() {
if (confirm('Are you sure you want to delete the selected item?')) {
AIRTIME.playlist.fnDelete(data.id);
}
};
}
else {
callback = function() {
var media = [];
if (confirm('Are you sure you want to delete the selected item?')) {
media.push({"id": data.id, "type": data.ftype});
$.post(oItems.del.url, {format: "json", media: media }, function(json){
var oTable, tr;
if (json.message) {
alert(json.message);
}
oTable = $("#library_display").dataTable();
oTable.fnDeleteRow( $tr[0] );
});
}
};
}
oItems.del.callback = callback;
}
//define a download callback.
if (oItems.download !== undefined) {
callback = function() {
document.location.href = oItems.download.url;
};
oItems.download.callback = callback;
}
//add callbacks for Soundcloud menu items.
if (oItems.soundcloud !== undefined) {
var soundcloud = oItems.soundcloud.items;
//define an upload to soundcloud callback.
if (soundcloud.upload !== undefined) {
callback = function() {
$.post(soundcloud.upload.url, function(){
addProgressIcon(data.id);
});
};
soundcloud.upload.callback = callback;
}
//define a view on soundcloud callback
if (soundcloud.view !== undefined) {
callback = function() {
window.open(soundcloud.view.url);
};
soundcloud.view.callback = callback;
}
}
items = oItems;
}
request = $.ajax({
url: "/library/context-menu",
type: "GET",
data: {id : data.id, type: data.ftype, format: "json", "screen": screen},
dataType: "json",
async: false,
success: function(json){
processMenuItems(json.items);
}
});
return {
items: items
};
}
});
});

View File

@ -0,0 +1 @@
$(document).ready(AIRTIME.library.libraryInit);

View File

@ -506,18 +506,25 @@ var AIRTIME = (function(AIRTIME){
fnUpdate; fnUpdate;
fnReceive = function(event, ui) { fnReceive = function(event, ui) {
var selected = $('#library_display tr[id^="au"] input:checked').parents('tr'), var aItems = [],
aItems = []; aSelected,
oLibTT = TableTools.fnGetInstance('library_display'),
i,
length;
//filter out anything that isn't an audiofile.
aSelected = oLibTT.fnGetSelectedData();
//if nothing is checked select the dragged item. //if nothing is checked select the dragged item.
if (selected.length === 0) { if (aSelected.length === 0) {
selected = ui.item; aSelected.push(ui.item.data("aData"));
} }
selected.each(function(i, el) { for (i = 0, length = aSelected.length; i < length; i++) {
aItems.push($(el).data("aData").id); if (aSelected[i].ftype === "audioclip") {
}); aItems.push(aSelected[i].id);
}
}
aReceiveItems = aItems; aReceiveItems = aItems;
html = ui.helper.html(); html = ui.helper.html();
}; };

View File

@ -67,21 +67,10 @@ function uploadToSoundCloud(show_instance_id){
} }
} }
function buildContentDialog (json){ function findViewportDimensions() {
var dialog = $(json.dialog), var viewportwidth,
viewportwidth, viewportheight;
viewportheight,
height,
width;
if (json.show_error == true){
alertShowErrorAndReload();
}
dialog.find("#show_progressbar").progressbar({
value: json.percentFilled
});
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use // the more standards compliant browsers (mozilla/netscape/opera/IE7) use
// window.innerWidth and window.innerHeight // window.innerWidth and window.innerHeight
if (typeof window.innerWidth != 'undefined') { if (typeof window.innerWidth != 'undefined') {
@ -101,9 +90,57 @@ function buildContentDialog (json){
viewportheight = document.getElementsByTagName('body')[0].clientHeight; viewportheight = document.getElementsByTagName('body')[0].clientHeight;
} }
height = viewportheight * 2/3; return {
width = viewportwidth * 4/5; width: viewportwidth,
height: viewportheight
};
}
function buildScheduleDialog (json) {
var dialog = $(json.dialog),
viewport = findViewportDimensions(),
height = viewport.height * 0.96,
width = viewport.width * 0.96,
fnServer = AIRTIME.showbuilder.fnServerData;
dialog.dialog({
autoOpen: false,
title: json.title,
width: width,
height: height,
modal: true,
close: closeDialog,
buttons: {"Ok": function() {
dialog.remove();
$("#schedule_calendar").fullCalendar( 'refetchEvents' );
}}
});
//set the start end times so the builder datatables knows its time range.
fnServer.start = json.start;
fnServer.end = json.end;
AIRTIME.library.libraryInit();
AIRTIME.showbuilder.builderDataTable();
dialog.dialog('open');
}
function buildContentDialog (json){
var dialog = $(json.dialog),
viewport = findViewportDimensions(),
height = viewport.height * 2/3,
width = viewport.width * 4/5;
if (json.show_error == true){
alertShowErrorAndReload();
}
dialog.find("#show_progressbar").progressbar({
value: json.percentFilled
});
dialog.dialog({ dialog.dialog({
autoOpen: false, autoOpen: false,
title: 'Show Contents', title: 'Show Contents',
@ -201,8 +238,12 @@ $(document).ready(function() {
if (oItems.schedule !== undefined) { if (oItems.schedule !== undefined) {
callback = function() { callback = function() {
document.location = oItems.schedule.url + "from/" + data.startUnix + "/to/" + data.endUnix;
}; $.post(oItems.schedule.url, {format: "json", id: data.id}, function(json){
buildScheduleDialog(json);
});
};
oItems.schedule.callback = callback; oItems.schedule.callback = callback;
} }

View File

@ -1,6 +1,7 @@
var AIRTIME = (function(AIRTIME){ var AIRTIME = (function(AIRTIME){
var mod, var mod,
oSchedTable; oSchedTable,
fnServerData;
if (AIRTIME.showbuilder === undefined) { if (AIRTIME.showbuilder === undefined) {
AIRTIME.showbuilder = {}; AIRTIME.showbuilder = {};
@ -45,111 +46,6 @@ var AIRTIME = (function(AIRTIME){
}); });
}; };
mod.init = function(oTable) {
oSchedTable = oTable;
};
return AIRTIME;
}(AIRTIME || {}));
$(document).ready(function() {
var tableDiv = $('#show_builder_table'),
oTable,
oBaseDatePickerSettings,
oBaseTimePickerSettings,
fnAddSelectedItems,
fnRemoveSelectedItems,
oRange,
fnServerData;
oBaseDatePickerSettings = {
dateFormat: 'yy-mm-dd',
onSelect: function(sDate, oDatePicker) {
var oDate,
dInput;
dInput = $(this);
oDate = dInput.datepicker( "setDate", sDate );
}
};
oBaseTimePickerSettings = {
showPeriodLabels: false,
showCloseButton: true,
showLeadingZero: false,
defaultTime: '0:00'
};
/*
* Get the schedule range start in unix timestamp form (in seconds).
* defaults to NOW if nothing is selected.
*
* @param String sDatePickerId
*
* @param String sTimePickerId
*
* @return Number iTime
*/
function fnGetTimestamp(sDatePickerId, sTimePickerId) {
var date,
time,
iTime,
iServerOffset,
iClientOffset;
if ($(sDatePickerId).val() === "") {
return 0;
}
date = $(sDatePickerId).val();
time = $(sTimePickerId).val();
date = date.split("-");
time = time.split(":");
//0 based month in js.
oDate = new Date(date[0], date[1]-1, date[2], time[0], time[1]);
iTime = oDate.getTime(); //value is in millisec.
iTime = Math.round(iTime / 1000);
iServerOffset = serverTimezoneOffset;
iClientOffset = oDate.getTimezoneOffset() * -60;//function returns minutes
//adjust for the fact the the Date object is in client time.
iTime = iTime + iClientOffset + iServerOffset;
return iTime;
}
/*
* Returns an object containing a unix timestamp in seconds for the start/end range
*
* @return Object {"start", "end", "range"}
*/
function fnGetScheduleRange() {
var iStart,
iEnd,
iRange,
DEFAULT_RANGE = 60*60*24;
iStart = fnGetTimestamp("#sb_date_start", "#sb_time_start");
iEnd = fnGetTimestamp("#sb_date_end", "#sb_time_end");
iRange = iEnd - iStart;
if (iRange === 0 || iEnd < iStart) {
iEnd = iStart + DEFAULT_RANGE;
iRange = DEFAULT_RANGE;
}
return {
start: iStart,
end: iEnd,
range: iRange
};
}
fnServerData = function ( sSource, aoData, fnCallback ) { fnServerData = function ( sSource, aoData, fnCallback ) {
aoData.push( { name: "format", value: "json"} ); aoData.push( { name: "format", value: "json"} );
@ -173,428 +69,435 @@ $(document).ready(function() {
} ); } );
}; };
oRange = fnGetScheduleRange(); mod.fnServerData = fnServerData;
fnServerData.start = oRange.start;
fnServerData.end = oRange.end;
fnRemoveSelectedItems = function() {
var oTT = TableTools.fnGetInstance('show_builder_table'),
aData = oTT.fnGetSelectedData(),
i,
length,
temp,
aItems = [];
for (i=0, length = aData.length; i < length; i++) { mod.builderDataTable = function() {
temp = aData[i]; var tableDiv = $('#show_builder_table'),
aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp}); oTable,
} fnRemoveSelectedItems;
AIRTIME.showbuilder.fnRemove(aItems);
};
oTable = tableDiv.dataTable( {
"aoColumns": [
/* checkbox */ {"mDataProp": "allowed", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"},
/* starts */{"mDataProp": "starts", "sTitle": "Start"},
/* ends */{"mDataProp": "ends", "sTitle": "End"},
/* runtime */{"mDataProp": "runtime", "sTitle": "Duration", "sClass": "library_length"},
/* title */{"mDataProp": "title", "sTitle": "Title"},
/* creator */{"mDataProp": "creator", "sTitle": "Creator"},
/* album */{"mDataProp": "album", "sTitle": "Album"},
/* cue in */{"mDataProp": "cuein", "sTitle": "Cue In", "bVisible": false},
/* cue out */{"mDataProp": "cueout", "sTitle": "Cue Out", "bVisible": false},
/* fade in */{"mDataProp": "fadein", "sTitle": "Fade In", "bVisible": false},
/* fade out */{"mDataProp": "fadeout", "sTitle": "Fade Out", "bVisible": false}
],
"bJQueryUI": true,
"bSort": false,
"bFilter": false,
"bProcessing": true,
"bServerSide": true,
"bInfo": false,
"bAutoWidth": false,
"bStateSave": true,
"fnStateSaveParams": function (oSettings, oData) {
//remove oData components we don't want to save.
delete oData.oSearch;
delete oData.aoSearchCols;
},
"fnStateSave": function (oSettings, oData) {
$.ajax({
url: "/usersettings/set-timeline-datatable",
type: "POST",
data: {settings : oData, format: "json"},
dataType: "json",
success: function(){},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
},
"fnStateLoad": function (oSettings) {
var o;
$.ajax({ fnRemoveSelectedItems = function() {
url: "/usersettings/get-timeline-datatable", var oTT = TableTools.fnGetInstance('show_builder_table'),
type: "GET", aData = oTT.fnGetSelectedData(),
data: {format: "json"}, i,
dataType: "json",
async: false,
success: function(json){
o = json.settings;
},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
return o;
},
"fnStateLoadParams": function (oSettings, oData) {
var i,
length, length,
a = oData.abVisCols; temp,
aItems = [];
//putting serialized data back into the correct js type to make for (i=0, length = aData.length; i < length; i++) {
//sure everything works properly. temp = aData[i];
for (i = 0, length = a.length; i < length; i++) { aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp});
a[i] = (a[i] === "true") ? true : false; }
}
AIRTIME.showbuilder.fnRemove(aItems);
};
oTable = tableDiv.dataTable( {
"aoColumns": [
/* checkbox */ {"mDataProp": "allowed", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"},
/* starts */{"mDataProp": "starts", "sTitle": "Start"},
/* ends */{"mDataProp": "ends", "sTitle": "End"},
/* runtime */{"mDataProp": "runtime", "sTitle": "Duration", "sClass": "library_length"},
/* title */{"mDataProp": "title", "sTitle": "Title"},
/* creator */{"mDataProp": "creator", "sTitle": "Creator"},
/* album */{"mDataProp": "album", "sTitle": "Album"},
/* cue in */{"mDataProp": "cuein", "sTitle": "Cue In", "bVisible": false},
/* cue out */{"mDataProp": "cueout", "sTitle": "Cue Out", "bVisible": false},
/* fade in */{"mDataProp": "fadein", "sTitle": "Fade In", "bVisible": false},
/* fade out */{"mDataProp": "fadeout", "sTitle": "Fade Out", "bVisible": false}
],
a = oData.ColReorder; "bJQueryUI": true,
for (i = 0, length = a.length; i < length; i++) { "bSort": false,
a[i] = parseInt(a[i], 10); "bFilter": false,
} "bProcessing": true,
"bServerSide": true,
oData.iCreate = parseInt(oData.iCreate, 10); "bInfo": false,
}, "bAutoWidth": false,
"fnServerData": fnServerData,
"fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
var i,
sSeparatorHTML,
fnPrepareSeparatorRow,
node,
cl="";
//save some info for reordering purposes. "bStateSave": true,
$(nRow).data({"aData": aData}); "fnStateSaveParams": function (oSettings, oData) {
//remove oData components we don't want to save.
delete oData.oSearch;
delete oData.aoSearchCols;
},
"fnStateSave": function (oSettings, oData) {
$.ajax({
url: "/usersettings/set-timeline-datatable",
type: "POST",
data: {settings : oData, format: "json"},
dataType: "json",
success: function(){},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
},
"fnStateLoad": function (oSettings) {
var o;
$.ajax({
url: "/usersettings/get-timeline-datatable",
type: "GET",
data: {format: "json"},
dataType: "json",
async: false,
success: function(json){
o = json.settings;
},
error: function (jqXHR, textStatus, errorThrown) {
var x;
}
});
return o;
},
"fnStateLoadParams": function (oSettings, oData) {
var i,
length,
a = oData.abVisCols;
if (aData.allowed !== true) { //putting serialized data back into the correct js type to make
$(nRow).addClass("sb-not-allowed"); //sure everything works properly.
} for (i = 0, length = a.length; i < length; i++) {
a[i] = (a[i] === "true") ? true : false;
//status used to colour tracks. }
if (aData.status === 1) {
$(nRow).addClass("sb-boundry"); a = oData.ColReorder;
} for (i = 0, length = a.length; i < length; i++) {
else if (aData.status === 2) { a[i] = parseInt(a[i], 10);
$(nRow).addClass("sb-over"); }
}
oData.iCreate = parseInt(oData.iCreate, 10);
fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) { },
"fnServerData": AIRTIME.showbuilder.fnServerData,
"fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
var i,
sSeparatorHTML,
fnPrepareSeparatorRow,
node,
cl="";
node = nRow.children[iNodeIndex]; //save some info for reordering purposes.
node.innerHTML = sRowContent; $(nRow).data({"aData": aData});
node.setAttribute('colspan',100);
for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) { if (aData.current === true) {
node = nRow.children[i]; $(nRow).addClass("sb-now-playing");
node.innerHTML = "";
node.setAttribute("style", "display : none");
} }
$(nRow).addClass(sClass); if (aData.allowed !== true) {
$(nRow).addClass("sb-not-allowed");
}
else {
$(nRow).addClass("sb-allowed");
}
//status used to colour tracks.
if (aData.status === 2) {
$(nRow).addClass("sb-boundry");
}
else if (aData.status === 0) {
$(nRow).addClass("sb-over");
}
fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) {
node = nRow.children[iNodeIndex];
node.innerHTML = sRowContent;
node.setAttribute('colspan',100);
for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) {
node = nRow.children[i];
node.innerHTML = "";
node.setAttribute("style", "display : none");
}
$(nRow).addClass(sClass);
};
if (aData.header === true) {
cl = 'sb-header';
sSeparatorHTML = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>';
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
}
else if (aData.footer === true) {
node = nRow.children[0];
cl = 'sb-footer';
//check the show's content status.
if (aData.runtime > 0) {
node.innerHTML = '<span class="ui-icon ui-icon-check"></span>';
cl = cl + ' ui-state-highlight';
}
else {
node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
cl = cl + ' ui-state-error';
}
sSeparatorHTML = '<span>'+aData.fRuntime+'</span>';
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
}
else if (aData.empty === true) {
sSeparatorHTML = '<span>Show Empty</span>';
cl = cl + " sb-empty odd";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
}
else if (aData.record === true) {
sSeparatorHTML = '<span>Recording From Line In</span>';
cl = cl + " sb-record odd";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
}
else {
node = nRow.children[0];
if (aData.allowed === true) {
node.innerHTML = '<input type="checkbox" name="'+aData.id+'"></input>';
}
else {
node.innerHTML = '';
}
}
},
"fnDrawCallback": function(oSettings, json) {
var wrapperDiv,
markerDiv,
td;
//create cursor arrows.
tableDiv.find("tr:not(:first, .sb-footer, .sb-empty, .sb-not-allowed)").each(function(i, el) {
td = $(el).find("td:first");
if (td.hasClass("dataTables_empty")) {
return false;
}
wrapperDiv = $("<div />", {
"class": "innerWrapper",
"css": {
"height": td.height()
}
});
markerDiv = $("<div />", {
"class": "marker"
});
td.append(markerDiv).wrapInner(wrapperDiv);
});
},
"fnHeaderCallback": function(nHead) {
$(nHead).find("input[type=checkbox]").attr("checked", false);
},
//remove any selected nodes before the draw.
"fnPreDrawCallback": function( oSettings ) {
var oTT = TableTools.fnGetInstance('show_builder_table');
oTT.fnSelectNone();
},
"oColVis": {
"aiExclude": [ 0, 1 ]
},
"oColReorder": {
"iFixedColumns": 2
},
"oTableTools": {
"sRowSelect": "multi",
"aButtons": [],
"fnPreRowSelect": function ( e ) {
var node = e.currentTarget;
//don't select separating rows, or shows without privileges.
if ($(node).hasClass("sb-header")
|| $(node).hasClass("sb-footer")
|| $(node).hasClass("sb-empty")
|| $(node).hasClass("sb-not-allowed")) {
return false;
}
return true;
},
"fnRowSelected": function ( node ) {
//seems to happen if everything is selected
if ( node === null) {
oTable.find("input[type=checkbox]").attr("checked", true);
}
else {
$(node).find("input[type=checkbox]").attr("checked", true);
}
//checking to enable buttons
AIRTIME.button.enableButton("sb_delete");
},
"fnRowDeselected": function ( node ) {
var selected;
//seems to happen if everything is deselected
if ( node === null) {
tableDiv.find("input[type=checkbox]").attr("checked", false);
selected = [];
}
else {
$(node).find("input[type=checkbox]").attr("checked", false);
selected = tableDiv.find("input[type=checkbox]").filter(":checked");
}
//checking to disable buttons
if (selected.length === 0) {
AIRTIME.button.disableButton("sb_delete");
}
}
},
// R = ColReorderResize, C = ColVis, T = TableTools
"sDom": 'Rr<"H"CT>t<"F">',
"sAjaxDataProp": "schedule",
"sAjaxSource": "/showbuilder/builder-feed"
});
$('[name="sb_cb_all"]').click(function(){
var oTT = TableTools.fnGetInstance('show_builder_table');
if ($(this).is(":checked")) {
var allowedNodes;
allowedNodes = oTable.find('tr:not(:first, .sb-header, .sb-empty, .sb-footer, .sb-not-allowed)');
allowedNodes.each(function(i, el){
oTT.fnSelect(el);
});
}
else {
oTT.fnSelectNone();
}
});
var sortableConf = (function(){
var origTrs,
aItemData = [],
oPrevData,
fnAdd,
fnMove,
fnReceive,
fnUpdate,
i,
html;
fnAdd = function() {
var aMediaIds = [],
aSchedIds = [];
for(i = 0; i < aItemData.length; i++) {
aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype});
}
aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
}; };
if (aData.header === true) { fnMove = function() {
cl = 'sb-header'; var aSelect = [],
aAfter = [];
aSelect.push({"id": aItemData[0].id, "instance": aItemData[0].instance, "timestamp": aItemData[0].timestamp});
aAfter.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
AIRTIME.showbuilder.fnMove(aSelect, aAfter);
};
fnReceive = function(event, ui) {
var aItems = [],
oLibTT = TableTools.fnGetInstance('library_display');
aItems = oLibTT.fnGetSelectedData();
sSeparatorHTML = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>'; //if nothing is checked select the dragged item.
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0); if (aItems.length === 0) {
} aItems.push(ui.item.data("aData"));
else if (aData.footer === true) { }
node = nRow.children[0];
cl = 'sb-footer'; origTrs = aItems;
html = ui.helper.html();
};
fnUpdate = function(event, ui) {
var prev = ui.item.prev();
//check the show's content status. //can't add items outside of shows.
if (aData.runtime > 0) { if (!prev.hasClass("sb-allowed")) {
node.innerHTML = '<span class="ui-icon ui-icon-check"></span>'; alert("Cannot schedule outside a show.");
cl = cl + ' ui-state-highlight'; ui.item.remove();
} return;
else {
node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
cl = cl + ' ui-state-error';
} }
aItemData = [];
oPrevData = prev.data("aData");
//item was dragged in
if (origTrs !== undefined) {
sSeparatorHTML = '<span>'+aData.fRuntime+'</span>'; $("#show_builder_table tr.ui-draggable")
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1); .empty()
} .after(html);
else if (aData.empty === true) {
aItemData = origTrs;
sSeparatorHTML = '<span>Show Empty</span>'; origTrs = undefined;
cl = cl + " sb-empty odd"; fnAdd();
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
}
else {
node = nRow.children[0];
if (aData.allowed === true) {
node.innerHTML = '<input type="checkbox" name="'+aData.id+'"></input>';
} }
//item was reordered.
else { else {
node.innerHTML = ''; aItemData.push(ui.item.data("aData"));
fnMove();
} }
};
return {
placeholder: "placeholder show-builder-placeholder ui-state-highlight",
forcePlaceholderSize: true,
items: 'tr:not(:first, :last, .sb-header, .sb-footer, .sb-not-allowed)',
receive: fnReceive,
update: fnUpdate
};
}());
tableDiv.sortable(sortableConf);
$("#show_builder .fg-toolbar")
.append('<div class="ColVis TableTools sb_delete"><button class="ui-button ui-state-default ui-state-disabled"><span>Delete</span></button></div>')
.click(fnRemoveSelectedItems);
//set things like a reference to the table.
AIRTIME.showbuilder.init(oTable);
//add event to cursors.
tableDiv.find("tbody").on("click", "div.marker", function(event){
var tr = $(this).parents("tr"),
cursorSelClass = "cursor-selected-row";
if (tr.hasClass(cursorSelClass)) {
tr.removeClass(cursorSelClass);
} }
},
"fnDrawCallback": function(oSettings, json) {
var wrapperDiv,
markerDiv,
td;
//create cursor arrows.
tableDiv.find("tr:not(:first, .sb-footer, .sb-empty, .sb-not-allowed)").each(function(i, el) {
td = $(el).find("td:first");
if (td.hasClass("dataTables_empty")) {
return false;
}
wrapperDiv = $("<div />", {
"class": "innerWrapper",
"css": {
"height": td.height()
}
});
markerDiv = $("<div />", {
"class": "marker"
});
td.append(markerDiv).wrapInner(wrapperDiv);
});
},
"fnHeaderCallback": function(nHead) {
$(nHead).find("input[type=checkbox]").attr("checked", false);
},
//remove any selected nodes before the draw.
"fnPreDrawCallback": function( oSettings ) {
var oTT = TableTools.fnGetInstance('show_builder_table');
oTT.fnSelectNone();
},
"oColVis": {
"aiExclude": [ 0, 1 ]
},
"oColReorder": {
"iFixedColumns": 2
},
"oTableTools": {
"sRowSelect": "multi",
"aButtons": [],
"fnPreRowSelect": function ( e ) {
var node = e.currentTarget;
//don't select separating rows, or shows without privileges.
if ($(node).hasClass("sb-header")
|| $(node).hasClass("sb-footer")
|| $(node).hasClass("sb-empty")
|| $(node).hasClass("sb-not-allowed")) {
return false;
}
return true;
},
"fnRowSelected": function ( node ) {
//seems to happen if everything is selected
if ( node === null) {
oTable.find("input[type=checkbox]").attr("checked", true);
}
else {
$(node).find("input[type=checkbox]").attr("checked", true);
}
},
"fnRowDeselected": function ( node ) {
//seems to happen if everything is deselected
if ( node === null) {
var oTable = $("#show_builder_table").dataTable();
oTable.find("input[type=checkbox]").attr("checked", false);
}
else {
$(node).find("input[type=checkbox]").attr("checked", false);
}
}
},
// R = ColReorderResize, C = ColVis, T = TableTools
"sDom": 'Rr<"H"CT>t<"F">',
"sAjaxDataProp": "schedule",
"sAjaxSource": "/showbuilder/builder-feed"
});
$('[name="sb_cb_all"]').click(function(){
var oTT = TableTools.fnGetInstance('show_builder_table');
if ($(this).is(":checked")) {
var allowedNodes;
allowedNodes = oTable.find('tr:not(:first, .sb-header, .sb-empty, .sb-footer, .sb-not-allowed)');
allowedNodes.each(function(i, el){
oTT.fnSelect(el);
});
}
else {
oTT.fnSelectNone();
}
});
$("#sb_date_start").datepicker(oBaseDatePickerSettings);
$("#sb_time_start").timepicker(oBaseTimePickerSettings);
$("#sb_date_end").datepicker(oBaseDatePickerSettings);
$("#sb_time_end").timepicker(oBaseTimePickerSettings);
$("#sb_submit").click(function(ev){
var fn,
oRange,
op;
oRange = fnGetScheduleRange();
fn = oTable.fnSettings().fnServerData;
fn.start = oRange.start;
fn.end = oRange.end;
op = $("div.sb-advanced-options");
if (op.is(":visible")) {
if (fn.ops === undefined) {
fn.ops = {};
}
fn.ops.showFilter = op.find("#sb_show_filter").val();
fn.ops.myShows = op.find("#sb_my_shows").is(":checked") ? 1 : 0;
}
oTable.fnDraw();
});
var sortableConf = (function(){
var origTrs,
aItemData = [],
oPrevData,
fnAdd,
fnMove,
fnReceive,
fnUpdate,
i,
html;
fnAdd = function() {
var aMediaIds = [],
aSchedIds = [],
oLibTT = TableTools.fnGetInstance('library_display');
for(i=0; i < aItemData.length; i++) {
aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype});
}
aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
};
fnMove = function() {
var aSelect = [],
aAfter = [];
aSelect.push({"id": aItemData[0].id, "instance": aItemData[0].instance, "timestamp": aItemData[0].timestamp});
aAfter.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
AIRTIME.showbuilder.fnMove(aSelect, aAfter);
};
fnReceive = function(event, ui) {
var selected = $('#library_display tr:not(:first) input:checked').parents('tr'),
aItems = [];
//if nothing is checked select the dragged item.
if (selected.length === 0) {
selected = ui.item;
}
selected.each(function(i, el) {
aItems.push($(el).data("aData"));
});
origTrs = aItems;
html = ui.helper.html();
};
fnUpdate = function(event, ui) {
var prev = ui.item.prev();
//can't add items outside of shows.
if (prev.hasClass("sb-footer")) {
alert("Cannot add an item outside a show.");
ui.item.remove();
return;
}
aItemData = [];
oPrevData = prev.data("aData");
//item was dragged in
if (origTrs !== undefined) {
$("#show_builder_table tr.ui-draggable")
.empty()
.after(html);
aItemData = origTrs;
origTrs = undefined;
fnAdd();
}
//item was reordered.
else { else {
aItemData.push(ui.item.data("aData")); tr.addClass(cursorSelClass);
fnMove();
} }
};
//check if add button can still be enabled.
AIRTIME.library.events.enableAddButtonCheck();
return false;
});
return { };
placeholder: "placeholder show-builder-placeholder ui-state-highlight",
forcePlaceholderSize: true,
items: 'tr:not(:first, :last, .sb-header, .sb-footer, .sb-not-allowed)',
receive: fnReceive,
update: fnUpdate
};
}());
tableDiv.sortable(sortableConf); mod.init = function(oTable) {
oSchedTable = oTable;
};
$("#show_builder .fg-toolbar") return AIRTIME;
.append('<div class="ColVis TableTools"><button class="ui-button ui-state-default"><span>Delete</span></button></div>')
.click(fnRemoveSelectedItems);
//set things like a reference to the table. }(AIRTIME || {}));
AIRTIME.showbuilder.init(oTable);
//add event to cursors.
tableDiv.find("tbody").on("click", "div.marker", function(event){
var tr = $(this).parents("tr");
if (tr.hasClass("cursor-selected-row")) {
tr.removeClass("cursor-selected-row");
}
else {
tr.addClass("cursor-selected-row");
}
return false;
});
});

View File

@ -0,0 +1,128 @@
$(document).ready(function(){
var oBaseDatePickerSettings,
oBaseTimePickerSettings,
oRange;
oBaseDatePickerSettings = {
dateFormat: 'yy-mm-dd',
onSelect: function(sDate, oDatePicker) {
var oDate,
dInput;
dInput = $(this);
oDate = dInput.datepicker( "setDate", sDate );
}
};
oBaseTimePickerSettings = {
showPeriodLabels: false,
showCloseButton: true,
showLeadingZero: false,
defaultTime: '0:00'
};
/*
* Get the schedule range start in unix timestamp form (in seconds).
* defaults to NOW if nothing is selected.
*
* @param String sDatePickerId
*
* @param String sTimePickerId
*
* @return Number iTime
*/
function fnGetTimestamp(sDatePickerId, sTimePickerId) {
var date,
time,
iTime,
iServerOffset,
iClientOffset;
if ($(sDatePickerId).val() === "") {
return 0;
}
date = $(sDatePickerId).val();
time = $(sTimePickerId).val();
date = date.split("-");
time = time.split(":");
//0 based month in js.
oDate = new Date(date[0], date[1]-1, date[2], time[0], time[1]);
iTime = oDate.getTime(); //value is in millisec.
iTime = Math.round(iTime / 1000);
iServerOffset = serverTimezoneOffset;
iClientOffset = oDate.getTimezoneOffset() * -60;//function returns minutes
//adjust for the fact the the Date object is in client time.
iTime = iTime + iClientOffset + iServerOffset;
return iTime;
}
/*
* Returns an object containing a unix timestamp in seconds for the start/end range
*
* @return Object {"start", "end", "range"}
*/
function fnGetScheduleRange() {
var iStart,
iEnd,
iRange,
DEFAULT_RANGE = 60*60*24;
iStart = fnGetTimestamp("#sb_date_start", "#sb_time_start");
iEnd = fnGetTimestamp("#sb_date_end", "#sb_time_end");
iRange = iEnd - iStart;
if (iRange === 0 || iEnd < iStart) {
iEnd = iStart + DEFAULT_RANGE;
iRange = DEFAULT_RANGE;
}
return {
start: iStart,
end: iEnd,
range: iRange
};
}
$("#sb_date_start").datepicker(oBaseDatePickerSettings);
$("#sb_time_start").timepicker(oBaseTimePickerSettings);
$("#sb_date_end").datepicker(oBaseDatePickerSettings);
$("#sb_time_end").timepicker(oBaseTimePickerSettings);
$("#sb_submit").click(function(ev){
var fn,
oRange,
op,
oTable = $('#show_builder_table').dataTable();
oRange = fnGetScheduleRange();
fn = oTable.fnSettings().fnServerData;
fn.start = oRange.start;
fn.end = oRange.end;
op = $("div.sb-advanced-options");
if (op.is(":visible")) {
if (fn.ops === undefined) {
fn.ops = {};
}
fn.ops.showFilter = op.find("#sb_show_filter").val();
fn.ops.myShows = op.find("#sb_my_shows").is(":checked") ? 1 : 0;
}
oTable.fnDraw();
});
oRange = fnGetScheduleRange();
AIRTIME.showbuilder.fnServerData.start = oRange.start;
AIRTIME.showbuilder.fnServerData.end = oRange.end;
AIRTIME.showbuilder.builderDataTable();
});

View File

@ -0,0 +1,163 @@
<?PHP
/*
The purpose of this script is to take a file from cc_files table, and insert it into
the schedule table. DB columns at the time of writing are
starts | ends | file_id | clip_length | fade_in | fade_out | cue_in | cue_out | media_item_played | instance_id
an example of data in this row is:
"9" | "2012-02-29 17:10:00" | "2012-02-29 17:15:05.037166" | 1 | "00:05:05.037166" | "00:00:00" | "00:00:00" | "00:00:00" | "00:05:05.037166" | FALSE | 5
*/
function query($conn, $query){
$result = pg_query($conn, $query);
if (!$result) {
echo "Error executing query $query.\n";
exit(1);
}
return $result;
}
function getFileFromCcFiles($conn){
$query = "SELECT * from cc_files LIMIT 1";
$result = query($conn, $query);
$file = null;
while ($row = pg_fetch_array($result)) {
$file = $row;
}
if (is_null($file)){
echo "Library is empty. Could not choose random file.";
exit(1);
}
return $file;
}
function insertIntoCcShow($conn){
/* Step 1:
* Create a show
* */
$query = "INSERT INTO cc_show (name, url, genre, description, color, background_color) VALUES ('test', '', '', '', '', '')";
echo $query.PHP_EOL;
$result = query($conn, $query);
$query = "SELECT currval('cc_show_id_seq');";
$result = pg_query($conn, $query);
if (!$result) {
echo "Error executing query $query.\n";
exit(1);
}
while ($row = pg_fetch_array($result)) {
$show_id = $row["currval"];
}
return $show_id;
}
function insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $file){
/* Step 2:
* Create a show instance.
* Column values:
* starts | ends | show_id | record | rebroadcast | instance_id | file_id | time_filled | last_scheduled | modified_instance
* */
$nowDateTime = new DateTime("now", new DateTimeZone("UTC"));
$now = $nowDateTime->format("Y-m-d H:i:s");
$columns = "(starts, ends, show_id, record, rebroadcast, instance_id, file_id, time_filled, last_scheduled, modified_instance)";
$values = "('$starts', '$ends', $show_id, 0, 0, NULL, NULL, '$file[length]', '$now', 'f')";
$query = "INSERT INTO cc_show_instances $columns values $values ";
echo $query.PHP_EOL;
$result = query($conn, $query);
$query = "SELECT currval('cc_show_instances_id_seq');";
$result = pg_query($conn, $query);
if (!$result) {
echo "Error executing query $query.\n";
exit(1);
}
while ($row = pg_fetch_array($result)) {
$show_instance_id = $row["currval"];
}
return $show_instance_id;
}
/*
* id | starts | ends | file_id | clip_length| fade_in | fade_out | cue_in | cue_out | media_item_played | instance_id
* 1 | 2012-02-29 23:25:00 | 2012-02-29 23:30:05.037166 | 1 | 00:05:05.037166 | 00:00:00 | 00:00:00 | 00:00:00 | 00:05:05.037166 | f | 5
*/
function insertIntoCcSchedule($conn, $file, $show_instance_id, $starts, $ends){
$columns = "(starts, ends, file_id, clip_length, fade_in, fade_out, cue_in, cue_out, media_item_played, instance_id)";
$values = "('$starts', '$ends', $file[id], '$file[length]', '00:00:00', '00:00:00', '00:00:00', '$file[length]', 'f', $show_instance_id)";
$query = "INSERT INTO cc_schedule $columns VALUES $values";
echo $query.PHP_EOL;
$result = query($conn, $query);
}
function rabbitMqNotify(){
$ini_file = parse_ini_file("/etc/airtime/airtime.conf", true);
$url = "http://localhost/api/rabbitmq-do-push/format/json/api_key/".$ini_file["general"]["api_key"];
echo "Contacting $url".PHP_EOL;
$ch = curl_init($url);
curl_exec($ch);
curl_close($ch);
}
$conn = pg_connect("host=localhost port=5432 dbname=airtime user=airtime password=airtime");
if (!$conn) {
echo "Couldn't connect to Airtime DB.\n";
exit(1);
}
if (count($argv) > 1){
if ($argv[1] == "--clean"){
$tables = array("cc_schedule", "cc_show_instances", "cc_show");
foreach($tables as $table){
$query = "DELETE FROM $table";
echo $query.PHP_EOL;
query($conn, $query);
}
rabbitMqNotify();
exit(0);
} else {
$str = <<<EOD
This script schedules a file to play 30 seconds in the future. It
modifies the database tables cc_schedule, cc_show_instances and cc_show.
You can clean up these tables using the --clean option.
EOD;
echo $str.PHP_EOL;
exit(0);
}
}
$startDateTime = new DateTime("now + 30sec", new DateTimeZone("UTC"));
$endDateTime = new DateTime("now + 1min 30sec", new DateTimeZone("UTC"));
$starts = $startDateTime->format("Y-m-d H:i:s");
$ends = $endDateTime->format("Y-m-d H:i:s");
$file = getFileFromCcFiles($conn);
$show_id = insertIntoCcShow($conn);
$show_instance_id = insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $file);
insertIntoCcSchedule($conn, $file, $show_instance_id, $starts, $ends);
rabbitMqNotify();
echo PHP_EOL."Show scheduled for $starts (UTC)".PHP_EOL;

39
dev_tools/toggle-pypo-debug.sh Executable file
View File

@ -0,0 +1,39 @@
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root." 1>&2
exit 1
fi
usage () {
echo "Use --enable <user> or --disable flag. Enable is to set up environment"
echo "for specified user. --disable is to reset it back to pypo user"
}
if [ "$1" = "--enable" ]; then
/etc/init.d/airtime-playout stop
/etc/init.d/airtime-playout start-liquidsoap
user=$2
echo "Changing ownership to user $1"
chown -Rv $user:$user /var/log/airtime/pypo
chown -v $user:$user /etc/airtime/pypo.cfg
chown -Rv $user:$user /var/tmp/airtime/pypo/
chmod -v a+r /etc/airtime/api_client.cfg
elif [ "$1" = "--disable" ]; then
user="pypo"
echo "Changing ownership to user $1"
chown -Rv $user:$user /var/log/airtime/pypo
chown -v $user:$user /etc/airtime/pypo.cfg
chown -Rv $user:$user /var/tmp/airtime/pypo/
chmod -v a+r /etc/airtime/api_client.cfg
/etc/init.d/airtime-playout stop-liquidsoap
/etc/init.d/airtime-playout start
else
usage
fi

View File

@ -81,14 +81,6 @@ class ApiClientInterface:
def get_media(self, src, dst): def get_media(self, src, dst):
pass pass
# Implementation: optional
#
# Called from: push loop
#
# Tell server that the scheduled *playlist* has started.
def notify_scheduled_item_start_playing(self, pkey, schedule):
pass
# Implementation: optional # Implementation: optional
# You dont actually have to implement this function for the liquidsoap playout to work. # You dont actually have to implement this function for the liquidsoap playout to work.
# #
@ -261,15 +253,15 @@ class AirTimeApiClient(ApiClientInterface):
export_url = export_url.replace('%%api_key%%', self.config["api_key"]) export_url = export_url.replace('%%api_key%%', self.config["api_key"])
response = "" response = ""
status = 0
try: try:
response_json = self.get_response_from_server(export_url) response_json = self.get_response_from_server(export_url)
response = json.loads(response_json) response = json.loads(response_json)
status = response['check'] success = True
except Exception, e: except Exception, e:
logger.error(e) logger.error(e)
success = False
return status, response return success, response
def get_media(self, uri, dst): def get_media(self, uri, dst):
@ -285,32 +277,6 @@ class AirTimeApiClient(ApiClientInterface):
except Exception, e: except Exception, e:
logger.error("%s", e) logger.error("%s", e)
"""
Tell server that the scheduled *playlist* has started.
"""
def notify_scheduled_item_start_playing(self, pkey, schedule):
logger = self.logger
playlist = schedule[pkey]
schedule_id = playlist["schedule_id"]
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_item_url"])
url = url.replace("%%schedule_id%%", str(schedule_id))
logger.debug(url)
url = url.replace("%%api_key%%", self.config["api_key"])
try:
response = urllib.urlopen(url)
response = json.loads(response.read())
logger.info("API-Status %s", response['status'])
logger.info("API-Message %s", response['message'])
except Exception, e:
logger.error("Unable to connect - %s", e)
return response
""" """
This is a callback from liquidsoap, we use this to notify about the This is a callback from liquidsoap, we use this to notify about the
currently playing *song*. We get passed a JSON string which we handed to currently playing *song*. We get passed a JSON string which we handed to

View File

@ -1,8 +1,8 @@
[loggers] [loggers]
keys=root,fetch,push,recorder keys=root,fetch,push,recorder,message_h
[handlers] [handlers]
keys=pypo,recorder keys=pypo,recorder,message_h
[formatters] [formatters]
keys=simpleFormatter keys=simpleFormatter
@ -29,6 +29,12 @@ handlers=recorder
qualname=recorder qualname=recorder
propagate=0 propagate=0
[logger_message_h]
level=DEBUG
handlers=message_h
qualname=message_h
propagate=0
[handler_pypo] [handler_pypo]
class=logging.handlers.RotatingFileHandler class=logging.handlers.RotatingFileHandler
level=DEBUG level=DEBUG
@ -41,6 +47,12 @@ level=DEBUG
formatter=simpleFormatter formatter=simpleFormatter
args=("/var/log/airtime/pypo/show-recorder.log", 'a', 1000000, 5,) args=("/var/log/airtime/pypo/show-recorder.log", 'a', 1000000, 5,)
[handler_message_h]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=simpleFormatter
args=("/var/log/airtime/pypo/message-handler.log", 'a', 1000000, 5,)
[formatter_simpleFormatter] [formatter_simpleFormatter]
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
datefmt= datefmt=

View File

@ -16,6 +16,7 @@ from Queue import Queue
from pypopush import PypoPush from pypopush import PypoPush
from pypofetch import PypoFetch from pypofetch import PypoFetch
from recorder import Recorder from recorder import Recorder
from pypomessagehandler import PypoMessageHandler
from configobj import ConfigObj from configobj import ConfigObj
@ -55,23 +56,16 @@ except Exception, e:
class Global: class Global:
def __init__(self): def __init__(self):
self.api_client = api_client.api_client_factory(config) self.api_client = api_client.api_client_factory(config)
self.set_export_source('scheduler')
def selfcheck(self): def selfcheck(self):
self.api_client = api_client.api_client_factory(config) self.api_client = api_client.api_client_factory(config)
return self.api_client.is_server_compatible() return self.api_client.is_server_compatible()
def set_export_source(self, export_source):
self.export_source = export_source
self.cache_dir = config["cache_dir"] + self.export_source + '/'
self.schedule_file = self.cache_dir + 'schedule.pickle'
self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle"
def test_api(self): def test_api(self):
self.api_client.test() self.api_client.test()
""" """
def check_schedule(self, export_source): def check_schedule(self):
logger = logging.getLogger() logger = logging.getLogger()
try: try:
@ -127,11 +121,19 @@ if __name__ == '__main__':
api_client = api_client.api_client_factory(config) api_client = api_client.api_client_factory(config)
api_client.register_component("pypo") api_client.register_component("pypo")
q = Queue() pypoFetch_q = Queue()
recorder_q = Queue() recorder_q = Queue()
pypoPush_q = Queue()
pp = PypoPush(q)
pmh = PypoMessageHandler(pypoFetch_q, recorder_q)
pmh.daemon = True
pmh.start()
pf = PypoFetch(pypoFetch_q, pypoPush_q)
pf.daemon = True
pf.start()
pp = PypoPush(pypoPush_q)
pp.daemon = True pp.daemon = True
pp.start() pp.start()
@ -139,12 +141,11 @@ if __name__ == '__main__':
recorder.daemon = True recorder.daemon = True
recorder.start() recorder.start()
pf = PypoFetch(q, recorder_q) pmh.join()
pf.daemon = True pp.join()
pf.start()
#pp.join()
pf.join() pf.join()
recorder.join()
logger.info("pypo fetch exit") logger.info("pypo fetch exit")
sys.exit() sys.exit()
""" """

View File

@ -34,7 +34,7 @@ import json
from configobj import ConfigObj from configobj import ConfigObj
# custom imports # custom imports
from util import * #from util import *
from api_clients import * from api_clients import *
# Set up command-line options # Set up command-line options

View File

@ -16,12 +16,6 @@ from datetime import datetime
from datetime import timedelta from datetime import timedelta
import filecmp import filecmp
# For RabbitMQ
from kombu.connection import BrokerConnection
from kombu.messaging import Exchange, Queue, Consumer, Producer
from kombu.exceptions import MessageStateError
from kombu.simple import SimpleQueue
from api_clients import api_client from api_clients import api_client
from configobj import ConfigObj from configobj import ConfigObj
@ -42,30 +36,31 @@ except Exception, e:
sys.exit() sys.exit()
class PypoFetch(Thread): class PypoFetch(Thread):
def __init__(self, q, recorder_q): def __init__(self, pypoFetch_q, pypoPush_q):
Thread.__init__(self) Thread.__init__(self)
self.api_client = api_client.api_client_factory(config) self.api_client = api_client.api_client_factory(config)
self.set_export_source('scheduler') self.fetch_queue = pypoFetch_q
self.queue = q self.push_queue = pypoPush_q
self.recorder_queue = recorder_q
self.schedule_data = [] self.logger = logging.getLogger();
logger = logging.getLogger('fetch')
logger.info("PypoFetch: init complete") self.cache_dir = os.path.join(config["cache_dir"], "scheduler")
self.logger.debug("Cache dir %s", self.cache_dir)
def init_rabbit_mq(self):
logger = logging.getLogger('fetch')
logger.info("Initializing RabbitMQ stuff")
try: try:
schedule_exchange = Exchange("airtime-pypo", "direct", durable=True, auto_delete=True) if not os.path.isdir(dir):
schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo") """
connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], config["rabbitmq_vhost"]) We get here if path does not exist, or path does exist but
channel = connection.channel() is a file. We are not handling the second case, but don't
self.simple_queue = SimpleQueue(channel, schedule_queue) think we actually care about handling it.
"""
self.logger.debug("Cache dir does not exist. Creating...")
os.makedirs(dir)
except Exception, e: except Exception, e:
logger.error(e) pass
return False
self.schedule_data = []
return True self.logger.info("PypoFetch: init complete")
""" """
Handle a message from RabbitMQ, put it into our yucky global var. Handle a message from RabbitMQ, put it into our yucky global var.
@ -73,55 +68,51 @@ class PypoFetch(Thread):
""" """
def handle_message(self, message): def handle_message(self, message):
try: try:
logger = logging.getLogger('fetch') self.logger.info("Received event from Pypo Message Handler: %s" % message)
logger.info("Received event from RabbitMQ: %s" % message)
m = json.loads(message) m = json.loads(message)
command = m['event_type'] command = m['event_type']
logger.info("Handling command: " + command) self.logger.info("Handling command: " + command)
if command == 'update_schedule': if command == 'update_schedule':
self.schedule_data = m['schedule'] self.schedule_data = m['schedule']
self.process_schedule(self.schedule_data, "scheduler", False) self.process_schedule(self.schedule_data, False)
elif command == 'update_stream_setting': elif command == 'update_stream_setting':
logger.info("Updating stream setting...") self.logger.info("Updating stream setting...")
self.regenerateLiquidsoapConf(m['setting']) self.regenerateLiquidsoapConf(m['setting'])
elif command == 'update_stream_format': elif command == 'update_stream_format':
logger.info("Updating stream format...") self.logger.info("Updating stream format...")
self.update_liquidsoap_stream_format(m['stream_format']) self.update_liquidsoap_stream_format(m['stream_format'])
elif command == 'update_station_name': elif command == 'update_station_name':
logger.info("Updating station name...") self.logger.info("Updating station name...")
self.update_liquidsoap_station_name(m['station_name']) self.update_liquidsoap_station_name(m['station_name'])
elif command == 'cancel_current_show': elif command == 'cancel_current_show':
logger.info("Cancel current show command received...") self.logger.info("Cancel current show command received...")
self.stop_current_show() self.stop_current_show()
elif command == 'update_recorder_schedule':
temp = m
if temp is not None:
self.parse_shows(temp)
elif command == 'cancel_recording':
self.recorder_queue.put('cancel_recording')
except Exception, e: except Exception, e:
logger.error("Exception in handling RabbitMQ message: %s", e) import traceback
top = traceback.format_exc()
self.logger.error('Exception: %s', e)
self.logger.error("traceback: %s", top)
self.logger.error("Exception in handling Message Handler message: %s", e)
def stop_current_show(self): def stop_current_show(self):
logger = logging.getLogger('fetch') self.logger.debug('Notifying Liquidsoap to stop playback.')
logger.debug('Notifying Liquidsoap to stop playback.')
try: try:
tn = telnetlib.Telnet(LS_HOST, LS_PORT) tn = telnetlib.Telnet(LS_HOST, LS_PORT)
tn.write('source.skip\n') tn.write('source.skip\n')
tn.write('exit\n') tn.write('exit\n')
tn.read_all() tn.read_all()
except Exception, e: except Exception, e:
logger.debug(e) self.logger.debug(e)
logger.debug('Could not connect to liquidsoap') self.logger.debug('Could not connect to liquidsoap')
def regenerateLiquidsoapConf(self, setting): def regenerateLiquidsoapConf(self, setting):
logger = logging.getLogger('fetch')
existing = {} existing = {}
# create a temp file # create a temp file
fh = open('/etc/airtime/liquidsoap.cfg', 'r') fh = open('/etc/airtime/liquidsoap.cfg', 'r')
logger.info("Reading existing config...") self.logger.info("Reading existing config...")
# read existing conf file and build dict # read existing conf file and build dict
while 1: while 1:
line = fh.readline() line = fh.readline()
@ -151,7 +142,7 @@ class PypoFetch(Thread):
#restart flag #restart flag
restart = False restart = False
logger.info("Looking for changes...") self.logger.info("Looking for changes...")
# look for changes # look for changes
for s in setting: for s in setting:
if "output_sound_device" in s[u'keyname'] or "icecast_vorbis_metadata" in s[u'keyname']: if "output_sound_device" in s[u'keyname'] or "icecast_vorbis_metadata" in s[u'keyname']:
@ -159,13 +150,13 @@ class PypoFetch(Thread):
state_change_restart[stream] = False state_change_restart[stream] = False
# This is the case where restart is required no matter what # This is the case where restart is required no matter what
if (existing[s[u'keyname']] != s[u'value']): if (existing[s[u'keyname']] != s[u'value']):
logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
restart = True; restart = True;
else: else:
stream, dump = s[u'keyname'].split('_',1) stream, dump = s[u'keyname'].split('_',1)
if "_output" in s[u'keyname']: if "_output" in s[u'keyname']:
if (existing[s[u'keyname']] != s[u'value']): if (existing[s[u'keyname']] != s[u'value']):
logger.info("'Need-to-restart' state detected for %s...", s[u'keyname']) self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
restart = True; restart = True;
state_change_restart[stream] = True state_change_restart[stream] = True
elif ( s[u'value'] != 'disabled'): elif ( s[u'value'] != 'disabled'):
@ -177,22 +168,22 @@ class PypoFetch(Thread):
if stream not in change: if stream not in change:
change[stream] = False change[stream] = False
if not (s[u'value'] == existing[s[u'keyname']]): if not (s[u'value'] == existing[s[u'keyname']]):
logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value']) self.logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value'])
change[stream] = True change[stream] = True
# set flag change for sound_device alway True # set flag change for sound_device alway True
logger.info("Change:%s, State_Change:%s...", change, state_change_restart) self.logger.info("Change:%s, State_Change:%s...", change, state_change_restart)
for k, v in state_change_restart.items(): for k, v in state_change_restart.items():
if k == "sound_device" and v: if k == "sound_device" and v:
restart = True restart = True
elif v and change[k]: elif v and change[k]:
logger.info("'Need-to-restart' state detected for %s...", k) self.logger.info("'Need-to-restart' state detected for %s...", k)
restart = True restart = True
# rewrite # rewrite
if restart: if restart:
fh = open('/etc/airtime/liquidsoap.cfg', 'w') fh = open('/etc/airtime/liquidsoap.cfg', 'w')
logger.info("Rewriting liquidsoap.cfg...") self.logger.info("Rewriting liquidsoap.cfg...")
fh.write("################################################\n") fh.write("################################################\n")
fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n") fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n")
fh.write("################################################\n") fh.write("################################################\n")
@ -214,17 +205,18 @@ class PypoFetch(Thread):
fh.close() fh.close()
# restarting pypo. # restarting pypo.
# we could just restart liquidsoap but it take more time somehow. # we could just restart liquidsoap but it take more time somehow.
logger.info("Restarting pypo...") self.logger.info("Restarting pypo...")
sys.exit(0) sys.exit(0)
else: else:
logger.info("No change detected in setting...") self.logger.info("No change detected in setting...")
self.update_liquidsoap_connection_status() self.update_liquidsoap_connection_status()
"""
def update_liquidsoap_connection_status(self):
"""
updates the status of liquidsoap connection to the streaming server updates the status of liquidsoap connection to the streaming server
This fucntion updates the bootup time variable in liquidsoap script This fucntion updates the bootup time variable in liquidsoap script
""" """
def update_liquidsoap_connection_status(self): def update_liquidsoap_connection_status(self):
logger = logging.getLogger('fetch')
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
@ -242,7 +234,7 @@ class PypoFetch(Thread):
# streamin info is in the form of: # streamin info is in the form of:
# eg. s1:true,2:true,3:false # eg. s1:true,2:true,3:false
streams = stream_info.split(",") streams = stream_info.split(",")
logger.info(streams) self.logger.info(streams)
fake_time = current_time + 1 fake_time = current_time + 1
for s in streams: for s in streams:
@ -252,46 +244,35 @@ 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 set_export_source(self, export_source):
logger = logging.getLogger('fetch')
self.export_source = export_source
self.cache_dir = config["cache_dir"] + self.export_source + '/'
logger.info("Creating cache directory at %s", self.cache_dir)
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!!!
try: try:
logger = logging.getLogger('fetch') self.logger.info(LS_HOST)
logger.info(LS_HOST) self.logger.info(LS_PORT)
logger.info(LS_PORT)
tn = telnetlib.Telnet(LS_HOST, LS_PORT) tn = telnetlib.Telnet(LS_HOST, LS_PORT)
command = ('vars.stream_metadata_type %s\n' % stream_format).encode('utf-8') command = ('vars.stream_metadata_type %s\n' % stream_format).encode('utf-8')
logger.info(command) self.logger.info(command)
tn.write(command) tn.write(command)
tn.write('exit\n') tn.write('exit\n')
tn.read_all() tn.read_all()
except Exception, e: except Exception, e:
logger.error("Exception %s", e) self.logger.error("Exception %s", e)
def update_liquidsoap_station_name(self, station_name): def update_liquidsoap_station_name(self, station_name):
# 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!!!
try: try:
logger = logging.getLogger('fetch') self.logger.info(LS_HOST)
logger.info(LS_HOST) self.logger.info(LS_PORT)
logger.info(LS_PORT)
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')
logger.info(command) self.logger.info(command)
tn.write(command) tn.write(command)
tn.write('exit\n') tn.write('exit\n')
tn.read_all() tn.read_all()
except Exception, e: except Exception, e:
logger.error("Exception %s", e) self.logger.error("Exception %s", e)
""" """
Process the schedule Process the schedule
@ -301,191 +282,157 @@ class PypoFetch(Thread):
to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss) to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
- runs the cleanup routine, to get rid of unused cached files - runs the cleanup routine, to get rid of unused cached files
""" """
def process_schedule(self, schedule_data, export_source, bootstrapping): def process_schedule(self, schedule_data, bootstrapping):
logger = logging.getLogger('fetch') self.logger.debug(schedule_data)
playlists = schedule_data["playlists"] media = schedule_data["media"]
# Download all the media and put playlists in liquidsoap "annotate" format # Download all the media and put playlists in liquidsoap "annotate" format
try: try:
liquidsoap_playlists = self.prepare_playlists(playlists, bootstrapping) media = self.prepare_media(media, bootstrapping)
except Exception, e: logger.error("%s", e) except Exception, e: self.logger.error("%s", e)
# Send the data to pypo-push # Send the data to pypo-push
scheduled_data = dict() self.logger.debug("Pushing to pypo-push: "+ str(media))
scheduled_data['liquidsoap_playlists'] = liquidsoap_playlists self.push_queue.put(media)
scheduled_data['schedule'] = playlists
self.queue.put(scheduled_data)
"""
TODO
# cleanup # cleanup
try: self.cleanup(self.export_source) try: self.cleanup()
except Exception, e: logger.error("%s", e) except Exception, e: self.logger.error("%s", e)
"""
def getDateTimeObj(self,time):
timeinfo = time.split(" ")
date = timeinfo[0].split("-")
time = timeinfo[1].split(":")
date = map(int, date)
time = map(int, time)
return datetime(date[0], date[1], date[2], time[0], time[1], time[2], 0, None)
def parse_shows(self, m):
logger = logging.getLogger('fetch')
logger.info("Parsing recording show schedules...")
shows_to_record = {}
shows = m['shows']
for show in shows:
show_starts = self.getDateTimeObj(show[u'starts'])
show_end = self.getDateTimeObj(show[u'ends'])
time_delta = show_end - show_starts
shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']]
self.recorder_queue.put(shows_to_record)
logger.info(shows_to_record)
"""
In this function every audio file is cut as necessary (cue_in/cue_out != 0)
and stored in a playlist folder.
file is e.g. 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3
"""
def prepare_playlists(self, playlists, bootstrapping):
logger = logging.getLogger('fetch')
liquidsoap_playlists = dict()
# Dont do anything if playlists is empty
if not playlists:
logger.debug("Schedule is empty.")
return liquidsoap_playlists
scheduleKeys = sorted(playlists.iterkeys())
def prepare_media(self, media, bootstrapping):
"""
Iterate through the list of media items in "media" and
download them.
"""
try: try:
for pkey in scheduleKeys: mediaKeys = sorted(media.iterkeys())
logger.info("Playlist starting at %s", pkey) for mkey in mediaKeys:
playlist = playlists[pkey] self.logger.debug("Media item starting at %s", mkey)
media_item = media[mkey]
if bootstrapping:
self.check_for_previous_crash(media_item)
# create playlist directory # create playlist directory
try: try:
os.mkdir(self.cache_dir + str(pkey)) """
Extract year, month, date from mkey
"""
y_m_d = mkey[0:10]
download_dir = os.path.join(self.cache_dir, y_m_d)
try:
os.makedirs(os.path.join(self.cache_dir, y_m_d))
except Exception, e:
pass
fileExt = os.path.splitext(media_item['uri'])[1]
dst = os.path.join(download_dir, media_item['id']+fileExt)
except Exception, e: except Exception, e:
logger.warning(e) self.logger.warning(e)
if self.handle_media_file(media_item, dst):
entry = self.create_liquidsoap_annotation(media_item, dst)
media_item['show_name'] = "TODO"
media_item["annotation"] = entry
ls_playlist = self.handle_media_file(playlist, pkey, bootstrapping)
liquidsoap_playlists[pkey] = ls_playlist
except Exception, e: except Exception, e:
logger.error("%s", e) self.logger.error("%s", e)
return liquidsoap_playlists
return media
def create_liquidsoap_annotation(self, media, dst):
entry = \
'annotate:media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",schedule_table_id="%s":%s' \
% (media['id'], 0, \
float(media['fade_in']) / 1000, \
float(media['fade_out']) / 1000, \
float(media['cue_in']), \
float(media['cue_out']), \
media['row_id'], dst)
""" return entry
Download and cache the media files.
This handles both remote and local files.
Returns an updated ls_playlist string.
"""
def handle_media_file(self, playlist, pkey, bootstrapping):
logger = logging.getLogger('fetch')
ls_playlist = [] def check_for_previous_crash(self, media_item):
start = media_item['start']
end = media_item['end']
dtnow = datetime.utcnow() dtnow = datetime.utcnow()
str_tnow_s = dtnow.strftime('%Y-%m-%d-%H-%M-%S') str_tnow_s = dtnow.strftime('%Y-%m-%d-%H-%M-%S')
sortedKeys = sorted(playlist['medias'].iterkeys()) if start <= str_tnow_s and str_tnow_s < end:
#song is currently playing and we just started pypo. Maybe there
for key in sortedKeys: #was a power outage? Let's restart playback of this song.
media = playlist['medias'][key] start_split = map(int, start.split('-'))
logger.debug("Processing track %s", media['uri']) media_start = datetime(start_split[0], start_split[1], start_split[2], start_split[3], start_split[4], start_split[5], 0, None)
self.logger.debug("Found media item that started at %s.", media_start)
if bootstrapping: delta = dtnow - media_start #we get a TimeDelta object from this operation
start = media['start'] self.logger.info("Starting media item at %d second point", delta.seconds)
end = media['end']
if end <= str_tnow_s:
continue
elif start <= str_tnow_s and str_tnow_s < end:
#song is currently playing and we just started pypo. Maybe there
#was a power outage? Let's restart playback of this song.
start_split = map(int, start.split('-'))
media_start = datetime(start_split[0], start_split[1], start_split[2], start_split[3], start_split[4], start_split[5], 0, None)
logger.debug("Found media item that started at %s.", media_start)
delta = dtnow - media_start #we get a TimeDelta object from this operation
logger.info("Starting media item at %d second point", delta.seconds)
media['cue_in'] = delta.seconds + 10
td = timedelta(seconds=10)
playlist['start'] = (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S')
logger.info("Crash detected, setting playlist to restart at %s", (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S'))
"""
Set the cue_in. This is used by Liquidsoap to determine at what point in the media
item it should start playing. If the cue_in happens to be > cue_out, then make cue_in = cue_out
"""
media_item['cue_in'] = delta.seconds + 10 if delta.seconds + 10 < media_item['cue_out'] else media_item['cue_out']
"""
Set the start time, which is used by pypo-push to determine when a media item is scheduled.
Pushing the start time into the future will ensure pypo-push will push this to Liquidsoap.
"""
td = timedelta(seconds=10)
media_item['start'] = (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S')
self.logger.info("Crash detected, setting playlist to restart at %s", (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S'))
def handle_media_file(self, media_item, dst):
"""
Download and cache the media item.
"""
self.logger.debug("Processing track %s", media_item['uri'])
fileExt = os.path.splitext(media['uri'])[1] try:
try: #blocking function to download the media item
dst = "%s%s/%s%s" % (self.cache_dir, pkey, media['id'], fileExt) self.download_file(media_item, dst)
# download media file if os.access(dst, os.R_OK):
self.handle_remote_file(media, dst) # check filesize (avoid zero-byte files)
try:
if True == os.access(dst, os.R_OK): fsize = os.path.getsize(dst)
# check filesize (avoid zero-byte files)
try: fsize = os.path.getsize(dst)
except Exception, e:
logger.error("%s", e)
fsize = 0
if fsize > 0: if fsize > 0:
pl_entry = \ return True
'annotate:export_source="%s",media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",schedule_table_id="%s":%s' \ except Exception, e:
% (media['export_source'], media['id'], 0, \ self.logger.error("%s", e)
float(media['fade_in']) / 1000, \ fsize = 0
float(media['fade_out']) / 1000, \ else:
float(media['cue_in']), \ self.logger.warning("Cannot read file %s.", dst)
float(media['cue_out']), \
media['row_id'], dst)
""" except Exception, e:
Tracks are only added to the playlist if they are accessible self.logger.info("%s", e)
on the file system and larger than 0 bytes.
So this can lead to playlists shorter than expectet. return False
(there is a hardware silence detector for this cases...)
"""
entry = dict()
entry['type'] = 'file'
entry['annotate'] = pl_entry
entry['show_name'] = playlist['show_name']
ls_playlist.append(entry)
else:
logger.warning("zero-size file - skipping %s. will not add it to playlist at %s", media['uri'], dst)
else:
logger.warning("something went wrong. file %s not available. will not add it to playlist", dst)
except Exception, e: logger.info("%s", e)
return ls_playlist
""" """
Download a file from a remote server and store it in the cache. Download a file from a remote server and store it in the cache.
""" """
def handle_remote_file(self, media, dst): def download_file(self, media_item, dst):
logger = logging.getLogger('fetch')
if os.path.isfile(dst): if os.path.isfile(dst):
pass pass
#logger.debug("file already in cache: %s", dst) #self.logger.debug("file already in cache: %s", dst)
else: else:
logger.debug("try to download %s", media['uri']) self.logger.debug("try to download %s", media_item['uri'])
self.api_client.get_media(media['uri'], dst) self.api_client.get_media(media_item['uri'], dst)
""" """
Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR" Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
and deletes them. and deletes them.
""" """
def cleanup(self, export_source): def cleanup(self):
logger = logging.getLogger('fetch')
offset = 3600 * int(config["cache_for"]) offset = 3600 * int(config["cache_for"])
now = time.time() now = time.time()
@ -495,86 +442,42 @@ class PypoFetch(Thread):
timestamp = calendar.timegm(time.strptime(dir, "%Y-%m-%d-%H-%M-%S")) timestamp = calendar.timegm(time.strptime(dir, "%Y-%m-%d-%H-%M-%S"))
if (now - timestamp) > offset: if (now - timestamp) > offset:
try: try:
logger.debug('trying to remove %s - timestamp: %s', os.path.join(r, dir), timestamp) self.logger.debug('trying to remove %s - timestamp: %s', os.path.join(r, dir), timestamp)
shutil.rmtree(os.path.join(r, dir)) shutil.rmtree(os.path.join(r, dir))
except Exception, e: except Exception, e:
logger.error("%s", e) self.logger.error("%s", e)
pass pass
else: else:
logger.info('sucessfully removed %s', os.path.join(r, dir)) self.logger.info('sucessfully removed %s', os.path.join(r, dir))
except Exception, e: except Exception, e:
logger.error(e) self.logger.error(e)
def main(self): def main(self):
logger = logging.getLogger('fetch')
try: os.mkdir(self.cache_dir)
except Exception, e: pass
# 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.
status, self.schedule_data = self.api_client.get_schedule() success, self.schedule_data = self.api_client.get_schedule()
if status == 1: if success:
logger.info("Bootstrap schedule received: %s", self.schedule_data) self.logger.info("Bootstrap schedule received: %s", self.schedule_data)
self.process_schedule(self.schedule_data, "scheduler", True) self.process_schedule(self.schedule_data, True)
# Bootstrap: since we are just starting up, we need to grab the
# most recent schedule. After that we can just wait for updates.
try:
temp = self.api_client.get_shows_to_record()
if temp is not None:
self.parse_shows(temp)
logger.info("Bootstrap recorder schedule received: %s", temp)
except Exception, e:
logger.error(e)
logger.info("Bootstrap complete: got initial copy of the schedule")
while not self.init_rabbit_mq():
logger.error("Error connecting to RabbitMQ Server. Trying again in few seconds")
time.sleep(5)
loops = 1 loops = 1
while True: while True:
logger.info("Loop #%s", loops) self.logger.info("Loop #%s", loops)
try: try:
try: message = self.fetch_queue.get(block=True, timeout=3600)
message = self.simple_queue.get(block=True) self.handle_message(message)
self.handle_message(message.payload)
# ACK the message to take it off the queue
message.ack()
except MessageStateError, m:
logger.error("Message ACK error: %s", m)
except Exception, e: except Exception, e:
""" self.logger.error("Exception, %s", e)
There is a problem with the RabbitMq messenger service. Let's
log the error and get the schedule via HTTP polling
"""
logger.error("Exception, %s", e)
status, self.schedule_data = self.api_client.get_schedule() success, self.schedule_data = self.api_client.get_schedule()
if status == 1: if success:
self.process_schedule(self.schedule_data, "scheduler", False) self.process_schedule(self.schedule_data, False)
"""
Fetch recorder schedule
"""
try:
temp = self.api_client.get_shows_to_record()
if temp is not None:
self.parse_shows(temp)
logger.info("updated recorder schedule received: %s", temp)
except Exception, e:
logger.error(e)
loops += 1 loops += 1
"""
Main loop of the thread:
Wait for schedule updates from RabbitMQ, but in case there arent any,
poll the server to get the upcoming schedule.
"""
def run(self): def run(self):
while True: """
self.main() Entry point of the thread
"""
self.main()

View File

@ -0,0 +1,120 @@
import logging
import logging.config
import sys
from configobj import ConfigObj
from threading import Thread
import time
# For RabbitMQ
from kombu.connection import BrokerConnection
from kombu.messaging import Exchange, Queue, Consumer, Producer
from kombu.exceptions import MessageStateError
from kombu.simple import SimpleQueue
import json
# configure logging
logging.config.fileConfig("logging.cfg")
# loading config file
try:
config = ConfigObj('/etc/airtime/pypo.cfg')
LS_HOST = config['ls_host']
LS_PORT = config['ls_port']
POLL_INTERVAL = int(config['poll_interval'])
except Exception, e:
logger = logging.getLogger('message_h')
logger.error('Error loading config file: %s', e)
sys.exit()
class PypoMessageHandler(Thread):
def __init__(self, pq, rq):
Thread.__init__(self)
self.logger = logging.getLogger('message_h')
self.pypo_queue = pq
self.recorder_queue = rq
def init_rabbit_mq(self):
self.logger.info("Initializing RabbitMQ stuff")
try:
schedule_exchange = Exchange("airtime-pypo", "direct", durable=True, auto_delete=True)
schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo")
connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], config["rabbitmq_vhost"])
channel = connection.channel()
self.simple_queue = SimpleQueue(channel, schedule_queue)
except Exception, e:
self.logger.error(e)
return False
return True
"""
Handle a message from RabbitMQ, put it into our yucky global var.
Hopefully there is a better way to do this.
"""
def handle_message(self, message):
try:
self.logger.info("Received event from RabbitMQ: %s" % message)
m = json.loads(message)
command = m['event_type']
self.logger.info("Handling command: " + command)
if command == 'update_schedule':
self.logger.info("Updating schdule...")
self.pypo_queue.put(message)
elif command == 'update_stream_setting':
self.logger.info("Updating stream setting...")
self.pypo_queue.put(message)
elif command == 'update_stream_format':
self.logger.info("Updating stream format...")
self.pypo_queue.put(message)
elif command == 'update_station_name':
self.logger.info("Updating station name...")
self.pypo_queue.put(message)
elif command == 'cancel_current_show':
self.logger.info("Cancel current show command received...")
self.pypo_queue.put(message)
elif command == 'update_recorder_schedule':
self.recorder_queue.put(message)
elif command == 'cancel_recording':
self.recorder_queue.put(message)
except Exception, e:
self.logger.error("Exception in handling RabbitMQ message: %s", e)
def main(self):
while not self.init_rabbit_mq():
self.logger.error("Error connecting to RabbitMQ Server. Trying again in few seconds")
time.sleep(5)
loops = 1
while True:
self.logger.info("Loop #%s", loops)
try:
message = self.simple_queue.get(block=True)
self.handle_message(message.payload)
# ACK the message to take it off the queue
message.ack()
except Exception, e:
"""
sleep 5 seconds so that we don't spin inside this
while loop and eat all the CPU
"""
time.sleep(5)
"""
There is a problem with the RabbitMq messenger service. Let's
log the error and get the schedule via HTTP polling
"""
self.logger.error("Exception, %s", e)
loops += 1
"""
Main loop of the thread:
Wait for schedule updates from RabbitMQ, but in case there arent any,
poll the server to get the upcoming schedule.
"""
def run(self):
while True:
self.main()

View File

@ -34,166 +34,147 @@ class PypoPush(Thread):
def __init__(self, q): def __init__(self, q):
Thread.__init__(self) Thread.__init__(self)
self.api_client = api_client.api_client_factory(config) self.api_client = api_client.api_client_factory(config)
self.set_export_source('scheduler')
self.queue = q self.queue = q
self.schedule = dict() self.media = dict()
self.playlists = dict()
self.liquidsoap_state_play = True self.liquidsoap_state_play = True
self.push_ahead = 10 self.push_ahead = 10
self.last_end_time = 0
def set_export_source(self, export_source):
self.export_source = export_source
self.cache_dir = config["cache_dir"] + self.export_source + '/'
self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle"
""" self.logger = logging.getLogger('push')
The Push Loop - the push loop periodically checks if there is a playlist
that should be scheduled at the current time. def push(self):
If yes, the current liquidsoap playlist gets replaced with the corresponding one, """
then liquidsoap is asked (via telnet) to reload and immediately play it. The Push Loop - the push loop periodically checks if there is a playlist
""" that should be scheduled at the current time.
def push(self, export_source): If yes, the current liquidsoap playlist gets replaced with the corresponding one,
logger = logging.getLogger('push') then liquidsoap is asked (via telnet) to reload and immediately play it.
"""
timenow = time.time() timenow = time.time()
# get a new schedule from pypo-fetch # get a new schedule from pypo-fetch
if not self.queue.empty(): if not self.queue.empty():
# make sure we get the latest schedule # make sure we get the latest schedule
while not self.queue.empty(): while not self.queue.empty():
scheduled_data = self.queue.get() self.media = self.queue.get()
logger.debug("Received data from pypo-fetch") self.logger.debug("Received data from pypo-fetch")
self.schedule = scheduled_data['schedule'] self.logger.debug('media %s' % json.dumps(self.media))
self.playlists = scheduled_data['liquidsoap_playlists']
logger.debug('schedule %s' % json.dumps(self.schedule))
logger.debug('playlists %s' % json.dumps(self.playlists))
schedule = self.schedule media = self.media
playlists = self.playlists
currently_on_air = False currently_on_air = False
if schedule: if media:
tnow = time.gmtime(timenow) tnow = time.gmtime(timenow)
tcoming = time.gmtime(timenow + self.push_ahead) tcoming = time.gmtime(timenow + self.push_ahead)
str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5]) str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
str_tcoming_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming[0], tcoming[1], tcoming[2], tcoming[3], tcoming[4], tcoming[5]) str_tcoming_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming[0], tcoming[1], tcoming[2], tcoming[3], tcoming[4], tcoming[5])
for pkey in schedule: for key in media:
plstart = schedule[pkey]['start'][0:19] media_item = media[key]
item_start = media_item['start'][0:19]
if str_tnow_s <= plstart and plstart < str_tcoming_s:
logger.debug('Preparing to push playlist scheduled at: %s', pkey) if str_tnow_s <= item_start and item_start < str_tcoming_s:
playlist = schedule[pkey] """
If the media item starts in the next 30 seconds, push it to the queue.
"""
# We have a match, replace the current playlist and self.logger.debug('Preparing to push media item scheduled at: %s', key)
# force liquidsoap to refresh.
if (self.push_liquidsoap(pkey, schedule, playlists) == 1): if self.push_to_liquidsoap(media_item):
logger.debug("Pushed to liquidsoap, updating 'played' status.") self.logger.debug("Pushed to liquidsoap, updating 'played' status.")
currently_on_air = True currently_on_air = True
self.liquidsoap_state_play = True self.liquidsoap_state_play = True
# Call API to update schedule states def push_to_liquidsoap(self, media_item):
logger.debug("Doing callback to server to update 'played' status.") """
self.api_client.notify_scheduled_item_start_playing(pkey, schedule) This function looks at the media item, and either pushes it to the Liquidsoap
queue immediately, or if the queue is empty - waits until the start time of the
show_start = schedule[pkey]['show_start'] media item before pushing it.
show_end = schedule[pkey]['show_end'] """
if show_start <= str_tnow_s and str_tnow_s < show_end:
currently_on_air = True
"""
If currently_on_air = False but liquidsoap_state_play = True then it means that Liquidsoap may
still be playing audio even though the show has ended ('currently_on_air = False' means no show is scheduled)
See CC-3231.
This is a temporary solution for Airtime 2.0
"""
if not currently_on_air and self.liquidsoap_state_play:
logger.debug('Notifying Liquidsoap to stop playback.')
try:
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
tn.write('source.skip\n')
tn.write('exit\n')
tn.read_all()
except Exception, e:
logger.debug(e)
logger.debug('Could not connect to liquidsoap')
self.liquidsoap_state_play = False
def push_liquidsoap(self, pkey, schedule, playlists):
logger = logging.getLogger('push')
try: try:
playlist = playlists[pkey] if media_item["start"] == self.last_end_time:
plstart = schedule[pkey]['start'][0:19] """
this media item is attached to the end of the last
#strptime returns struct_time in local time track, so let's push it now so that Liquidsoap can start playing
#mktime takes a time_struct and returns a floating point it immediately after (and prepare crossfades if need be).
#gmtime Convert a time expressed in seconds since the epoch to a struct_time in UTC """
#mktime: expresses the time in local time, not UTC. It returns a floating point number, for compatibility with time(). telnet_to_liquidsoap(media_item)
self.last_end_time = media_item["end"]
epoch_start = calendar.timegm(time.strptime(plstart, '%Y-%m-%d-%H-%M-%S')) else:
"""
#Return the time as a floating point number expressed in seconds since the epoch, in UTC. this media item does not start right after a current playing track.
epoch_now = time.time() We need to sleep, and then wake up when this track starts.
"""
logger.debug("Epoch start: %s" % epoch_start) self.sleep_until_start(media_item)
logger.debug("Epoch now: %s" % epoch_now)
self.telnet_to_liquidsoap(media_item)
sleep_time = epoch_start - epoch_now; self.last_end_time = media_item["end"]
if sleep_time < 0:
sleep_time = 0
logger.debug('sleeping for %s s' % (sleep_time))
time.sleep(sleep_time)
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
#skip the currently playing song if any.
logger.debug("source.skip\n")
tn.write("source.skip\n")
# Get any extra information for liquidsoap (which will be sent back to us)
liquidsoap_data = self.api_client.get_liquidsoap_data(pkey, schedule)
#Sending schedule table row id string.
logger.debug("vars.pypo_data %s\n"%(liquidsoap_data["schedule_id"]))
tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
logger.debug('Preparing to push playlist %s' % pkey)
for item in playlist:
annotate = item['annotate']
tn.write(str('queue.push %s\n' % annotate.encode('utf-8')))
show_name = item['show_name']
tn.write(str('vars.show_name %s\n' % show_name.encode('utf-8')))
tn.write("exit\n")
logger.debug(tn.read_all())
status = 1
except Exception, e: except Exception, e:
logger.error('%s', e) return False
status = 0
return status return True
def sleep_until_start(self, media_item):
"""
The purpose of this function is to look at the difference between
"now" and when the media_item starts, and sleep for that period of time.
After waking from sleep, this function returns.
"""
mi_start = media_item['start'][0:19]
#strptime returns struct_time in local time
epoch_start = calendar.timegm(time.strptime(mi_start, '%Y-%m-%d-%H-%M-%S'))
#Return the time as a floating point number expressed in seconds since the epoch, in UTC.
epoch_now = time.time()
self.logger.debug("Epoch start: %s" % epoch_start)
self.logger.debug("Epoch now: %s" % epoch_now)
sleep_time = epoch_start - epoch_now
if sleep_time < 0:
sleep_time = 0
self.logger.debug('sleeping for %s s' % (sleep_time))
time.sleep(sleep_time)
def telnet_to_liquidsoap(self, media_item):
"""
telnets to liquidsoap and pushes the media_item to its queue. Push the
show name of every media_item as well, just to keep Liquidsoap up-to-date
about which show is playing.
"""
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
#tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
annotation = media_item['annotation']
msg = 'queue.push %s\n' % annotation.encode('utf-8')
tn.write(msg)
self.logger.debug(msg)
show_name = media_item['show_name']
msg = 'vars.show_name %s\n' % show_name.encode('utf-8')
tn.write(msg)
self.logger.debug(msg)
tn.write("exit\n")
self.logger.debug(tn.read_all())
def run(self): def run(self):
loops = 0 loops = 0
heartbeat_period = math.floor(30/PUSH_INTERVAL) heartbeat_period = math.floor(30/PUSH_INTERVAL)
logger = logging.getLogger('push')
while True: while True:
if loops % heartbeat_period == 0: if loops % heartbeat_period == 0:
logger.info("heartbeat") self.logger.info("heartbeat")
loops = 0 loops = 0
try: self.push('scheduler') try: self.push()
except Exception, e: except Exception, e:
logger.error('Pypo Push Exception: %s', e) self.logger.error('Pypo Push Exception: %s', e)
time.sleep(PUSH_INTERVAL) time.sleep(PUSH_INTERVAL)
loops += 1 loops += 1

View File

@ -169,26 +169,42 @@ class Recorder(Thread):
def __init__(self, q): def __init__(self, q):
Thread.__init__(self) Thread.__init__(self)
self.logger = logging.getLogger('recorder') self.logger = logging.getLogger('recorder')
self.api_client = api_client.api_client_factory(config) self.api_client = api_client.api_client_factory(config, self.logger)
self.api_client.register_component("show-recorder") self.api_client.register_component("show-recorder")
self.sr = None self.sr = None
self.shows_to_record = {} self.shows_to_record = {}
self.server_timezone = '' self.server_timezone = ''
self.queue = q self.queue = q
self.logger.info("RecorderFetch: init complete") self.logger.info("RecorderFetch: init complete")
self.loops = 0
def handle_message(self): def handle_message(self):
if not self.queue.empty(): if not self.queue.empty():
msg = self.queue.get() message = self.queue.get()
self.logger.info("Receivied msg from Pypo Fetch: %s", msg) msg = json.loads(message)
if msg == 'cancel_recording': command = msg["event_type"]
self.logger.info("Received msg from Pypo Message Handler: %s", msg)
if command == 'cancel_recording':
if self.sr is not None and self.sr.is_recording(): if self.sr is not None and self.sr.is_recording():
self.sr.cancel_recording() self.sr.cancel_recording()
else: else:
self.shows_to_record = msg self.process_recorder_schedule(msg)
self.loops = 0
if self.shows_to_record: if self.shows_to_record:
self.start_record() self.start_record()
def process_recorder_schedule(self, m):
self.logger.info("Parsing recording show schedules...")
temp_shows_to_record = {}
shows = m['shows']
for show in shows:
show_starts = getDateTimeObj(show[u'starts'])
show_end = getDateTimeObj(show[u'ends'])
time_delta = show_end - show_starts
temp_shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']]
self.shows_to_record = temp_shows_to_record
def get_time_till_next_show(self): def get_time_till_next_show(self):
if len(self.shows_to_record) != 0: if len(self.shows_to_record) != 0:
@ -247,21 +263,43 @@ class Recorder(Thread):
def run(self): def run(self):
try: try:
self.logger.info("Started...") self.logger.info("Started...")
# Bootstrap: since we are just starting up, we need to grab the
# most recent schedule. After that we can just wait for updates.
try:
temp = self.api_client.get_shows_to_record()
if temp is not None:
self.process_recorder_schedule(temp)
self.logger.info("Bootstrap recorder schedule received: %s", temp)
except Exception, e:
self.logger.error(e)
self.logger.info("Bootstrap complete: got initial copy of the schedule")
recording = False recording = False
loops = 0 self.loops = 0
heartbeat_period = math.floor(30/PUSH_INTERVAL) heartbeat_period = math.floor(30/PUSH_INTERVAL)
while True: while True:
if loops % heartbeat_period == 0: if self.loops % heartbeat_period == 0:
self.logger.info("heartbeat") self.logger.info("heartbeat")
loops = 0 if self.loops * PUSH_INTERVAL > 3600:
self.loops = 0
"""
Fetch recorder schedule
"""
try:
temp = self.api_client.get_shows_to_record()
if temp is not None:
self.process_recorder_schedule(temp)
self.logger.info("updated recorder schedule received: %s", temp)
except Exception, e:
self.logger.error(e)
try: self.handle_message() try: self.handle_message()
except Exception, e: except Exception, e:
self.logger.error('Pypo Recorder Exception: %s', e) self.logger.error('Pypo Recorder Exception: %s', e)
time.sleep(PUSH_INTERVAL) time.sleep(PUSH_INTERVAL)
loops += 1 self.loops += 1
except Exception,e : except Exception,e :
import traceback import traceback
top = traceback.format_exc() top = traceback.format_exc()