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;
@ -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;
$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; $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;
$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; $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;
$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; $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)) {
$useMinMax = false;
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) { if (null === $comparison) {
if (is_array($dbCliplength)) {
$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)) {
$useMinMax = false;
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) { if (null === $comparison) {
if (is_array($dbCuein)) {
$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)) {
$useMinMax = false;
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) { if (null === $comparison) {
if (is_array($dbCueout)) {
$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;
$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; $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;
$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; $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;
$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; $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)) {
$useMinMax = false;
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) { if (null === $comparison) {
if (is_array($dbClipLength)) {
$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)) {
$useMinMax = false;
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) { if (null === $comparison) {
if (is_array($dbCueIn)) {
$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)) {
$useMinMax = false;
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) { if (null === $comparison) {
if (is_array($dbCueOut)) {
$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

@ -35,3 +35,16 @@ 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

@ -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[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,20 +81,20 @@ 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);
} }
} }
@ -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,183 +41,7 @@ var AIRTIME = (function(AIRTIME){
AIRTIME.library.fnDeleteItems(aMedia); AIRTIME.library.fnDeleteItems(aMedia);
}; };
return AIRTIME; libraryInit = function() {
}(AIRTIME || {}));
function addToolBarButtonsLibrary(aButtons) {
var i,
length = aButtons.length,
libToolBar,
html,
buttonClass = '',
DEFAULT_CLASS = 'ui-button ui-state-default',
DISABLED_CLASS = 'ui-state-disabled';
libToolBar = $(".library_toolbar");
for ( i=0; i < length; i+=1 ) {
buttonClass = '';
//add disabled class if not enabled.
if (aButtons[i][2] === false) {
buttonClass+=DISABLED_CLASS;
}
html = '<div id="'+aButtons[i][1]+'" class="ColVis TableTools"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>';
libToolBar.append(html);
libToolBar.find("#"+aButtons[i][1]).click(aButtons[i][3]);
}
}
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(){
$.getJSON('/Preference/is-import-in-progress', function(data){
var div = $('#import_status');
if (data == true){
div.show();
}
else{
div.hide();
}
});
}
function addProgressIcon(id) {
var tr = $("#au_"+id),
span;
span = tr.find("td.library_title").find("span");
if (span.length > 0){
span.removeClass()
.addClass("small-icon progress");
}
else{
tr.find("td.library_title")
.append('<span class="small-icon progress"></span>');
}
}
function checkSCUploadStatus(){
var url = '/Library/get-upload-to-soundcloud-status';
$("span[class*=progress]").each(function(){
var span, id;
span = $(this);
id = span.parent().parent().data("aData").id;
$.post(url, {format: "json", id: id, type:"file"}, function(json){
if (json.sc_id > 0) {
span.removeClass("progress")
.addClass("soundcloud");
}
else if (json.sc_id == "-3") {
span.removeClass("progress")
.addClass("sc-error");
}
});
});
}
function addQtipToSCIcons(){
$(".progress, .soundcloud, .sc-error").live('mouseover', function(){
var id = $(this).parent().parent().data("aData").id;
if ($(this).hasClass("progress")){
$(this).qtip({
content: {
text: "Uploading in progress..."
},
position:{
adjust: {
resize: true,
method: "flip flip"
},
at: "right center",
my: "left top",
viewport: $(window)
},
show: {
ready: true // Needed to make it show on first mouseover event
}
});
}
else if($(this).hasClass("soundcloud")){
$(this).qtip({
content: {
text: "Retreiving data from the server...",
ajax: {
url: "/Library/get-upload-to-soundcloud-status",
type: "post",
data: ({format: "json", id : id, type: "file"}),
success: function(json, status){
this.set('content.text', "The soundcloud id for this file is: "+json.sc_id);
}
}
},
position:{
adjust: {
resize: true,
method: "flip flip"
},
at: "right center",
my: "left top",
viewport: $(window)
},
show: {
ready: true // Needed to make it show on first mouseover event
}
});
}else if($(this).hasClass("sc-error")){
$(this).qtip({
content: {
text: "Retreiving data from the server...",
ajax: {
url: "/Library/get-upload-to-soundcloud-status",
type: "post",
data: ({format: "json", id : id, type: "file"}),
success: function(json, status){
this.set('content.text', "There was error while uploading to soundcloud.<br>"+"Error code: "+json.error_code+
"<br>"+"Error msg: "+json.error_msg+"<br>");
}
}
},
position:{
adjust: {
resize: true,
method: "flip flip"
},
at: "right center",
my: "left top",
viewport: $(window)
},
show: {
ready: true // Needed to make it show on first mouseover event
}
});
}
});
}
$(document).ready(function() {
var oTable; var oTable;
oTable = $('#library_display').dataTable( { oTable = $('#library_display').dataTable( {
@ -234,8 +63,8 @@ $(document).ready(function() {
/* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"}, /* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"},
/* Composer */ {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer"}, /* 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"}, /* 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"}, /* Bit Rate */ {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate", "sWidth": "80px"},
/* Sameple Rate */ {"sTitle": "Sample Rate", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr"}, /* 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"}, /* 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"}, /* 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"}, /* Label */ {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label"},
@ -400,24 +229,40 @@ $(document).ready(function() {
"sRowSelect": "multi", "sRowSelect": "multi",
"aButtons": [], "aButtons": [],
"fnRowSelected": function ( node ) { "fnRowSelected": function ( node ) {
var selected;
//seems to happen if everything is selected //seems to happen if everything is selected
if ( node === null) { if ( node === null) {
oTable.find("input[type=checkbox]").attr("checked", true); selected = oTable.find("input[type=checkbox]");
selected.attr("checked", true);
} }
else { else {
$(node).find("input[type=checkbox]").attr("checked", true); $(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 ) { "fnRowDeselected": function ( node ) {
var selected;
//seems to happen if everything is deselected //seems to happen if everything is deselected
if ( node === null) { if ( node === null) {
oTable.find("input[type=checkbox]").attr("checked", false); oTable.find("input[type=checkbox]").attr("checked", false);
selected = [];
} }
else { else {
$(node).find("input[type=checkbox]").attr("checked", false); $(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();
} }
}, },
@ -472,7 +317,7 @@ $(document).ready(function() {
ignoreRightClick: true, ignoreRightClick: true,
build: function($el, e) { build: function($el, e) {
var x, request, data, screen, items, callback, $tr; var data, screen, items, callback, $tr;
$tr = $el.parent(); $tr = $el.parent();
data = $tr.data("aData"); data = $tr.data("aData");
@ -527,7 +372,7 @@ $(document).ready(function() {
media.push({"id": data.id, "type": data.ftype}); media.push({"id": data.id, "type": data.ftype});
$.post(oItems.del.url, {format: "json", media: media }, function(json){ $.post(oItems.del.url, {format: "json", media: media }, function(json){
var oTable, tr; var oTable;
if (json.message) { if (json.message) {
alert(json.message); alert(json.message);
@ -595,4 +440,184 @@ $(document).ready(function() {
}; };
} }
}); });
}); };
mod.libraryInit = libraryInit;
return AIRTIME;
}(AIRTIME || {}));
function addToolBarButtonsLibrary(aButtons) {
var i,
length = aButtons.length,
libToolBar = $(".library_toolbar"),
html,
buttonClass = '',
DEFAULT_CLASS = 'ui-button ui-state-default',
DISABLED_CLASS = 'ui-state-disabled',
fn;
for ( i = 0; i < length; i += 1 ) {
buttonClass = '';
//add disabled class if not enabled.
if (aButtons[i][2] === false) {
buttonClass += DISABLED_CLASS;
}
html = '<div class="ColVis TableTools '+aButtons[i][1]+'"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>';
libToolBar.append(html);
//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 checkImportStatus(){
$.getJSON('/Preference/is-import-in-progress', function(data){
var div = $('#import_status');
if (data == true){
div.show();
}
else{
div.hide();
}
});
}
function addProgressIcon(id) {
var tr = $("#au_"+id),
span;
span = tr.find("td.library_title").find("span");
if (span.length > 0){
span.removeClass()
.addClass("small-icon progress");
}
else{
tr.find("td.library_title")
.append('<span class="small-icon progress"></span>');
}
}
function checkSCUploadStatus(){
var url = '/Library/get-upload-to-soundcloud-status';
$("span[class*=progress]").each(function(){
var span, id;
span = $(this);
id = span.parent().parent().data("aData").id;
$.post(url, {format: "json", id: id, type:"file"}, function(json){
if (json.sc_id > 0) {
span.removeClass("progress")
.addClass("soundcloud");
}
else if (json.sc_id == "-3") {
span.removeClass("progress")
.addClass("sc-error");
}
});
});
}
function addQtipToSCIcons(){
$(".progress, .soundcloud, .sc-error").live('mouseover', function(){
var id = $(this).parent().parent().data("aData").id;
if ($(this).hasClass("progress")){
$(this).qtip({
content: {
text: "Uploading in progress..."
},
position:{
adjust: {
resize: true,
method: "flip flip"
},
at: "right center",
my: "left top",
viewport: $(window)
},
show: {
ready: true // Needed to make it show on first mouseover event
}
});
}
else if($(this).hasClass("soundcloud")){
$(this).qtip({
content: {
text: "Retreiving data from the server...",
ajax: {
url: "/Library/get-upload-to-soundcloud-status",
type: "post",
data: ({format: "json", id : id, type: "file"}),
success: function(json, status){
this.set('content.text', "The soundcloud id for this file is: "+json.sc_id);
}
}
},
position:{
adjust: {
resize: true,
method: "flip flip"
},
at: "right center",
my: "left top",
viewport: $(window)
},
show: {
ready: true // Needed to make it show on first mouseover event
}
});
}else if($(this).hasClass("sc-error")){
$(this).qtip({
content: {
text: "Retreiving data from the server...",
ajax: {
url: "/Library/get-upload-to-soundcloud-status",
type: "post",
data: ({format: "json", id : id, type: "file"}),
success: function(json, status){
this.set('content.text', "There was error while uploading to soundcloud.<br>"+"Error code: "+json.error_code+
"<br>"+"Error msg: "+json.error_msg+"<br>");
}
}
},
position:{
adjust: {
resize: true,
method: "flip flip"
},
at: "right center",
my: "left top",
viewport: $(window)
},
show: {
ready: true // Needed to make it show on first mouseover event
}
});
}
});
}

View File

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

View File

@ -506,17 +506,24 @@ 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,20 +67,9 @@ 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
@ -101,8 +90,56 @@ 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,
@ -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,9 +69,12 @@ $(document).ready(function() {
} ); } );
}; };
oRange = fnGetScheduleRange(); mod.fnServerData = fnServerData;
fnServerData.start = oRange.start;
fnServerData.end = oRange.end; mod.builderDataTable = function() {
var tableDiv = $('#show_builder_table'),
oTable,
fnRemoveSelectedItems;
fnRemoveSelectedItems = function() { fnRemoveSelectedItems = function() {
var oTT = TableTools.fnGetInstance('show_builder_table'), var oTT = TableTools.fnGetInstance('show_builder_table'),
@ -273,7 +172,7 @@ $(document).ready(function() {
oData.iCreate = parseInt(oData.iCreate, 10); oData.iCreate = parseInt(oData.iCreate, 10);
}, },
"fnServerData": fnServerData, "fnServerData": AIRTIME.showbuilder.fnServerData,
"fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { "fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
var i, var i,
sSeparatorHTML, sSeparatorHTML,
@ -284,15 +183,22 @@ $(document).ready(function() {
//save some info for reordering purposes. //save some info for reordering purposes.
$(nRow).data({"aData": aData}); $(nRow).data({"aData": aData});
if (aData.current === true) {
$(nRow).addClass("sb-now-playing");
}
if (aData.allowed !== true) { if (aData.allowed !== true) {
$(nRow).addClass("sb-not-allowed"); $(nRow).addClass("sb-not-allowed");
} }
else {
$(nRow).addClass("sb-allowed");
}
//status used to colour tracks. //status used to colour tracks.
if (aData.status === 1) { if (aData.status === 2) {
$(nRow).addClass("sb-boundry"); $(nRow).addClass("sb-boundry");
} }
else if (aData.status === 2) { else if (aData.status === 0) {
$(nRow).addClass("sb-over"); $(nRow).addClass("sb-over");
} }
@ -340,6 +246,13 @@ $(document).ready(function() {
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0); 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 { else {
node = nRow.children[0]; node = nRow.children[0];
@ -416,16 +329,26 @@ $(document).ready(function() {
else { else {
$(node).find("input[type=checkbox]").attr("checked", true); $(node).find("input[type=checkbox]").attr("checked", true);
} }
//checking to enable buttons
AIRTIME.button.enableButton("sb_delete");
}, },
"fnRowDeselected": function ( node ) { "fnRowDeselected": function ( node ) {
var selected;
//seems to happen if everything is deselected //seems to happen if everything is deselected
if ( node === null) { if ( node === null) {
var oTable = $("#show_builder_table").dataTable(); tableDiv.find("input[type=checkbox]").attr("checked", false);
oTable.find("input[type=checkbox]").attr("checked", false); selected = [];
} }
else { else {
$(node).find("input[type=checkbox]").attr("checked", false); $(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");
} }
} }
}, },
@ -454,35 +377,6 @@ $(document).ready(function() {
} }
}); });
$("#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 sortableConf = (function(){
var origTrs, var origTrs,
aItemData = [], aItemData = [],
@ -496,10 +390,9 @@ $(document).ready(function() {
fnAdd = function() { fnAdd = function() {
var aMediaIds = [], var aMediaIds = [],
aSchedIds = [], aSchedIds = [];
oLibTT = TableTools.fnGetInstance('library_display');
for(i=0; i < aItemData.length; i++) { for(i = 0; i < aItemData.length; i++) {
aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype}); aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype});
} }
aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp}); aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
@ -518,18 +411,16 @@ $(document).ready(function() {
}; };
fnReceive = function(event, ui) { fnReceive = function(event, ui) {
var selected = $('#library_display tr:not(:first) input:checked').parents('tr'), var aItems = [],
aItems = []; oLibTT = TableTools.fnGetInstance('library_display');
aItems = oLibTT.fnGetSelectedData();
//if nothing is checked select the dragged item. //if nothing is checked select the dragged item.
if (selected.length === 0) { if (aItems.length === 0) {
selected = ui.item; aItems.push(ui.item.data("aData"));
} }
selected.each(function(i, el) {
aItems.push($(el).data("aData"));
});
origTrs = aItems; origTrs = aItems;
html = ui.helper.html(); html = ui.helper.html();
}; };
@ -538,8 +429,8 @@ $(document).ready(function() {
var prev = ui.item.prev(); var prev = ui.item.prev();
//can't add items outside of shows. //can't add items outside of shows.
if (prev.hasClass("sb-footer")) { if (!prev.hasClass("sb-allowed")) {
alert("Cannot add an item outside a show."); alert("Cannot schedule outside a show.");
ui.item.remove(); ui.item.remove();
return; return;
} }
@ -577,7 +468,7 @@ $(document).ready(function() {
tableDiv.sortable(sortableConf); tableDiv.sortable(sortableConf);
$("#show_builder .fg-toolbar") $("#show_builder .fg-toolbar")
.append('<div class="ColVis TableTools"><button class="ui-button ui-state-default"><span>Delete</span></button></div>') .append('<div class="ColVis TableTools sb_delete"><button class="ui-button ui-state-default ui-state-disabled"><span>Delete</span></button></div>')
.click(fnRemoveSelectedItems); .click(fnRemoveSelectedItems);
//set things like a reference to the table. //set things like a reference to the table.
@ -585,16 +476,28 @@ $(document).ready(function() {
//add event to cursors. //add event to cursors.
tableDiv.find("tbody").on("click", "div.marker", function(event){ tableDiv.find("tbody").on("click", "div.marker", function(event){
var tr = $(this).parents("tr"); var tr = $(this).parents("tr"),
cursorSelClass = "cursor-selected-row";
if (tr.hasClass("cursor-selected-row")) { if (tr.hasClass(cursorSelClass)) {
tr.removeClass("cursor-selected-row"); tr.removeClass(cursorSelClass);
} }
else { else {
tr.addClass("cursor-selected-row"); tr.addClass(cursorSelClass);
} }
//check if add button can still be enabled.
AIRTIME.library.events.enableAddButtonCheck();
return false; return false;
}); });
}); };
mod.init = function(oTable) {
oSchedTable = oTable;
};
return AIRTIME;
}(AIRTIME || {}));

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
return True self.schedule_data = []
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_media(self, media, bootstrapping):
"""
Iterate through the list of media items in "media" and
download them.
""" """
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())
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))
except Exception, e:
logger.warning(e)
ls_playlist = self.handle_media_file(playlist, pkey, bootstrapping)
liquidsoap_playlists[pkey] = ls_playlist
except Exception, e:
logger.error("%s", e)
return liquidsoap_playlists
""" """
Download and cache the media files. Extract year, month, date from mkey
This handles both remote and local files.
Returns an updated ls_playlist string.
""" """
def handle_media_file(self, playlist, pkey, bootstrapping): y_m_d = mkey[0:10]
logger = logging.getLogger('fetch') download_dir = os.path.join(self.cache_dir, y_m_d)
ls_playlist = []
dtnow = datetime.utcnow()
str_tnow_s = dtnow.strftime('%Y-%m-%d-%H-%M-%S')
sortedKeys = sorted(playlist['medias'].iterkeys())
for key in sortedKeys:
media = playlist['medias'][key]
logger.debug("Processing track %s", media['uri'])
if bootstrapping:
start = media['start']
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'))
fileExt = os.path.splitext(media['uri'])[1]
try: try:
dst = "%s%s/%s%s" % (self.cache_dir, pkey, media['id'], fileExt) os.makedirs(os.path.join(self.cache_dir, y_m_d))
# download media file
self.handle_remote_file(media, dst)
if True == os.access(dst, os.R_OK):
# check filesize (avoid zero-byte files)
try: fsize = os.path.getsize(dst)
except Exception, e: except Exception, e:
logger.error("%s", e) pass
fsize = 0 fileExt = os.path.splitext(media_item['uri'])[1]
dst = os.path.join(download_dir, media_item['id']+fileExt)
except Exception, e:
self.logger.warning(e)
if fsize > 0: if self.handle_media_file(media_item, dst):
pl_entry = \ entry = self.create_liquidsoap_annotation(media_item, dst)
'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' \ media_item['show_name'] = "TODO"
% (media['export_source'], media['id'], 0, \ media_item["annotation"] = entry
except Exception, e:
self.logger.error("%s", e)
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_in']) / 1000, \
float(media['fade_out']) / 1000, \ float(media['fade_out']) / 1000, \
float(media['cue_in']), \ float(media['cue_in']), \
float(media['cue_out']), \ float(media['cue_out']), \
media['row_id'], dst) media['row_id'], dst)
return entry
def check_for_previous_crash(self, media_item):
start = media_item['start']
end = media_item['end']
dtnow = datetime.utcnow()
str_tnow_s = dtnow.strftime('%Y-%m-%d-%H-%M-%S')
if 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)
self.logger.debug("Found media item that started at %s.", media_start)
delta = dtnow - media_start #we get a TimeDelta object from this operation
self.logger.info("Starting media item at %d second point", delta.seconds)
""" """
Tracks are only added to the playlist if they are accessible Set the cue_in. This is used by Liquidsoap to determine at what point in the media
on the file system and larger than 0 bytes. item it should start playing. If the cue_in happens to be > cue_out, then make cue_in = cue_out
So this can lead to playlists shorter than expectet.
(there is a hardware silence detector for this cases...)
""" """
entry = dict() media_item['cue_in'] = delta.seconds + 10 if delta.seconds + 10 < media_item['cue_out'] else media_item['cue_out']
entry['type'] = 'file'
entry['annotate'] = pl_entry
entry['show_name'] = playlist['show_name']
ls_playlist.append(entry)
"""
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'])
try:
#blocking function to download the media item
self.download_file(media_item, dst)
if os.access(dst, os.R_OK):
# check filesize (avoid zero-byte files)
try:
fsize = os.path.getsize(dst)
if fsize > 0:
return True
except Exception, e:
self.logger.error("%s", e)
fsize = 0
else: else:
logger.warning("zero-size file - skipping %s. will not add it to playlist at %s", media['uri'], dst) self.logger.warning("Cannot read file %s.", dst)
else: except Exception, e:
logger.warning("something went wrong. file %s not available. will not add it to playlist", dst) self.logger.info("%s", e)
except Exception, e: logger.info("%s", e) return False
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: """
Entry point of the thread
"""
self.main() 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.logger = logging.getLogger('push')
self.export_source = export_source
self.cache_dir = config["cache_dir"] + self.export_source + '/'
self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle"
def push(self):
""" """
The Push Loop - the push loop periodically checks if there is a playlist The Push Loop - the push loop periodically checks if there is a playlist
that should be scheduled at the current time. that should be scheduled at the current time.
If yes, the current liquidsoap playlist gets replaced with the corresponding one, If yes, the current liquidsoap playlist gets replaced with the corresponding one,
then liquidsoap is asked (via telnet) to reload and immediately play it. then liquidsoap is asked (via telnet) to reload and immediately play it.
""" """
def push(self, export_source):
logger = logging.getLogger('push')
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)) media = self.media
logger.debug('playlists %s' % json.dumps(self.playlists))
schedule = self.schedule
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: if str_tnow_s <= item_start and item_start < str_tcoming_s:
logger.debug('Preparing to push playlist scheduled at: %s', pkey) """
playlist = schedule[pkey] If the media item starts in the next 30 seconds, push it to the queue.
"""
self.logger.debug('Preparing to push media item scheduled at: %s', key)
if self.push_to_liquidsoap(media_item):
# We have a match, replace the current playlist and self.logger.debug("Pushed to liquidsoap, updating 'played' status.")
# force liquidsoap to refresh.
if (self.push_liquidsoap(pkey, schedule, playlists) == 1):
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)
show_start = schedule[pkey]['show_start']
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 This function looks at the media item, and either pushes it to the Liquidsoap
still be playing audio even though the show has ended ('currently_on_air = False' means no show is scheduled) queue immediately, or if the queue is empty - waits until the start time of the
See CC-3231. media item before pushing it.
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: try:
tn = telnetlib.Telnet(LS_HOST, LS_PORT) if media_item["start"] == self.last_end_time:
tn.write('source.skip\n') """
tn.write('exit\n') this media item is attached to the end of the last
tn.read_all() track, so let's push it now so that Liquidsoap can start playing
it immediately after (and prepare crossfades if need be).
"""
telnet_to_liquidsoap(media_item)
self.last_end_time = media_item["end"]
else:
"""
this media item does not start right after a current playing track.
We need to sleep, and then wake up when this track starts.
"""
self.sleep_until_start(media_item)
self.telnet_to_liquidsoap(media_item)
self.last_end_time = media_item["end"]
except Exception, e: except Exception, e:
logger.debug(e) return False
logger.debug('Could not connect to liquidsoap')
self.liquidsoap_state_play = False 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.
"""
def push_liquidsoap(self, pkey, schedule, playlists): mi_start = media_item['start'][0:19]
logger = logging.getLogger('push')
try:
playlist = playlists[pkey]
plstart = schedule[pkey]['start'][0:19]
#strptime returns struct_time in local time #strptime returns struct_time in local time
#mktime takes a time_struct and returns a floating point epoch_start = calendar.timegm(time.strptime(mi_start, '%Y-%m-%d-%H-%M-%S'))
#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().
epoch_start = calendar.timegm(time.strptime(plstart, '%Y-%m-%d-%H-%M-%S'))
#Return the time as a floating point number expressed in seconds since the epoch, in UTC. #Return the time as a floating point number expressed in seconds since the epoch, in UTC.
epoch_now = time.time() epoch_now = time.time()
logger.debug("Epoch start: %s" % epoch_start) self.logger.debug("Epoch start: %s" % epoch_start)
logger.debug("Epoch now: %s" % epoch_now) self.logger.debug("Epoch now: %s" % epoch_now)
sleep_time = epoch_start - epoch_now; sleep_time = epoch_start - epoch_now
if sleep_time < 0: if sleep_time < 0:
sleep_time = 0 sleep_time = 0
logger.debug('sleeping for %s s' % (sleep_time)) self.logger.debug('sleeping for %s s' % (sleep_time))
time.sleep(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 = telnetlib.Telnet(LS_HOST, LS_PORT)
#skip the currently playing song if any. #tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
logger.debug("source.skip\n")
tn.write("source.skip\n")
# Get any extra information for liquidsoap (which will be sent back to us) annotation = media_item['annotation']
liquidsoap_data = self.api_client.get_liquidsoap_data(pkey, schedule) msg = 'queue.push %s\n' % annotation.encode('utf-8')
tn.write(msg)
self.logger.debug(msg)
#Sending schedule table row id string. show_name = media_item['show_name']
logger.debug("vars.pypo_data %s\n"%(liquidsoap_data["schedule_id"])) msg = 'vars.show_name %s\n' % show_name.encode('utf-8')
tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8')) tn.write(msg)
self.logger.debug(msg)
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") tn.write("exit\n")
logger.debug(tn.read_all()) self.logger.debug(tn.read_all())
status = 1
except Exception, e:
logger.error('%s', e)
status = 0
return status
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,27 +169,43 @@ 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:
tnow = datetime.datetime.utcnow() tnow = datetime.datetime.utcnow()
@ -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()