Merge branch 'devel' of dev.sourcefabric.org:airtime into devel
This commit is contained in:
commit
f8e7d6eae3
|
@ -27,6 +27,7 @@ class ApiController extends Zend_Controller_Action
|
|||
->addActionContext('live-chat', 'json')
|
||||
->addActionContext('update-file-system-mount', 'json')
|
||||
->addActionContext('handle-watched-dir-missing', 'json')
|
||||
->addActionContext('rabbitmq-do-push', 'json')
|
||||
->initContext();
|
||||
}
|
||||
|
||||
|
@ -276,17 +277,19 @@ class ApiController extends Zend_Controller_Action
|
|||
|
||||
$api_key = $this->_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;
|
||||
}
|
||||
* */
|
||||
|
||||
PEAR::setErrorHandling(PEAR_ERROR_RETURN);
|
||||
|
||||
$result = Application_Model_Schedule::GetScheduledPlaylists();
|
||||
echo json_encode($result);
|
||||
$data = Application_Model_Schedule::GetScheduledPlaylists();
|
||||
echo json_encode($data, JSON_FORCE_OBJECT);
|
||||
}
|
||||
|
||||
public function notifyMediaItemStartPlayAction()
|
||||
|
@ -316,6 +319,7 @@ class ApiController extends Zend_Controller_Action
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public function notifyScheduleGroupPlayAction()
|
||||
{
|
||||
global $CC_CONFIG;
|
||||
|
@ -355,6 +359,7 @@ class ApiController extends Zend_Controller_Action
|
|||
exit;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public function recordedShowsAction()
|
||||
{
|
||||
|
@ -901,5 +906,26 @@ class ApiController extends Zend_Controller_Action
|
|||
$dir = base64_decode($request->getParam('dir'));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.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/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/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
|
||||
|
|
|
@ -197,7 +197,7 @@ class PlaylistController extends Zend_Controller_Action
|
|||
|
||||
public function addItemsAction()
|
||||
{
|
||||
$ids = $this->_getParam('ids');
|
||||
$ids = $this->_getParam('ids', array());
|
||||
$ids = (!is_array($ids)) ? array($ids) : $ids;
|
||||
$afterItem = $this->_getParam('afterItem', null);
|
||||
$addType = $this->_getParam('type', 'after');
|
||||
|
|
|
@ -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/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);
|
||||
|
||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||
|
@ -78,10 +101,12 @@ class ScheduleController extends Zend_Controller_Action
|
|||
|
||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||
$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;
|
||||
else
|
||||
}
|
||||
else {
|
||||
$editable = false;
|
||||
}
|
||||
|
||||
$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();
|
||||
$user = new Application_Model_User($userInfo->id);
|
||||
|
||||
if($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
|
||||
try{
|
||||
if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
|
||||
try {
|
||||
$showInstance = new Application_Model_ShowInstance($showInstanceId);
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e){
|
||||
$this->view->show_error = true;
|
||||
return false;
|
||||
}
|
||||
$error = $showInstance->moveShow($deltaDay, $deltaMin);
|
||||
}
|
||||
|
||||
if(isset($error))
|
||||
if (isset($error)) {
|
||||
$this->view->error = $error;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function resizeShowAction()
|
||||
|
@ -200,7 +225,7 @@ class ScheduleController extends Zend_Controller_Action
|
|||
&& !$instance->isRebroadcast()) {
|
||||
|
||||
$menu["schedule"] = array("name"=> "Add / Remove Content",
|
||||
"url" => "/showbuilder/index/");
|
||||
"url" => "/showbuilder/builder-dialog/");
|
||||
|
||||
$menu["clear"] = array("name"=> "Remove All Content", "icon" => "delete",
|
||||
"url" => "/schedule/clear-show");
|
||||
|
|
|
@ -9,6 +9,7 @@ class ShowbuilderController extends Zend_Controller_Action
|
|||
$ajaxContext->addActionContext('schedule-move', 'json')
|
||||
->addActionContext('schedule-add', 'json')
|
||||
->addActionContext('schedule-remove', 'json')
|
||||
->addActionContext('builder-dialog', 'json')
|
||||
->addActionContext('builder-feed', 'json')
|
||||
->initContext();
|
||||
}
|
||||
|
@ -53,11 +54,40 @@ class ShowbuilderController extends Zend_Controller_Action
|
|||
$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/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/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() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
|
|
|
@ -6,7 +6,7 @@ class Zend_Filter_ImageSize implements Zend_Filter_Interface {
|
|||
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) {
|
||||
throw new Zend_Filter_Exception('Can\'t load image: ' . $value);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class Application_Model_RabbitMq
|
|||
$EXCHANGE = 'airtime-pypo';
|
||||
$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'));
|
||||
|
||||
$channel->basic_publish($msg, $EXCHANGE);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
@ -318,6 +237,7 @@ class Application_Model_Schedule {
|
|||
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.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.album_title AS file_album_title, ft.length AS file_length
|
||||
|
@ -476,6 +396,120 @@ class Application_Model_Schedule {
|
|||
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)
|
||||
|
@ -485,7 +519,7 @@ class Application_Model_Schedule {
|
|||
* @param string $p_toDateTime
|
||||
* 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;
|
||||
|
||||
|
@ -546,7 +580,6 @@ class Application_Model_Schedule {
|
|||
|
||||
$starts = Application_Model_Schedule::AirtimeTimeToPypoTime($item["starts"]);
|
||||
$medias[$starts] = array(
|
||||
'row_id' => $item["id"],
|
||||
'id' => $storedFile->getGunid(),
|
||||
'uri' => $uri,
|
||||
'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]),
|
||||
|
@ -554,7 +587,6 @@ class Application_Model_Schedule {
|
|||
'fade_cross' => 0,
|
||||
'cue_in' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_in"]),
|
||||
'cue_out' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_out"]),
|
||||
'export_source' => 'scheduler',
|
||||
'start' => $starts,
|
||||
'end' => Application_Model_Schedule::AirtimeTimeToPypoTime($item["ends"])
|
||||
);
|
||||
|
|
|
@ -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.
|
||||
CcShowInstancesQuery::create()
|
||||
->filterByPrimaryKeys($affectedShowInstances)
|
||||
|
@ -383,11 +392,20 @@ class Application_Model_Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
foreach($showInstances as $instance) {
|
||||
foreach ($showInstances as $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.
|
||||
CcShowInstancesQuery::create()
|
||||
->filterByPrimaryKeys($showInstances)
|
||||
|
|
|
@ -125,10 +125,12 @@ class Application_Model_Show {
|
|||
}
|
||||
|
||||
$hours = $deltaMin/60;
|
||||
if($hours > 0)
|
||||
if ($hours > 0) {
|
||||
$hours = floor($hours);
|
||||
else
|
||||
}
|
||||
else {
|
||||
$hours = ceil($hours);
|
||||
}
|
||||
|
||||
$mins = abs($deltaMin%60);
|
||||
|
||||
|
@ -149,6 +151,28 @@ class Application_Model_Show {
|
|||
//do both the queries at once.
|
||||
$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();
|
||||
}
|
||||
|
||||
|
@ -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_RabbitMq::PushSchedule();
|
||||
return $showId;
|
||||
|
@ -1491,7 +1542,7 @@ class Application_Model_Show {
|
|||
$events = array();
|
||||
|
||||
$interval = $start->diff($end);
|
||||
$days = $interval->format('%a');
|
||||
$days = $interval->format('%a');
|
||||
|
||||
$shows = Application_Model_Show::getShows($start, $end);
|
||||
|
||||
|
@ -1508,10 +1559,9 @@ class Application_Model_Show {
|
|||
|
||||
if ($editable && (strtotime($today_timestamp) < strtotime($show["starts"]))) {
|
||||
$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;
|
||||
|
@ -1521,10 +1571,6 @@ class Application_Model_Show {
|
|||
{
|
||||
$event = array();
|
||||
|
||||
if($show["rebroadcast"]) {
|
||||
$event["disableResizing"] = true;
|
||||
}
|
||||
|
||||
$startDateTime = new DateTime($show["starts"], new DateTimeZone("UTC"));
|
||||
$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["endUnix"] = $endDateTime->format("U");
|
||||
$event["allDay"] = false;
|
||||
//$event["description"] = $show["description"];
|
||||
$event["showId"] = intval($show["show_id"]);
|
||||
$event["record"] = intval($show["record"]);
|
||||
$event["rebroadcast"] = intval($show["rebroadcast"]);
|
||||
|
||||
// get soundcloud_id
|
||||
if(!is_null($show["file_id"])){
|
||||
if (!is_null($show["file_id"])){
|
||||
$file = Application_Model_StoredFile::Recall($show["file_id"]);
|
||||
$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
|
||||
if($show["color"] != "") {
|
||||
if ($show["color"] != "") {
|
||||
$event["textColor"] = "#".$show["color"];
|
||||
}
|
||||
if($show["background_color"] != "") {
|
||||
if ($show["background_color"] != "") {
|
||||
$event["color"] = "#".$show["background_color"];
|
||||
}
|
||||
|
||||
foreach($options as $key=>$value) {
|
||||
foreach ($options as $key => $value) {
|
||||
$event[$key] = $value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
require_once 'formatters/LengthFormatter.php';
|
||||
require_once 'formatters/TimeFilledFormatter.php';
|
||||
|
||||
class Application_Model_ShowBuilder {
|
||||
|
||||
|
@ -30,7 +31,8 @@ class Application_Model_ShowBuilder {
|
|||
"cuein" => "",
|
||||
"cueout" => "",
|
||||
"fadein" => "",
|
||||
"fadeout" => ""
|
||||
"fadeout" => "",
|
||||
"current" => false,
|
||||
);
|
||||
|
||||
/*
|
||||
|
@ -47,37 +49,14 @@ class Application_Model_ShowBuilder {
|
|||
$this->epoch_now = time();
|
||||
}
|
||||
|
||||
private function formatTimeFilled($p_sec) {
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
//check to see if this row should be editable.
|
||||
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"));
|
||||
|
||||
//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) {
|
||||
|
||||
$showEndDT = new DateTime($p_item["si_ends"]);
|
||||
$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;
|
||||
$row["status"] = intval($p_item["sched_status"]);
|
||||
}
|
||||
|
||||
private function getRowTimestamp($p_item, &$row) {
|
||||
|
@ -121,6 +83,16 @@ class Application_Model_ShowBuilder {
|
|||
$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) {
|
||||
|
||||
$row = $this->defaultRowArray;
|
||||
|
@ -148,8 +120,8 @@ class Application_Model_ShowBuilder {
|
|||
private function makeScheduledItemRow($p_item) {
|
||||
$row = $this->defaultRowArray;
|
||||
|
||||
$this->isAllowed($p_item, $row);
|
||||
$this->getRowTimestamp($p_item, $row);
|
||||
$this->isAllowed($p_item, $row);
|
||||
|
||||
if (isset($p_item["sched_starts"])) {
|
||||
|
||||
|
@ -157,9 +129,19 @@ class Application_Model_ShowBuilder {
|
|||
$schedStartDT->setTimezone(new DateTimeZone($this->timezone));
|
||||
$schedEndDT = new DateTime($p_item["sched_ends"], new DateTimeZone("UTC"));
|
||||
$schedEndDT->setTimezone(new DateTimeZone($this->timezone));
|
||||
$showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
|
||||
|
||||
$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["instance"] = intval($p_item["si_id"]);
|
||||
$row["starts"] = $schedStartDT->format("H:i:s");
|
||||
|
@ -179,7 +161,10 @@ class Application_Model_ShowBuilder {
|
|||
|
||||
$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 {
|
||||
|
||||
$row["empty"] = true;
|
||||
|
@ -193,7 +178,6 @@ class Application_Model_ShowBuilder {
|
|||
private function makeFooterRow($p_item) {
|
||||
|
||||
$row = $this->defaultRowArray;
|
||||
$this->isAllowed($p_item, $row);
|
||||
$row["footer"] = true;
|
||||
|
||||
$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);
|
||||
$row["runtime"] = $runtime;
|
||||
$row["fRuntime"] = $this->formatTimeFilled($runtime);
|
||||
|
||||
$timeFilled = new TimeFilledFormatter($runtime);
|
||||
$row["fRuntime"] = $timeFilled->format();
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
|
|
@ -617,14 +617,14 @@ class Application_Model_ShowInstance {
|
|||
public function getTimeScheduledSecs()
|
||||
{
|
||||
$time_filled = $this->getTimeScheduled();
|
||||
return Application_Model_Schedule::WallTimeToMillisecs($time_filled) / 1000;
|
||||
return Application_Model_Playlist::playlistTimeToSeconds($time_filled);
|
||||
}
|
||||
|
||||
public function getDurationSecs()
|
||||
{
|
||||
$ends = $this->getShowInstanceEnd(null);
|
||||
$starts = $this->getShowInstanceStart(null);
|
||||
return $ends->format('U') - $starts->format('U');
|
||||
return intval($ends->format('U')) - intval($starts->format('U'));
|
||||
}
|
||||
|
||||
public function getPercentScheduled()
|
||||
|
|
|
@ -35,39 +35,6 @@ class CcPlaylistcontents extends BaseCcPlaylistcontents {
|
|||
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.
|
||||
|
@ -124,88 +91,4 @@ class CcPlaylistcontents extends BaseCcPlaylistcontents {
|
|||
return $this;
|
||||
} // 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
|
||||
|
|
|
@ -15,11 +15,6 @@
|
|||
*/
|
||||
class CcSchedule extends BaseCcSchedule {
|
||||
|
||||
public function getDbClipLength($format = 'H:i:s.u')
|
||||
{
|
||||
return parent::getDbClipLength($format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [optionally formatted] temporal [starts] column value.
|
||||
*
|
||||
|
@ -104,28 +99,6 @@ class CcSchedule extends BaseCcSchedule {
|
|||
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.
|
||||
|
@ -182,89 +155,6 @@ class CcSchedule extends BaseCcSchedule {
|
|||
return $this;
|
||||
} // 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.
|
||||
*
|
||||
|
|
|
@ -107,4 +107,33 @@ class CcShowInstances extends BaseCcShowInstances {
|
|||
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
|
||||
|
|
|
@ -42,9 +42,9 @@ class CcPlaylistcontentsTableMap extends TableMap {
|
|||
$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->addColumn('POSITION', 'DbPosition', 'INTEGER', false, null, null);
|
||||
$this->addColumn('CLIPLENGTH', 'DbCliplength', 'TIME', false, null, '00:00:00');
|
||||
$this->addColumn('CUEIN', 'DbCuein', 'TIME', false, null, '00:00:00');
|
||||
$this->addColumn('CUEOUT', 'DbCueout', 'TIME', false, null, '00:00:00');
|
||||
$this->addColumn('CLIPLENGTH', 'DbCliplength', 'VARCHAR', false, null, '00:00:00');
|
||||
$this->addColumn('CUEIN', 'DbCuein', 'VARCHAR', 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('FADEOUT', 'DbFadeout', 'TIME', false, null, '00:00:00');
|
||||
// validators
|
||||
|
|
|
@ -42,13 +42,14 @@ class CcScheduleTableMap extends TableMap {
|
|||
$this->addColumn('STARTS', 'DbStarts', '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->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_OUT', 'DbFadeOut', 'TIME', false, null, '00:00:00');
|
||||
$this->addColumn('CUE_IN', 'DbCueIn', 'TIME', false, null, '00:00:00');
|
||||
$this->addColumn('CUE_OUT', 'DbCueOut', 'TIME', false, null, '00:00:00');
|
||||
$this->addColumn('CUE_IN', 'DbCueIn', 'VARCHAR', 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->addForeignKey('INSTANCE_ID', 'DbInstanceId', 'INTEGER', 'cc_show_instances', 'ID', true, null, null);
|
||||
$this->addColumn('STATUS', 'DbStatus', 'SMALLINT', true, null, 1);
|
||||
// validators
|
||||
} // initialize()
|
||||
|
||||
|
|
|
@ -176,102 +176,33 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the [optionally formatted] temporal [cliplength] column value.
|
||||
* Get the [cliplength] column value.
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* @return string
|
||||
*/
|
||||
public function getDbCliplength($format = '%X')
|
||||
public function getDbCliplength()
|
||||
{
|
||||
if ($this->cliplength === null) {
|
||||
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);
|
||||
}
|
||||
return $this->cliplength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [optionally formatted] temporal [cuein] column value.
|
||||
* Get the [cuein] column value.
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* @return string
|
||||
*/
|
||||
public function getDbCuein($format = '%X')
|
||||
public function getDbCuein()
|
||||
{
|
||||
if ($this->cuein === null) {
|
||||
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);
|
||||
}
|
||||
return $this->cuein;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [optionally formatted] temporal [cueout] column value.
|
||||
* Get the [cueout] column value.
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* @return string
|
||||
*/
|
||||
public function getDbCueout($format = '%X')
|
||||
public function getDbCueout()
|
||||
{
|
||||
if ($this->cueout === null) {
|
||||
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);
|
||||
}
|
||||
return $this->cueout;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -429,151 +360,61 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
|
|||
} // 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
|
||||
* be treated as NULL for temporal objects.
|
||||
* @param string $v new value
|
||||
* @return CcPlaylistcontents The current object (for fluent API support)
|
||||
*/
|
||||
public function setDbCliplength($v)
|
||||
{
|
||||
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
|
||||
// -- which is unexpected, to say the least.
|
||||
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 ($v !== null) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
|
||||
if ( $this->cliplength !== null || $dt !== null ) {
|
||||
// (nested ifs are a little easier to read in this case)
|
||||
|
||||
$currNorm = ($this->cliplength !== null && $tmpDt = new DateTime($this->cliplength)) ? $tmpDt->format('H:i:s') : null;
|
||||
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
|
||||
|
||||
if ( ($currNorm !== $newNorm) // normalized values don't match
|
||||
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
|
||||
)
|
||||
{
|
||||
$this->cliplength = ($dt ? $dt->format('H:i:s') : null);
|
||||
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
|
||||
}
|
||||
} // if either are not null
|
||||
if ($this->cliplength !== $v || $this->isNew()) {
|
||||
$this->cliplength = $v;
|
||||
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
|
||||
}
|
||||
|
||||
return $this;
|
||||
} // 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
|
||||
* be treated as NULL for temporal objects.
|
||||
* @param string $v new value
|
||||
* @return CcPlaylistcontents The current object (for fluent API support)
|
||||
*/
|
||||
public function setDbCuein($v)
|
||||
{
|
||||
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
|
||||
// -- which is unexpected, to say the least.
|
||||
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 ($v !== null) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
|
||||
if ( $this->cuein !== null || $dt !== null ) {
|
||||
// (nested ifs are a little easier to read in this case)
|
||||
|
||||
$currNorm = ($this->cuein !== null && $tmpDt = new DateTime($this->cuein)) ? $tmpDt->format('H:i:s') : null;
|
||||
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
|
||||
|
||||
if ( ($currNorm !== $newNorm) // normalized values don't match
|
||||
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
|
||||
)
|
||||
{
|
||||
$this->cuein = ($dt ? $dt->format('H:i:s') : null);
|
||||
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
|
||||
}
|
||||
} // if either are not null
|
||||
if ($this->cuein !== $v || $this->isNew()) {
|
||||
$this->cuein = $v;
|
||||
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
|
||||
}
|
||||
|
||||
return $this;
|
||||
} // 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
|
||||
* be treated as NULL for temporal objects.
|
||||
* @param string $v new value
|
||||
* @return CcPlaylistcontents The current object (for fluent API support)
|
||||
*/
|
||||
public function setDbCueout($v)
|
||||
{
|
||||
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
|
||||
// -- which is unexpected, to say the least.
|
||||
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 ($v !== null) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
|
||||
if ( $this->cueout !== null || $dt !== null ) {
|
||||
// (nested ifs are a little easier to read in this case)
|
||||
|
||||
$currNorm = ($this->cueout !== null && $tmpDt = new DateTime($this->cueout)) ? $tmpDt->format('H:i:s') : null;
|
||||
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
|
||||
|
||||
if ( ($currNorm !== $newNorm) // normalized values don't match
|
||||
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
|
||||
)
|
||||
{
|
||||
$this->cueout = ($dt ? $dt->format('H:i:s') : null);
|
||||
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
|
||||
}
|
||||
} // if either are not null
|
||||
if ($this->cueout !== $v || $this->isNew()) {
|
||||
$this->cueout = $v;
|
||||
$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
|
||||
}
|
||||
|
||||
return $this;
|
||||
} // setDbCueout()
|
||||
|
|
|
@ -282,29 +282,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
|
|||
/**
|
||||
* Filter the query on the cliplength column
|
||||
*
|
||||
* @param string|array $dbCliplength The value to use as filter.
|
||||
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
|
||||
* @param string $dbCliplength The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return CcPlaylistcontentsQuery The current query, for fluid interface
|
||||
*/
|
||||
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;
|
||||
} elseif (preg_match('/[\%\*]/', $dbCliplength)) {
|
||||
$dbCliplength = str_replace('*', '%', $dbCliplength);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
return $this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength, $comparison);
|
||||
|
@ -313,29 +304,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
|
|||
/**
|
||||
* Filter the query on the cuein column
|
||||
*
|
||||
* @param string|array $dbCuein The value to use as filter.
|
||||
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
|
||||
* @param string $dbCuein The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return CcPlaylistcontentsQuery The current query, for fluid interface
|
||||
*/
|
||||
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;
|
||||
} elseif (preg_match('/[\%\*]/', $dbCuein)) {
|
||||
$dbCuein = str_replace('*', '%', $dbCuein);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein, $comparison);
|
||||
|
@ -344,29 +326,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
|
|||
/**
|
||||
* Filter the query on the cueout column
|
||||
*
|
||||
* @param string|array $dbCueout The value to use as filter.
|
||||
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
|
||||
* @param string $dbCueout The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return CcPlaylistcontentsQuery The current query, for fluid interface
|
||||
*/
|
||||
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;
|
||||
} elseif (preg_match('/[\%\*]/', $dbCueout)) {
|
||||
$dbCueout = str_replace('*', '%', $dbCueout);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout, $comparison);
|
||||
|
|
|
@ -96,6 +96,13 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -137,6 +144,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
$this->cue_in = '00:00:00';
|
||||
$this->cue_out = '00:00:00';
|
||||
$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.
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* @return string
|
||||
*/
|
||||
public function getDbClipLength($format = '%X')
|
||||
public function getDbClipLength()
|
||||
{
|
||||
if ($this->clip_length === null) {
|
||||
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);
|
||||
}
|
||||
return $this->clip_length;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* @return string
|
||||
*/
|
||||
public function getDbCueIn($format = '%X')
|
||||
public function getDbCueIn()
|
||||
{
|
||||
if ($this->cue_in === null) {
|
||||
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);
|
||||
}
|
||||
return $this->cue_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [optionally formatted] temporal [cue_out] column value.
|
||||
* Get the [cue_out] column value.
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* @return string
|
||||
*/
|
||||
public function getDbCueOut($format = '%X')
|
||||
public function getDbCueOut()
|
||||
{
|
||||
if ($this->cue_out === null) {
|
||||
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);
|
||||
}
|
||||
return $this->cue_out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -420,6 +359,16 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
return $this->instance_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [status] column value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDbStatus()
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of [id] column.
|
||||
*
|
||||
|
@ -563,51 +512,21 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
} // 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
|
||||
* be treated as NULL for temporal objects.
|
||||
* @param string $v new value
|
||||
* @return CcSchedule The current object (for fluent API support)
|
||||
*/
|
||||
public function setDbClipLength($v)
|
||||
{
|
||||
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
|
||||
// -- which is unexpected, to say the least.
|
||||
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 ($v !== null) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
|
||||
if ( $this->clip_length !== null || $dt !== null ) {
|
||||
// (nested ifs are a little easier to read in this case)
|
||||
|
||||
$currNorm = ($this->clip_length !== null && $tmpDt = new DateTime($this->clip_length)) ? $tmpDt->format('H:i:s') : null;
|
||||
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
|
||||
|
||||
if ( ($currNorm !== $newNorm) // normalized values don't match
|
||||
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
|
||||
)
|
||||
{
|
||||
$this->clip_length = ($dt ? $dt->format('H:i:s') : null);
|
||||
$this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
|
||||
}
|
||||
} // if either are not null
|
||||
if ($this->clip_length !== $v || $this->isNew()) {
|
||||
$this->clip_length = $v;
|
||||
$this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
|
||||
}
|
||||
|
||||
return $this;
|
||||
} // setDbClipLength()
|
||||
|
@ -713,101 +632,41 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
} // 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
|
||||
* be treated as NULL for temporal objects.
|
||||
* @param string $v new value
|
||||
* @return CcSchedule The current object (for fluent API support)
|
||||
*/
|
||||
public function setDbCueIn($v)
|
||||
{
|
||||
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
|
||||
// -- which is unexpected, to say the least.
|
||||
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 ($v !== null) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
|
||||
if ( $this->cue_in !== null || $dt !== null ) {
|
||||
// (nested ifs are a little easier to read in this case)
|
||||
|
||||
$currNorm = ($this->cue_in !== null && $tmpDt = new DateTime($this->cue_in)) ? $tmpDt->format('H:i:s') : null;
|
||||
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
|
||||
|
||||
if ( ($currNorm !== $newNorm) // normalized values don't match
|
||||
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
|
||||
)
|
||||
{
|
||||
$this->cue_in = ($dt ? $dt->format('H:i:s') : null);
|
||||
$this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
|
||||
}
|
||||
} // if either are not null
|
||||
if ($this->cue_in !== $v || $this->isNew()) {
|
||||
$this->cue_in = $v;
|
||||
$this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
|
||||
}
|
||||
|
||||
return $this;
|
||||
} // 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
|
||||
* be treated as NULL for temporal objects.
|
||||
* @param string $v new value
|
||||
* @return CcSchedule The current object (for fluent API support)
|
||||
*/
|
||||
public function setDbCueOut($v)
|
||||
{
|
||||
// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
|
||||
// -- which is unexpected, to say the least.
|
||||
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 ($v !== null) {
|
||||
$v = (string) $v;
|
||||
}
|
||||
|
||||
if ( $this->cue_out !== null || $dt !== null ) {
|
||||
// (nested ifs are a little easier to read in this case)
|
||||
|
||||
$currNorm = ($this->cue_out !== null && $tmpDt = new DateTime($this->cue_out)) ? $tmpDt->format('H:i:s') : null;
|
||||
$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
|
||||
|
||||
if ( ($currNorm !== $newNorm) // normalized values don't match
|
||||
|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
|
||||
)
|
||||
{
|
||||
$this->cue_out = ($dt ? $dt->format('H:i:s') : null);
|
||||
$this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
|
||||
}
|
||||
} // if either are not null
|
||||
if ($this->cue_out !== $v || $this->isNew()) {
|
||||
$this->cue_out = $v;
|
||||
$this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
|
||||
}
|
||||
|
||||
return $this;
|
||||
} // setDbCueOut()
|
||||
|
@ -856,6 +715,26 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
return $this;
|
||||
} // 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.
|
||||
*
|
||||
|
@ -890,6 +769,10 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($this->status !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, everything was equal, so return TRUE
|
||||
return true;
|
||||
} // 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->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->status = ($row[$startcol + 11] !== null) ? (int) $row[$startcol + 11] : null;
|
||||
$this->resetModified();
|
||||
|
||||
$this->setNew(false);
|
||||
|
@ -931,7 +815,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
$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) {
|
||||
throw new PropelException("Error populating CcSchedule object", $e);
|
||||
|
@ -1310,6 +1194,9 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
case 10:
|
||||
return $this->getDbInstanceId();
|
||||
break;
|
||||
case 11:
|
||||
return $this->getDbStatus();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
break;
|
||||
|
@ -1345,6 +1232,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
$keys[8] => $this->getDbCueOut(),
|
||||
$keys[9] => $this->getDbMediaItemPlayed(),
|
||||
$keys[10] => $this->getDbInstanceId(),
|
||||
$keys[11] => $this->getDbStatus(),
|
||||
);
|
||||
if ($includeForeignObjects) {
|
||||
if (null !== $this->aCcShowInstances) {
|
||||
|
@ -1417,6 +1305,9 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
case 10:
|
||||
$this->setDbInstanceId($value);
|
||||
break;
|
||||
case 11:
|
||||
$this->setDbStatus($value);
|
||||
break;
|
||||
} // 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[9], $arr)) $this->setDbMediaItemPlayed($arr[$keys[9]]);
|
||||
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::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::STATUS)) $criteria->add(CcSchedulePeer::STATUS, $this->status);
|
||||
|
||||
return $criteria;
|
||||
}
|
||||
|
@ -1545,6 +1438,7 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent
|
|||
$copyObj->setDbCueOut($this->cue_out);
|
||||
$copyObj->setDbMediaItemPlayed($this->media_item_played);
|
||||
$copyObj->setDbInstanceId($this->instance_id);
|
||||
$copyObj->setDbStatus($this->status);
|
||||
|
||||
$copyObj->setNew(true);
|
||||
$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->media_item_played = null;
|
||||
$this->instance_id = null;
|
||||
$this->status = null;
|
||||
$this->alreadyInSave = false;
|
||||
$this->alreadyInValidation = false;
|
||||
$this->clearAllReferences();
|
||||
|
|
|
@ -26,7 +26,7 @@ abstract class BaseCcSchedulePeer {
|
|||
const TM_CLASS = 'CcScheduleTableMap';
|
||||
|
||||
/** The total number of columns. */
|
||||
const NUM_COLUMNS = 11;
|
||||
const NUM_COLUMNS = 12;
|
||||
|
||||
/** The number of lazy-loaded columns. */
|
||||
const NUM_LAZY_LOAD_COLUMNS = 0;
|
||||
|
@ -64,6 +64,9 @@ abstract class BaseCcSchedulePeer {
|
|||
/** the column name for the INSTANCE_ID field */
|
||||
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.
|
||||
* 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'
|
||||
*/
|
||||
private static $fieldNames = array (
|
||||
BasePeer::TYPE_PHPNAME => 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', ),
|
||||
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_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_FIELDNAME => array ('id', 'starts', 'ends', 'file_id', 'clip_length', 'fade_in', 'fade_out', 'cue_in', 'cue_out', 'media_item_played', 'instance_id', ),
|
||||
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, )
|
||||
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', '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, 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', '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', 'status', ),
|
||||
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
|
||||
*/
|
||||
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_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_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_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_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_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 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, '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, 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, '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, 'status' => 11, ),
|
||||
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::MEDIA_ITEM_PLAYED);
|
||||
$criteria->addSelectColumn(CcSchedulePeer::INSTANCE_ID);
|
||||
$criteria->addSelectColumn(CcSchedulePeer::STATUS);
|
||||
} else {
|
||||
$criteria->addSelectColumn($alias . '.ID');
|
||||
$criteria->addSelectColumn($alias . '.STARTS');
|
||||
|
@ -195,6 +199,7 @@ abstract class BaseCcSchedulePeer {
|
|||
$criteria->addSelectColumn($alias . '.CUE_OUT');
|
||||
$criteria->addSelectColumn($alias . '.MEDIA_ITEM_PLAYED');
|
||||
$criteria->addSelectColumn($alias . '.INSTANCE_ID');
|
||||
$criteria->addSelectColumn($alias . '.STATUS');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* @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 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 groupByDbStarts() Group by the starts column
|
||||
|
@ -29,6 +30,7 @@
|
|||
* @method CcScheduleQuery groupByDbCueOut() Group by the cue_out column
|
||||
* @method CcScheduleQuery groupByDbMediaItemPlayed() Group by the media_item_played 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 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 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 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 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 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 findByDbStatus(int $status) Return CcSchedule objects filtered by the status column
|
||||
*
|
||||
* @package propel.generator.airtime.om
|
||||
*/
|
||||
|
@ -290,29 +294,20 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
|
|||
/**
|
||||
* Filter the query on the clip_length column
|
||||
*
|
||||
* @param string|array $dbClipLength The value to use as filter.
|
||||
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
|
||||
* @param string $dbClipLength The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return CcScheduleQuery The current query, for fluid interface
|
||||
*/
|
||||
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;
|
||||
} elseif (preg_match('/[\%\*]/', $dbClipLength)) {
|
||||
$dbClipLength = str_replace('*', '%', $dbClipLength);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
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
|
||||
*
|
||||
* @param string|array $dbCueIn The value to use as filter.
|
||||
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
|
||||
* @param string $dbCueIn The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return CcScheduleQuery The current query, for fluid interface
|
||||
*/
|
||||
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;
|
||||
} elseif (preg_match('/[\%\*]/', $dbCueIn)) {
|
||||
$dbCueIn = str_replace('*', '%', $dbCueIn);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
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
|
||||
*
|
||||
* @param string|array $dbCueOut The value to use as filter.
|
||||
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
|
||||
* @param string $dbCueOut The value to use as filter.
|
||||
* Accepts wildcards (* and % trigger a LIKE)
|
||||
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
|
||||
*
|
||||
* @return CcScheduleQuery The current query, for fluid interface
|
||||
*/
|
||||
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;
|
||||
} elseif (preg_match('/[\%\*]/', $dbCueOut)) {
|
||||
$dbCueOut = str_replace('*', '%', $dbCueOut);
|
||||
$comparison = Criteria::LIKE;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
<br /><br /><center>View script for controller <b>Library</b> and script/action name <b>delete</b></center>
|
|
@ -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>
|
|
@ -1 +0,0 @@
|
|||
<br /><br /><center>View script for controller <b>Library</b> and script/action name <b>search</b></center>
|
|
@ -1,3 +0,0 @@
|
|||
<?php
|
||||
|
||||
echo $this->partialLoop('library/libraryTablePartial.phtml', $this->files);
|
|
@ -4,11 +4,11 @@ if (count($items)) : ?>
|
|||
|
||||
<?php $i = 0; ?>
|
||||
<?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">
|
||||
<a href="javascript:void(0);" class="big_play"
|
||||
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">
|
||||
<span class="spl_playlength"><?php echo $item["cliplength"] ?></span>
|
||||
|
|
|
@ -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>
|
|
@ -237,9 +237,9 @@
|
|||
<column name="playlist_id" phpName="DbPlaylistId" 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="cliplength" phpName="DbCliplength" type="TIME" required="false" defaultValue="00:00:00"/>
|
||||
<column name="cuein" phpName="DbCuein" type="TIME" required="false" defaultValue="00:00:00"/>
|
||||
<column name="cueout" phpName="DbCueout" 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="VARCHAR" sqlType="interval" 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="fadeout" phpName="DbFadeout" type="TIME" required="false" defaultValue="00:00:00"/>
|
||||
<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="ends" phpName="DbEnds" type="TIMESTAMP" required="true"/>
|
||||
<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_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_out" phpName="DbCueOut" 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="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
|
||||
<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="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.
|
||||
We will do delete them in some cases (when editing a show and changing the repeating days of the week
|
||||
for example. \
|
||||
|
|
|
@ -315,9 +315,9 @@ CREATE TABLE "cc_playlistcontents"
|
|||
"playlist_id" INTEGER,
|
||||
"file_id" INTEGER,
|
||||
"position" INTEGER,
|
||||
"cliplength" TIME default '00:00:00',
|
||||
"cuein" TIME default '00:00:00',
|
||||
"cueout" TIME default '00:00:00',
|
||||
"cliplength" interval default '00:00:00',
|
||||
"cuein" interval default '00:00:00',
|
||||
"cueout" interval default '00:00:00',
|
||||
"fadein" TIME default '00:00:00',
|
||||
"fadeout" TIME default '00:00:00',
|
||||
PRIMARY KEY ("id")
|
||||
|
@ -364,13 +364,14 @@ CREATE TABLE "cc_schedule"
|
|||
"starts" TIMESTAMP NOT NULL,
|
||||
"ends" TIMESTAMP NOT NULL,
|
||||
"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_out" TIME default '00:00:00',
|
||||
"cue_in" TIME default '00:00:00',
|
||||
"cue_out" TIME default '00:00:00',
|
||||
"cue_in" interval default '00:00:00',
|
||||
"cue_out" interval default '00:00:00',
|
||||
"media_item_played" BOOLEAN default 'f',
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"status" INT2 default 1 NOT NULL,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ button.ColVis_Button::-moz-focus-inner {
|
|||
|
||||
div.ColVis_collectionBackground {
|
||||
background-color: black;
|
||||
z-index: 996;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
div.ColVis_collection {
|
||||
|
@ -39,7 +39,7 @@ div.ColVis_collection {
|
|||
background-color: #999;
|
||||
padding: 3px;
|
||||
border: 1px solid #ccc;
|
||||
z-index: 998;
|
||||
z-index: 1005;
|
||||
}
|
||||
|
||||
div.ColVis_collection button.ColVis_Button {
|
||||
|
@ -51,7 +51,7 @@ div.ColVis_collection button.ColVis_Button {
|
|||
|
||||
div.ColVis_catcher {
|
||||
position: absolute;
|
||||
z-index: 997;
|
||||
z-index: 1004;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
|
|
|
@ -79,3 +79,8 @@
|
|||
.library_year {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.library_sr,
|
||||
.library_bitrate {
|
||||
text-align: right;
|
||||
}
|
||||
|
|
|
@ -457,7 +457,5 @@ div.helper li {
|
|||
}
|
||||
|
||||
li.spl_empty {
|
||||
text-align: center;
|
||||
height: 56px;
|
||||
border:2px dashed black;
|
||||
}
|
|
@ -34,4 +34,17 @@ tr.cursor-selected-row .marker {
|
|||
|
||||
.sb-over {
|
||||
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;
|
||||
}
|
|
@ -601,6 +601,7 @@ dl.inline-list dd {
|
|||
}
|
||||
|
||||
.dataTables_info {
|
||||
float: left;
|
||||
padding: 8px 0 0 8px;
|
||||
font-size:12px;
|
||||
color:#555555;
|
||||
|
@ -608,6 +609,7 @@ dl.inline-list dd {
|
|||
}
|
||||
|
||||
.dataTables_paginate {
|
||||
float: right;
|
||||
padding: 8px 0 8px 8px;
|
||||
}
|
||||
.dataTables_paginate .ui-button {
|
||||
|
@ -618,7 +620,7 @@ dl.inline-list dd {
|
|||
}
|
||||
.dataTables_filter input {
|
||||
background: url("images/search_auto_bg.png") no-repeat scroll 0 0 #DDDDDD;
|
||||
width: 60%;
|
||||
width: 55%;
|
||||
border: 1px solid #5B5B5B;
|
||||
margin-left: -8px;
|
||||
padding: 4px 3px 4px 25px;
|
||||
|
|
|
@ -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 || {}));
|
|
@ -7,6 +7,24 @@ var AIRTIME = (function(AIRTIME){
|
|||
|
||||
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 ) {
|
||||
var $nRow = $(nRow);
|
||||
|
@ -63,22 +81,22 @@ var AIRTIME = (function(AIRTIME){
|
|||
*/
|
||||
mod.setupLibraryToolbar = function( oLibTable ) {
|
||||
var aButtons,
|
||||
fnResetCol,
|
||||
fnAddSelectedItems;
|
||||
|
||||
fnAddSelectedItems = function() {
|
||||
var oLibTT = TableTools.fnGetInstance('library_display'),
|
||||
aData = oLibTT.fnGetSelectedData(),
|
||||
item,
|
||||
i,
|
||||
temp,
|
||||
length,
|
||||
aMediaIds = [];
|
||||
|
||||
//process selected files/playlists.
|
||||
for (item in aData) {
|
||||
temp = aData[item];
|
||||
if (temp !== null && temp.hasOwnProperty('id') && temp.ftype === "audioclip") {
|
||||
for (i = 0, length = aData.length; i < length; i++) {
|
||||
temp = aData[i];
|
||||
if (temp.ftype === "audioclip") {
|
||||
aMediaIds.push(temp.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AIRTIME.playlist.fnAddItems(aMediaIds, undefined, 'after');
|
||||
|
@ -88,8 +106,8 @@ var AIRTIME = (function(AIRTIME){
|
|||
//[1] = id
|
||||
//[2] = enabled
|
||||
//[3] = click event
|
||||
aButtons = [["Delete", "library_group_delete", true, AIRTIME.library.fnDeleteSelectedItems],
|
||||
["Add", "library_group_add", true, fnAddSelectedItems]];
|
||||
aButtons = [["Delete", "library_group_delete", false, AIRTIME.library.fnDeleteSelectedItems],
|
||||
["Add", "library_group_add", false, fnAddSelectedItems]];
|
||||
|
||||
addToolBarButtonsLibrary(aButtons);
|
||||
};
|
||||
|
|
|
@ -8,6 +8,24 @@ var AIRTIME = (function(AIRTIME){
|
|||
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 ) {
|
||||
var $nRow = $(nRow);
|
||||
|
||||
|
@ -21,7 +39,6 @@ var AIRTIME = (function(AIRTIME){
|
|||
$('#library_display tr:not(:first)').draggable({
|
||||
helper: function(){
|
||||
var selected = $('#library_display tr:not(:first) input:checked').parents('tr'),
|
||||
aItems = [],
|
||||
container,
|
||||
thead = $("#show_builder_table thead"),
|
||||
colspan = thead.find("th").length,
|
||||
|
@ -34,10 +51,10 @@ var AIRTIME = (function(AIRTIME){
|
|||
}
|
||||
|
||||
if (selected.length === 1) {
|
||||
message = "Moving "+selected.length+" Item."
|
||||
message = "Moving "+selected.length+" Item.";
|
||||
}
|
||||
else {
|
||||
message = "Moving "+selected.length+" Items."
|
||||
message = "Moving "+selected.length+" Items.";
|
||||
}
|
||||
|
||||
container = $('<div/>').attr('id', 'draggingContainer')
|
||||
|
@ -61,8 +78,6 @@ var AIRTIME = (function(AIRTIME){
|
|||
|
||||
mod.setupLibraryToolbar = function(oLibTable) {
|
||||
var aButtons,
|
||||
fnTest,
|
||||
fnResetCol,
|
||||
fnAddSelectedItems,
|
||||
|
||||
fnAddSelectedItems = function() {
|
||||
|
@ -75,7 +90,7 @@ var AIRTIME = (function(AIRTIME){
|
|||
aSchedIds = [];
|
||||
|
||||
//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];
|
||||
aMediaIds.push({"id": temp.id, "type": temp.ftype});
|
||||
}
|
||||
|
@ -93,12 +108,13 @@ var AIRTIME = (function(AIRTIME){
|
|||
|
||||
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
|
||||
};
|
||||
|
||||
//[0] = button text
|
||||
//[1] = id
|
||||
//[2] = enabled
|
||||
//[3] = click event
|
||||
aButtons = [["Delete", "library_group_delete", true, AIRTIME.library.fnDeleteSelectedItems],
|
||||
["Add", "library_group_add", true, fnAddSelectedItems]];
|
||||
aButtons = [["Delete", "library_group_delete", false, AIRTIME.library.fnDeleteSelectedItems],
|
||||
["Add", "library_group_add", false, fnAddSelectedItems]];
|
||||
|
||||
addToolBarButtonsLibrary(aButtons);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var AIRTIME = (function(AIRTIME){
|
||||
var mod;
|
||||
var mod,
|
||||
libraryInit;
|
||||
|
||||
if (AIRTIME.library === undefined) {
|
||||
AIRTIME.library = {};
|
||||
|
@ -13,6 +14,10 @@ var AIRTIME = (function(AIRTIME){
|
|||
$.post("/library/delete",
|
||||
{"format": "json", "media": aMedia},
|
||||
function(json){
|
||||
if (json.message !== undefined) {
|
||||
alert(json.message);
|
||||
}
|
||||
|
||||
oLibTT.fnSelectNone();
|
||||
oLibTable.fnDraw();
|
||||
});
|
||||
|
@ -36,6 +41,408 @@ var AIRTIME = (function(AIRTIME){
|
|||
AIRTIME.library.fnDeleteItems(aMedia);
|
||||
};
|
||||
|
||||
libraryInit = function() {
|
||||
var oTable;
|
||||
|
||||
oTable = $('#library_display').dataTable( {
|
||||
|
||||
"aoColumns": [
|
||||
/* Checkbox */ {"sTitle": "<input type='checkbox' name='pl_cb_all'>", "mDataProp": "checkbox", "bSortable": false, "bSearchable": false, "sWidth": "25px", "sClass": "library_checkbox"},
|
||||
/* Type */ {"sTitle": "", "mDataProp": "image", "bSearchable": false, "sWidth": "25px", "sClass": "library_type", "iDataSort": 2},
|
||||
/* ftype */ {"sTitle": "", "mDataProp": "ftype", "bSearchable": false, "bVisible": false},
|
||||
/* Title */ {"sTitle": "Title", "mDataProp": "track_title", "sClass": "library_title"},
|
||||
/* Creator */ {"sTitle": "Creator", "mDataProp": "artist_name", "sClass": "library_creator"},
|
||||
/* Album */ {"sTitle": "Album", "mDataProp": "album_title", "sClass": "library_album"},
|
||||
/* Genre */ {"sTitle": "Genre", "mDataProp": "genre", "sClass": "library_genre"},
|
||||
/* Year */ {"sTitle": "Year", "mDataProp": "year", "sClass": "library_year", "sWidth": "60px"},
|
||||
/* Length */ {"sTitle": "Length", "mDataProp": "length", "sClass": "library_length", "sWidth": "80px"},
|
||||
/* Upload Time */ {"sTitle": "Uploaded", "mDataProp": "utime", "sClass": "library_upload_time"},
|
||||
/* Last Modified */ {"sTitle": "Last Modified", "mDataProp": "mtime", "bVisible": false, "sClass": "library_modified_time"},
|
||||
/* Track Number */ {"sTitle": "Track", "mDataProp": "track_number", "bSearchable": false, "bVisible": false, "sClass": "library_track"},
|
||||
/* Mood */ {"sTitle": "Mood", "mDataProp": "mood", "bSearchable": false, "bVisible": false, "sClass": "library_mood"},
|
||||
/* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"},
|
||||
/* Composer */ {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer"},
|
||||
/* Website */ {"sTitle": "Website", "mDataProp": "info_url", "bSearchable": false, "bVisible": false, "sClass": "library_url"},
|
||||
/* Bit Rate */ {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate", "sWidth": "80px"},
|
||||
/* Sample Rate */ {"sTitle": "Sample", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr", "sWidth": "80px"},
|
||||
/* ISRC Number */ {"sTitle": "ISRC", "mDataProp": "isrc_number", "bSearchable": false, "bVisible": false, "sClass": "library_isrc"},
|
||||
/* Encoded */ {"sTitle": "Encoded", "mDataProp": "encoded_by", "bSearchable": false, "bVisible": false, "sClass": "library_encoded"},
|
||||
/* Label */ {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label"},
|
||||
/* Copyright */ {"sTitle": "Copyright", "mDataProp": "copyright", "bSearchable": false, "bVisible": false, "sClass": "library_copyright"},
|
||||
/* Mime */ {"sTitle": "Mime", "mDataProp": "mime", "bSearchable": false, "bVisible": false, "sClass": "library_mime"},
|
||||
/* Language */ {"sTitle": "Language", "mDataProp": "language", "bSearchable": false, "bVisible": false, "sClass": "library_language"}
|
||||
],
|
||||
|
||||
"bProcessing": true,
|
||||
"bServerSide": true,
|
||||
|
||||
"bStateSave": true,
|
||||
"fnStateSaveParams": function (oSettings, oData) {
|
||||
//remove oData components we don't want to save.
|
||||
delete oData.oSearch;
|
||||
delete oData.aoSearchCols;
|
||||
},
|
||||
"fnStateSave": function (oSettings, oData) {
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/set-library-datatable",
|
||||
type: "POST",
|
||||
data: {settings : oData, format: "json"},
|
||||
dataType: "json",
|
||||
success: function(){},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
},
|
||||
"fnStateLoad": function (oSettings) {
|
||||
var o;
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/get-library-datatable",
|
||||
type: "GET",
|
||||
data: {format: "json"},
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json){
|
||||
o = json.settings;
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
|
||||
return o;
|
||||
},
|
||||
"fnStateLoadParams": function (oSettings, oData) {
|
||||
var i,
|
||||
length,
|
||||
a = oData.abVisCols;
|
||||
|
||||
//putting serialized data back into the correct js type to make
|
||||
//sure everything works properly.
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = (a[i] === "true") ? true : false;
|
||||
}
|
||||
|
||||
a = oData.ColReorder;
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = parseInt(a[i], 10);
|
||||
}
|
||||
|
||||
oData.iEnd = parseInt(oData.iEnd, 10);
|
||||
oData.iLength = parseInt(oData.iLength, 10);
|
||||
oData.iStart = parseInt(oData.iStart, 10);
|
||||
oData.iCreate = parseInt(oData.iCreate, 10);
|
||||
},
|
||||
|
||||
"sAjaxSource": "/Library/contents",
|
||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||
var type;
|
||||
|
||||
aoData.push( { name: "format", value: "json"} );
|
||||
|
||||
//push whether to search files/playlists or all.
|
||||
type = $("#library_display_type").find("select").val();
|
||||
type = (type === undefined) ? 0 : type;
|
||||
aoData.push( { name: "type", value: type} );
|
||||
|
||||
$.ajax( {
|
||||
"dataType": 'json',
|
||||
"type": "GET",
|
||||
"url": sSource,
|
||||
"data": aoData,
|
||||
"success": fnCallback
|
||||
} );
|
||||
},
|
||||
"fnRowCallback": AIRTIME.library.events.fnRowCallback,
|
||||
"fnCreatedRow": function( nRow, aData, iDataIndex ) {
|
||||
|
||||
//call the context menu so we can prevent the event from propagating.
|
||||
$(nRow).find('td:not(.library_checkbox)').click(function(e){
|
||||
|
||||
$(this).contextMenu({x: e.pageX, y: e.pageY});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
//add a tool tip to appear when the user clicks on the type icon.
|
||||
$(nRow).find("td:not(:first, td>img)").qtip({
|
||||
content: {
|
||||
text: "Loading...",
|
||||
title: {
|
||||
text: aData.track_title
|
||||
},
|
||||
ajax: {
|
||||
url: "/Library/get-file-meta-data",
|
||||
type: "get",
|
||||
data: ({format: "html", id : aData.id, type: aData.ftype}),
|
||||
success: function(data, status) {
|
||||
this.set('content.text', data);
|
||||
}
|
||||
}
|
||||
},
|
||||
position: {
|
||||
target: 'event',
|
||||
adjust: {
|
||||
resize: true,
|
||||
method: "flip flip"
|
||||
},
|
||||
my: 'left center',
|
||||
at: 'right center',
|
||||
viewport: $(window), // Keep the tooltip on-screen at all times
|
||||
effect: false // Disable positioning animation
|
||||
},
|
||||
style: {
|
||||
classes: "ui-tooltip-dark"
|
||||
},
|
||||
show: 'mousedown',
|
||||
events: {
|
||||
show: function(event, api) {
|
||||
// Only show the tooltip if it was a right-click
|
||||
if(event.originalEvent.button !== 2) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
hide: 'mouseout'
|
||||
|
||||
});
|
||||
},
|
||||
"fnDrawCallback": AIRTIME.library.events.fnDrawCallback,
|
||||
"fnHeaderCallback": function(nHead) {
|
||||
$(nHead).find("input[type=checkbox]").attr("checked", false);
|
||||
},
|
||||
|
||||
"aaSorting": [[3, 'asc']],
|
||||
"sPaginationType": "full_numbers",
|
||||
"bJQueryUI": true,
|
||||
"bAutoWidth": false,
|
||||
"oLanguage": {
|
||||
"sSearch": ""
|
||||
},
|
||||
|
||||
// R = ColReorder, C = ColVis, T = TableTools
|
||||
"sDom": 'Rl<"#library_display_type">fr<"H"T<"library_toolbar"C>>t<"F"ip>',
|
||||
|
||||
"oTableTools": {
|
||||
"sRowSelect": "multi",
|
||||
"aButtons": [],
|
||||
"fnRowSelected": function ( node ) {
|
||||
var selected;
|
||||
|
||||
//seems to happen if everything is selected
|
||||
if ( node === null) {
|
||||
selected = oTable.find("input[type=checkbox]");
|
||||
selected.attr("checked", true);
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", true);
|
||||
selected = oTable.find("input[type=checkbox]").filter(":checked");
|
||||
}
|
||||
|
||||
//checking to enable buttons
|
||||
AIRTIME.button.enableButton("library_group_delete");
|
||||
AIRTIME.library.events.enableAddButtonCheck();
|
||||
},
|
||||
"fnRowDeselected": function ( node ) {
|
||||
var selected;
|
||||
|
||||
//seems to happen if everything is deselected
|
||||
if ( node === null) {
|
||||
oTable.find("input[type=checkbox]").attr("checked", false);
|
||||
selected = [];
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", false);
|
||||
selected = oTable.find("input[type=checkbox]").filter(":checked");
|
||||
}
|
||||
|
||||
//checking to disable buttons
|
||||
if (selected.length === 0) {
|
||||
AIRTIME.button.disableButton("library_group_delete");
|
||||
}
|
||||
AIRTIME.library.events.enableAddButtonCheck();
|
||||
}
|
||||
},
|
||||
|
||||
"oColVis": {
|
||||
"buttonText": "Show/Hide Columns",
|
||||
"sAlign": "right",
|
||||
"aiExclude": [0, 1, 2],
|
||||
"sSize": "css"
|
||||
},
|
||||
|
||||
"oColReorder": {
|
||||
"iFixedColumns": 2
|
||||
}
|
||||
|
||||
});
|
||||
oTable.fnSetFilteringDelay(350);
|
||||
|
||||
AIRTIME.library.events.setupLibraryToolbar(oTable);
|
||||
|
||||
$("#library_display_type")
|
||||
.addClass("dataTables_type")
|
||||
.append('<select name="library_display_type" />')
|
||||
.find("select")
|
||||
.append('<option value="0">All</option>')
|
||||
.append('<option value="1">Files</option>')
|
||||
.append('<option value="2">Playlists</option>')
|
||||
.end()
|
||||
.change(function(ev){
|
||||
oTable.fnDraw();
|
||||
});
|
||||
|
||||
$('[name="pl_cb_all"]').click(function(){
|
||||
var oTT = TableTools.fnGetInstance('library_display');
|
||||
|
||||
if ($(this).is(":checked")) {
|
||||
oTT.fnSelectAll();
|
||||
}
|
||||
else {
|
||||
oTT.fnSelectNone();
|
||||
}
|
||||
});
|
||||
|
||||
checkImportStatus();
|
||||
setInterval( checkImportStatus, 5000 );
|
||||
setInterval( checkSCUploadStatus, 5000 );
|
||||
|
||||
addQtipToSCIcons();
|
||||
|
||||
$.contextMenu({
|
||||
selector: '#library_display td:not(.library_checkbox)',
|
||||
trigger: "left",
|
||||
ignoreRightClick: true,
|
||||
|
||||
build: function($el, e) {
|
||||
var data, screen, items, callback, $tr;
|
||||
|
||||
$tr = $el.parent();
|
||||
data = $tr.data("aData");
|
||||
screen = $tr.data("screen");
|
||||
|
||||
function processMenuItems(oItems) {
|
||||
|
||||
//define an add to playlist callback.
|
||||
if (oItems.pl_add !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
AIRTIME.playlist.fnAddItems([data.id], undefined, 'after');
|
||||
};
|
||||
|
||||
oItems.pl_add.callback = callback;
|
||||
}
|
||||
|
||||
//define an edit callback.
|
||||
if (oItems.edit !== undefined) {
|
||||
|
||||
if (data.ftype === "audioclip") {
|
||||
callback = function() {
|
||||
document.location.href = oItems.edit.url;
|
||||
};
|
||||
}
|
||||
else {
|
||||
callback = function() {
|
||||
AIRTIME.playlist.fnEdit(data.id);
|
||||
};
|
||||
}
|
||||
oItems.edit.callback = callback;
|
||||
}
|
||||
|
||||
//define a delete callback.
|
||||
if (oItems.del !== undefined) {
|
||||
|
||||
//delete through the playlist controller, will reset
|
||||
//playlist screen if this is the currently edited playlist.
|
||||
if (data.ftype === "playlist" && screen === "playlist") {
|
||||
callback = function() {
|
||||
|
||||
if (confirm('Are you sure you want to delete the selected item?')) {
|
||||
AIRTIME.playlist.fnDelete(data.id);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
callback = function() {
|
||||
var media = [];
|
||||
|
||||
if (confirm('Are you sure you want to delete the selected item?')) {
|
||||
|
||||
media.push({"id": data.id, "type": data.ftype});
|
||||
$.post(oItems.del.url, {format: "json", media: media }, function(json){
|
||||
var oTable;
|
||||
|
||||
if (json.message) {
|
||||
alert(json.message);
|
||||
}
|
||||
|
||||
oTable = $("#library_display").dataTable();
|
||||
oTable.fnDeleteRow( $tr[0] );
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
oItems.del.callback = callback;
|
||||
}
|
||||
|
||||
//define a download callback.
|
||||
if (oItems.download !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
document.location.href = oItems.download.url;
|
||||
};
|
||||
oItems.download.callback = callback;
|
||||
}
|
||||
//add callbacks for Soundcloud menu items.
|
||||
if (oItems.soundcloud !== undefined) {
|
||||
var soundcloud = oItems.soundcloud.items;
|
||||
|
||||
//define an upload to soundcloud callback.
|
||||
if (soundcloud.upload !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
$.post(soundcloud.upload.url, function(){
|
||||
addProgressIcon(data.id);
|
||||
});
|
||||
};
|
||||
soundcloud.upload.callback = callback;
|
||||
}
|
||||
|
||||
//define a view on soundcloud callback
|
||||
if (soundcloud.view !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
window.open(soundcloud.view.url);
|
||||
};
|
||||
soundcloud.view.callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
items = oItems;
|
||||
}
|
||||
|
||||
request = $.ajax({
|
||||
url: "/library/context-menu",
|
||||
type: "GET",
|
||||
data: {id : data.id, type: data.ftype, format: "json", "screen": screen},
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json){
|
||||
processMenuItems(json.items);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
items: items
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
mod.libraryInit = libraryInit;
|
||||
|
||||
return AIRTIME;
|
||||
|
||||
}(AIRTIME || {}));
|
||||
|
@ -43,42 +450,45 @@ var AIRTIME = (function(AIRTIME){
|
|||
function addToolBarButtonsLibrary(aButtons) {
|
||||
var i,
|
||||
length = aButtons.length,
|
||||
libToolBar,
|
||||
libToolBar = $(".library_toolbar"),
|
||||
html,
|
||||
buttonClass = '',
|
||||
DEFAULT_CLASS = 'ui-button ui-state-default',
|
||||
DISABLED_CLASS = 'ui-state-disabled';
|
||||
DISABLED_CLASS = 'ui-state-disabled',
|
||||
fn;
|
||||
|
||||
libToolBar = $(".library_toolbar");
|
||||
|
||||
for ( i=0; i < length; i+=1 ) {
|
||||
for ( i = 0; i < length; i += 1 ) {
|
||||
buttonClass = '';
|
||||
|
||||
//add disabled class if not enabled.
|
||||
if (aButtons[i][2] === false) {
|
||||
buttonClass+=DISABLED_CLASS;
|
||||
buttonClass += DISABLED_CLASS;
|
||||
}
|
||||
|
||||
html = '<div id="'+aButtons[i][1]+'" class="ColVis TableTools"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>';
|
||||
html = '<div class="ColVis TableTools '+aButtons[i][1]+'"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>';
|
||||
libToolBar.append(html);
|
||||
libToolBar.find("#"+aButtons[i][1]).click(aButtons[i][3]);
|
||||
|
||||
//create a closure to preserve the state of i.
|
||||
(function(index){
|
||||
|
||||
libToolBar.find("."+aButtons[index][1]).click(function(){
|
||||
fn = function() {
|
||||
var $button = $(this).find("button");
|
||||
|
||||
//only call the passed function if the button is enabled.
|
||||
if (!$button.hasClass(DISABLED_CLASS)) {
|
||||
aButtons[index][3]();
|
||||
}
|
||||
};
|
||||
|
||||
fn.call(this);
|
||||
});
|
||||
|
||||
}(i));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function enableGroupBtn(btnId, func) {
|
||||
btnId = '#' + btnId;
|
||||
if ($(btnId).hasClass('ui-state-disabled')) {
|
||||
$(btnId).removeClass('ui-state-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function disableGroupBtn(btnId) {
|
||||
btnId = '#' + btnId;
|
||||
if (!$(btnId).hasClass('ui-state-disabled')) {
|
||||
$(btnId).addClass('ui-state-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function checkImportStatus(){
|
||||
$.getJSON('/Preference/is-import-in-progress', function(data){
|
||||
var div = $('#import_status');
|
||||
|
@ -210,389 +620,4 @@ function addQtipToSCIcons(){
|
|||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var oTable;
|
||||
|
||||
oTable = $('#library_display').dataTable( {
|
||||
|
||||
"aoColumns": [
|
||||
/* Checkbox */ {"sTitle": "<input type='checkbox' name='pl_cb_all'>", "mDataProp": "checkbox", "bSortable": false, "bSearchable": false, "sWidth": "25px", "sClass": "library_checkbox"},
|
||||
/* Type */ {"sTitle": "", "mDataProp": "image", "bSearchable": false, "sWidth": "25px", "sClass": "library_type", "iDataSort": 2},
|
||||
/* ftype */ {"sTitle": "", "mDataProp": "ftype", "bSearchable": false, "bVisible": false},
|
||||
/* Title */ {"sTitle": "Title", "mDataProp": "track_title", "sClass": "library_title"},
|
||||
/* Creator */ {"sTitle": "Creator", "mDataProp": "artist_name", "sClass": "library_creator"},
|
||||
/* Album */ {"sTitle": "Album", "mDataProp": "album_title", "sClass": "library_album"},
|
||||
/* Genre */ {"sTitle": "Genre", "mDataProp": "genre", "sClass": "library_genre"},
|
||||
/* Year */ {"sTitle": "Year", "mDataProp": "year", "sClass": "library_year", "sWidth": "60px"},
|
||||
/* Length */ {"sTitle": "Length", "mDataProp": "length", "sClass": "library_length", "sWidth": "80px"},
|
||||
/* Upload Time */ {"sTitle": "Uploaded", "mDataProp": "utime", "sClass": "library_upload_time"},
|
||||
/* Last Modified */ {"sTitle": "Last Modified", "mDataProp": "mtime", "bVisible": false, "sClass": "library_modified_time"},
|
||||
/* Track Number */ {"sTitle": "Track", "mDataProp": "track_number", "bSearchable": false, "bVisible": false, "sClass": "library_track"},
|
||||
/* Mood */ {"sTitle": "Mood", "mDataProp": "mood", "bSearchable": false, "bVisible": false, "sClass": "library_mood"},
|
||||
/* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"},
|
||||
/* Composer */ {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer"},
|
||||
/* Website */ {"sTitle": "Website", "mDataProp": "info_url", "bSearchable": false, "bVisible": false, "sClass": "library_url"},
|
||||
/* Bit Rate */ {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate"},
|
||||
/* Sameple Rate */ {"sTitle": "Sample Rate", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr"},
|
||||
/* ISRC Number */ {"sTitle": "ISRC", "mDataProp": "isrc_number", "bSearchable": false, "bVisible": false, "sClass": "library_isrc"},
|
||||
/* Encoded */ {"sTitle": "Encoded", "mDataProp": "encoded_by", "bSearchable": false, "bVisible": false, "sClass": "library_encoded"},
|
||||
/* Label */ {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label"},
|
||||
/* Copyright */ {"sTitle": "Copyright", "mDataProp": "copyright", "bSearchable": false, "bVisible": false, "sClass": "library_copyright"},
|
||||
/* Mime */ {"sTitle": "Mime", "mDataProp": "mime", "bSearchable": false, "bVisible": false, "sClass": "library_mime"},
|
||||
/* Language */ {"sTitle": "Language", "mDataProp": "language", "bSearchable": false, "bVisible": false, "sClass": "library_language"}
|
||||
],
|
||||
|
||||
"bProcessing": true,
|
||||
"bServerSide": true,
|
||||
|
||||
"bStateSave": true,
|
||||
"fnStateSaveParams": function (oSettings, oData) {
|
||||
//remove oData components we don't want to save.
|
||||
delete oData.oSearch;
|
||||
delete oData.aoSearchCols;
|
||||
},
|
||||
"fnStateSave": function (oSettings, oData) {
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/set-library-datatable",
|
||||
type: "POST",
|
||||
data: {settings : oData, format: "json"},
|
||||
dataType: "json",
|
||||
success: function(){},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
},
|
||||
"fnStateLoad": function (oSettings) {
|
||||
var o;
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/get-library-datatable",
|
||||
type: "GET",
|
||||
data: {format: "json"},
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json){
|
||||
o = json.settings;
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
|
||||
return o;
|
||||
},
|
||||
"fnStateLoadParams": function (oSettings, oData) {
|
||||
var i,
|
||||
length,
|
||||
a = oData.abVisCols;
|
||||
|
||||
//putting serialized data back into the correct js type to make
|
||||
//sure everything works properly.
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = (a[i] === "true") ? true : false;
|
||||
}
|
||||
|
||||
a = oData.ColReorder;
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = parseInt(a[i], 10);
|
||||
}
|
||||
|
||||
oData.iEnd = parseInt(oData.iEnd, 10);
|
||||
oData.iLength = parseInt(oData.iLength, 10);
|
||||
oData.iStart = parseInt(oData.iStart, 10);
|
||||
oData.iCreate = parseInt(oData.iCreate, 10);
|
||||
},
|
||||
|
||||
"sAjaxSource": "/Library/contents",
|
||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||
var type;
|
||||
|
||||
aoData.push( { name: "format", value: "json"} );
|
||||
|
||||
//push whether to search files/playlists or all.
|
||||
type = $("#library_display_type").find("select").val();
|
||||
type = (type === undefined) ? 0 : type;
|
||||
aoData.push( { name: "type", value: type} );
|
||||
|
||||
$.ajax( {
|
||||
"dataType": 'json',
|
||||
"type": "GET",
|
||||
"url": sSource,
|
||||
"data": aoData,
|
||||
"success": fnCallback
|
||||
} );
|
||||
},
|
||||
"fnRowCallback": AIRTIME.library.events.fnRowCallback,
|
||||
"fnCreatedRow": function( nRow, aData, iDataIndex ) {
|
||||
|
||||
//call the context menu so we can prevent the event from propagating.
|
||||
$(nRow).find('td:not(.library_checkbox)').click(function(e){
|
||||
|
||||
$(this).contextMenu({x: e.pageX, y: e.pageY});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
//add a tool tip to appear when the user clicks on the type icon.
|
||||
$(nRow).find("td:not(:first, td>img)").qtip({
|
||||
content: {
|
||||
text: "Loading...",
|
||||
title: {
|
||||
text: aData.track_title
|
||||
},
|
||||
ajax: {
|
||||
url: "/Library/get-file-meta-data",
|
||||
type: "get",
|
||||
data: ({format: "html", id : aData.id, type: aData.ftype}),
|
||||
success: function(data, status) {
|
||||
this.set('content.text', data);
|
||||
}
|
||||
}
|
||||
},
|
||||
position: {
|
||||
target: 'event',
|
||||
adjust: {
|
||||
resize: true,
|
||||
method: "flip flip"
|
||||
},
|
||||
my: 'left center',
|
||||
at: 'right center',
|
||||
viewport: $(window), // Keep the tooltip on-screen at all times
|
||||
effect: false // Disable positioning animation
|
||||
},
|
||||
style: {
|
||||
classes: "ui-tooltip-dark"
|
||||
},
|
||||
show: 'mousedown',
|
||||
events: {
|
||||
show: function(event, api) {
|
||||
// Only show the tooltip if it was a right-click
|
||||
if(event.originalEvent.button !== 2) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
hide: 'mouseout'
|
||||
|
||||
});
|
||||
},
|
||||
"fnDrawCallback": AIRTIME.library.events.fnDrawCallback,
|
||||
"fnHeaderCallback": function(nHead) {
|
||||
$(nHead).find("input[type=checkbox]").attr("checked", false);
|
||||
},
|
||||
|
||||
"aaSorting": [[3, 'asc']],
|
||||
"sPaginationType": "full_numbers",
|
||||
"bJQueryUI": true,
|
||||
"bAutoWidth": false,
|
||||
"oLanguage": {
|
||||
"sSearch": ""
|
||||
},
|
||||
|
||||
// R = ColReorder, C = ColVis, T = TableTools
|
||||
"sDom": 'Rl<"#library_display_type">fr<"H"T<"library_toolbar"C>>t<"F"ip>',
|
||||
|
||||
"oTableTools": {
|
||||
"sRowSelect": "multi",
|
||||
"aButtons": [],
|
||||
"fnRowSelected": function ( node ) {
|
||||
|
||||
//seems to happen if everything is selected
|
||||
if ( node === null) {
|
||||
oTable.find("input[type=checkbox]").attr("checked", true);
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", true);
|
||||
}
|
||||
},
|
||||
"fnRowDeselected": function ( node ) {
|
||||
|
||||
//seems to happen if everything is deselected
|
||||
if ( node === null) {
|
||||
oTable.find("input[type=checkbox]").attr("checked", false);
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"oColVis": {
|
||||
"buttonText": "Show/Hide Columns",
|
||||
"sAlign": "right",
|
||||
"aiExclude": [0, 1, 2],
|
||||
"sSize": "css"
|
||||
},
|
||||
|
||||
"oColReorder": {
|
||||
"iFixedColumns": 2
|
||||
}
|
||||
|
||||
});
|
||||
oTable.fnSetFilteringDelay(350);
|
||||
|
||||
AIRTIME.library.events.setupLibraryToolbar(oTable);
|
||||
|
||||
$("#library_display_type")
|
||||
.addClass("dataTables_type")
|
||||
.append('<select name="library_display_type" />')
|
||||
.find("select")
|
||||
.append('<option value="0">All</option>')
|
||||
.append('<option value="1">Files</option>')
|
||||
.append('<option value="2">Playlists</option>')
|
||||
.end()
|
||||
.change(function(ev){
|
||||
oTable.fnDraw();
|
||||
});
|
||||
|
||||
$('[name="pl_cb_all"]').click(function(){
|
||||
var oTT = TableTools.fnGetInstance('library_display');
|
||||
|
||||
if ($(this).is(":checked")) {
|
||||
oTT.fnSelectAll();
|
||||
}
|
||||
else {
|
||||
oTT.fnSelectNone();
|
||||
}
|
||||
});
|
||||
|
||||
checkImportStatus();
|
||||
setInterval( checkImportStatus, 5000 );
|
||||
setInterval( checkSCUploadStatus, 5000 );
|
||||
|
||||
addQtipToSCIcons();
|
||||
|
||||
$.contextMenu({
|
||||
selector: '#library_display td:not(.library_checkbox)',
|
||||
trigger: "left",
|
||||
ignoreRightClick: true,
|
||||
|
||||
build: function($el, e) {
|
||||
var x, request, data, screen, items, callback, $tr;
|
||||
|
||||
$tr = $el.parent();
|
||||
data = $tr.data("aData");
|
||||
screen = $tr.data("screen");
|
||||
|
||||
function processMenuItems(oItems) {
|
||||
|
||||
//define an add to playlist callback.
|
||||
if (oItems.pl_add !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
AIRTIME.playlist.fnAddItems([data.id], undefined, 'after');
|
||||
};
|
||||
|
||||
oItems.pl_add.callback = callback;
|
||||
}
|
||||
|
||||
//define an edit callback.
|
||||
if (oItems.edit !== undefined) {
|
||||
|
||||
if (data.ftype === "audioclip") {
|
||||
callback = function() {
|
||||
document.location.href = oItems.edit.url;
|
||||
};
|
||||
}
|
||||
else {
|
||||
callback = function() {
|
||||
AIRTIME.playlist.fnEdit(data.id);
|
||||
};
|
||||
}
|
||||
oItems.edit.callback = callback;
|
||||
}
|
||||
|
||||
//define a delete callback.
|
||||
if (oItems.del !== undefined) {
|
||||
|
||||
//delete through the playlist controller, will reset
|
||||
//playlist screen if this is the currently edited playlist.
|
||||
if (data.ftype === "playlist" && screen === "playlist") {
|
||||
callback = function() {
|
||||
|
||||
if (confirm('Are you sure you want to delete the selected item?')) {
|
||||
AIRTIME.playlist.fnDelete(data.id);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
callback = function() {
|
||||
var media = [];
|
||||
|
||||
if (confirm('Are you sure you want to delete the selected item?')) {
|
||||
|
||||
media.push({"id": data.id, "type": data.ftype});
|
||||
$.post(oItems.del.url, {format: "json", media: media }, function(json){
|
||||
var oTable, tr;
|
||||
|
||||
if (json.message) {
|
||||
alert(json.message);
|
||||
}
|
||||
|
||||
oTable = $("#library_display").dataTable();
|
||||
oTable.fnDeleteRow( $tr[0] );
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
oItems.del.callback = callback;
|
||||
}
|
||||
|
||||
//define a download callback.
|
||||
if (oItems.download !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
document.location.href = oItems.download.url;
|
||||
};
|
||||
oItems.download.callback = callback;
|
||||
}
|
||||
//add callbacks for Soundcloud menu items.
|
||||
if (oItems.soundcloud !== undefined) {
|
||||
var soundcloud = oItems.soundcloud.items;
|
||||
|
||||
//define an upload to soundcloud callback.
|
||||
if (soundcloud.upload !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
$.post(soundcloud.upload.url, function(){
|
||||
addProgressIcon(data.id);
|
||||
});
|
||||
};
|
||||
soundcloud.upload.callback = callback;
|
||||
}
|
||||
|
||||
//define a view on soundcloud callback
|
||||
if (soundcloud.view !== undefined) {
|
||||
|
||||
callback = function() {
|
||||
window.open(soundcloud.view.url);
|
||||
};
|
||||
soundcloud.view.callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
items = oItems;
|
||||
}
|
||||
|
||||
request = $.ajax({
|
||||
url: "/library/context-menu",
|
||||
type: "GET",
|
||||
data: {id : data.id, type: data.ftype, format: "json", "screen": screen},
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json){
|
||||
processMenuItems(json.items);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
items: items
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
$(document).ready(AIRTIME.library.libraryInit);
|
|
@ -506,18 +506,25 @@ var AIRTIME = (function(AIRTIME){
|
|||
fnUpdate;
|
||||
|
||||
fnReceive = function(event, ui) {
|
||||
var selected = $('#library_display tr[id^="au"] input:checked').parents('tr'),
|
||||
aItems = [];
|
||||
|
||||
var 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 (selected.length === 0) {
|
||||
selected = ui.item;
|
||||
if (aSelected.length === 0) {
|
||||
aSelected.push(ui.item.data("aData"));
|
||||
}
|
||||
|
||||
selected.each(function(i, el) {
|
||||
aItems.push($(el).data("aData").id);
|
||||
});
|
||||
|
||||
for (i = 0, length = aSelected.length; i < length; i++) {
|
||||
if (aSelected[i].ftype === "audioclip") {
|
||||
aItems.push(aSelected[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
aReceiveItems = aItems;
|
||||
html = ui.helper.html();
|
||||
};
|
||||
|
|
|
@ -67,21 +67,10 @@ function uploadToSoundCloud(show_instance_id){
|
|||
}
|
||||
}
|
||||
|
||||
function buildContentDialog (json){
|
||||
var dialog = $(json.dialog),
|
||||
viewportwidth,
|
||||
viewportheight,
|
||||
height,
|
||||
width;
|
||||
function findViewportDimensions() {
|
||||
var viewportwidth,
|
||||
viewportheight;
|
||||
|
||||
if (json.show_error == true){
|
||||
alertShowErrorAndReload();
|
||||
}
|
||||
|
||||
dialog.find("#show_progressbar").progressbar({
|
||||
value: json.percentFilled
|
||||
});
|
||||
|
||||
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use
|
||||
// window.innerWidth and window.innerHeight
|
||||
if (typeof window.innerWidth != 'undefined') {
|
||||
|
@ -101,9 +90,57 @@ function buildContentDialog (json){
|
|||
viewportheight = document.getElementsByTagName('body')[0].clientHeight;
|
||||
}
|
||||
|
||||
height = viewportheight * 2/3;
|
||||
width = viewportwidth * 4/5;
|
||||
return {
|
||||
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({
|
||||
autoOpen: false,
|
||||
title: 'Show Contents',
|
||||
|
@ -201,8 +238,12 @@ $(document).ready(function() {
|
|||
if (oItems.schedule !== undefined) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
var AIRTIME = (function(AIRTIME){
|
||||
var mod,
|
||||
oSchedTable;
|
||||
oSchedTable,
|
||||
fnServerData;
|
||||
|
||||
if (AIRTIME.showbuilder === undefined) {
|
||||
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 ) {
|
||||
aoData.push( { name: "format", value: "json"} );
|
||||
|
||||
|
@ -173,428 +69,435 @@ $(document).ready(function() {
|
|||
} );
|
||||
};
|
||||
|
||||
oRange = fnGetScheduleRange();
|
||||
fnServerData.start = oRange.start;
|
||||
fnServerData.end = oRange.end;
|
||||
|
||||
fnRemoveSelectedItems = function() {
|
||||
var oTT = TableTools.fnGetInstance('show_builder_table'),
|
||||
aData = oTT.fnGetSelectedData(),
|
||||
i,
|
||||
length,
|
||||
temp,
|
||||
aItems = [];
|
||||
mod.fnServerData = fnServerData;
|
||||
|
||||
for (i=0, length = aData.length; i < length; i++) {
|
||||
temp = aData[i];
|
||||
aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp});
|
||||
}
|
||||
|
||||
AIRTIME.showbuilder.fnRemove(aItems);
|
||||
};
|
||||
|
||||
oTable = tableDiv.dataTable( {
|
||||
"aoColumns": [
|
||||
/* checkbox */ {"mDataProp": "allowed", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"},
|
||||
/* starts */{"mDataProp": "starts", "sTitle": "Start"},
|
||||
/* ends */{"mDataProp": "ends", "sTitle": "End"},
|
||||
/* runtime */{"mDataProp": "runtime", "sTitle": "Duration", "sClass": "library_length"},
|
||||
/* title */{"mDataProp": "title", "sTitle": "Title"},
|
||||
/* creator */{"mDataProp": "creator", "sTitle": "Creator"},
|
||||
/* album */{"mDataProp": "album", "sTitle": "Album"},
|
||||
/* cue in */{"mDataProp": "cuein", "sTitle": "Cue In", "bVisible": false},
|
||||
/* cue out */{"mDataProp": "cueout", "sTitle": "Cue Out", "bVisible": false},
|
||||
/* fade in */{"mDataProp": "fadein", "sTitle": "Fade In", "bVisible": false},
|
||||
/* fade out */{"mDataProp": "fadeout", "sTitle": "Fade Out", "bVisible": false}
|
||||
],
|
||||
|
||||
"bJQueryUI": true,
|
||||
"bSort": false,
|
||||
"bFilter": false,
|
||||
"bProcessing": true,
|
||||
"bServerSide": true,
|
||||
"bInfo": false,
|
||||
"bAutoWidth": false,
|
||||
|
||||
"bStateSave": true,
|
||||
"fnStateSaveParams": function (oSettings, oData) {
|
||||
//remove oData components we don't want to save.
|
||||
delete oData.oSearch;
|
||||
delete oData.aoSearchCols;
|
||||
},
|
||||
"fnStateSave": function (oSettings, oData) {
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/set-timeline-datatable",
|
||||
type: "POST",
|
||||
data: {settings : oData, format: "json"},
|
||||
dataType: "json",
|
||||
success: function(){},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
},
|
||||
"fnStateLoad": function (oSettings) {
|
||||
var o;
|
||||
mod.builderDataTable = function() {
|
||||
var tableDiv = $('#show_builder_table'),
|
||||
oTable,
|
||||
fnRemoveSelectedItems;
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/get-timeline-datatable",
|
||||
type: "GET",
|
||||
data: {format: "json"},
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json){
|
||||
o = json.settings;
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
|
||||
return o;
|
||||
},
|
||||
"fnStateLoadParams": function (oSettings, oData) {
|
||||
var i,
|
||||
fnRemoveSelectedItems = function() {
|
||||
var oTT = TableTools.fnGetInstance('show_builder_table'),
|
||||
aData = oTT.fnGetSelectedData(),
|
||||
i,
|
||||
length,
|
||||
a = oData.abVisCols;
|
||||
temp,
|
||||
aItems = [];
|
||||
|
||||
//putting serialized data back into the correct js type to make
|
||||
//sure everything works properly.
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = (a[i] === "true") ? true : false;
|
||||
}
|
||||
for (i=0, length = aData.length; i < length; i++) {
|
||||
temp = aData[i];
|
||||
aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp});
|
||||
}
|
||||
|
||||
AIRTIME.showbuilder.fnRemove(aItems);
|
||||
};
|
||||
|
||||
oTable = tableDiv.dataTable( {
|
||||
"aoColumns": [
|
||||
/* checkbox */ {"mDataProp": "allowed", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"},
|
||||
/* starts */{"mDataProp": "starts", "sTitle": "Start"},
|
||||
/* ends */{"mDataProp": "ends", "sTitle": "End"},
|
||||
/* runtime */{"mDataProp": "runtime", "sTitle": "Duration", "sClass": "library_length"},
|
||||
/* title */{"mDataProp": "title", "sTitle": "Title"},
|
||||
/* creator */{"mDataProp": "creator", "sTitle": "Creator"},
|
||||
/* album */{"mDataProp": "album", "sTitle": "Album"},
|
||||
/* cue in */{"mDataProp": "cuein", "sTitle": "Cue In", "bVisible": false},
|
||||
/* cue out */{"mDataProp": "cueout", "sTitle": "Cue Out", "bVisible": false},
|
||||
/* fade in */{"mDataProp": "fadein", "sTitle": "Fade In", "bVisible": false},
|
||||
/* fade out */{"mDataProp": "fadeout", "sTitle": "Fade Out", "bVisible": false}
|
||||
],
|
||||
|
||||
a = oData.ColReorder;
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = parseInt(a[i], 10);
|
||||
}
|
||||
|
||||
oData.iCreate = parseInt(oData.iCreate, 10);
|
||||
},
|
||||
|
||||
"fnServerData": fnServerData,
|
||||
"fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
|
||||
var i,
|
||||
sSeparatorHTML,
|
||||
fnPrepareSeparatorRow,
|
||||
node,
|
||||
cl="";
|
||||
"bJQueryUI": true,
|
||||
"bSort": false,
|
||||
"bFilter": false,
|
||||
"bProcessing": true,
|
||||
"bServerSide": true,
|
||||
"bInfo": false,
|
||||
"bAutoWidth": false,
|
||||
|
||||
//save some info for reordering purposes.
|
||||
$(nRow).data({"aData": aData});
|
||||
"bStateSave": true,
|
||||
"fnStateSaveParams": function (oSettings, oData) {
|
||||
//remove oData components we don't want to save.
|
||||
delete oData.oSearch;
|
||||
delete oData.aoSearchCols;
|
||||
},
|
||||
"fnStateSave": function (oSettings, oData) {
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/set-timeline-datatable",
|
||||
type: "POST",
|
||||
data: {settings : oData, format: "json"},
|
||||
dataType: "json",
|
||||
success: function(){},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
},
|
||||
"fnStateLoad": function (oSettings) {
|
||||
var o;
|
||||
|
||||
$.ajax({
|
||||
url: "/usersettings/get-timeline-datatable",
|
||||
type: "GET",
|
||||
data: {format: "json"},
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json){
|
||||
o = json.settings;
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
var x;
|
||||
}
|
||||
});
|
||||
|
||||
return o;
|
||||
},
|
||||
"fnStateLoadParams": function (oSettings, oData) {
|
||||
var i,
|
||||
length,
|
||||
a = oData.abVisCols;
|
||||
|
||||
if (aData.allowed !== true) {
|
||||
$(nRow).addClass("sb-not-allowed");
|
||||
}
|
||||
|
||||
//status used to colour tracks.
|
||||
if (aData.status === 1) {
|
||||
$(nRow).addClass("sb-boundry");
|
||||
}
|
||||
else if (aData.status === 2) {
|
||||
$(nRow).addClass("sb-over");
|
||||
}
|
||||
|
||||
fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) {
|
||||
//putting serialized data back into the correct js type to make
|
||||
//sure everything works properly.
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = (a[i] === "true") ? true : false;
|
||||
}
|
||||
|
||||
a = oData.ColReorder;
|
||||
for (i = 0, length = a.length; i < length; i++) {
|
||||
a[i] = parseInt(a[i], 10);
|
||||
}
|
||||
|
||||
oData.iCreate = parseInt(oData.iCreate, 10);
|
||||
},
|
||||
|
||||
"fnServerData": AIRTIME.showbuilder.fnServerData,
|
||||
"fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
|
||||
var i,
|
||||
sSeparatorHTML,
|
||||
fnPrepareSeparatorRow,
|
||||
node,
|
||||
cl="";
|
||||
|
||||
node = nRow.children[iNodeIndex];
|
||||
node.innerHTML = sRowContent;
|
||||
node.setAttribute('colspan',100);
|
||||
for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) {
|
||||
node = nRow.children[i];
|
||||
node.innerHTML = "";
|
||||
node.setAttribute("style", "display : none");
|
||||
//save some info for reordering purposes.
|
||||
$(nRow).data({"aData": aData});
|
||||
|
||||
if (aData.current === true) {
|
||||
$(nRow).addClass("sb-now-playing");
|
||||
}
|
||||
|
||||
$(nRow).addClass(sClass);
|
||||
if (aData.allowed !== true) {
|
||||
$(nRow).addClass("sb-not-allowed");
|
||||
}
|
||||
else {
|
||||
$(nRow).addClass("sb-allowed");
|
||||
}
|
||||
|
||||
//status used to colour tracks.
|
||||
if (aData.status === 2) {
|
||||
$(nRow).addClass("sb-boundry");
|
||||
}
|
||||
else if (aData.status === 0) {
|
||||
$(nRow).addClass("sb-over");
|
||||
}
|
||||
|
||||
fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) {
|
||||
|
||||
node = nRow.children[iNodeIndex];
|
||||
node.innerHTML = sRowContent;
|
||||
node.setAttribute('colspan',100);
|
||||
for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) {
|
||||
node = nRow.children[i];
|
||||
node.innerHTML = "";
|
||||
node.setAttribute("style", "display : none");
|
||||
}
|
||||
|
||||
$(nRow).addClass(sClass);
|
||||
};
|
||||
|
||||
if (aData.header === true) {
|
||||
cl = 'sb-header';
|
||||
|
||||
sSeparatorHTML = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>';
|
||||
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
|
||||
}
|
||||
else if (aData.footer === true) {
|
||||
node = nRow.children[0];
|
||||
cl = 'sb-footer';
|
||||
|
||||
//check the show's content status.
|
||||
if (aData.runtime > 0) {
|
||||
node.innerHTML = '<span class="ui-icon ui-icon-check"></span>';
|
||||
cl = cl + ' ui-state-highlight';
|
||||
}
|
||||
else {
|
||||
node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
|
||||
cl = cl + ' ui-state-error';
|
||||
}
|
||||
|
||||
sSeparatorHTML = '<span>'+aData.fRuntime+'</span>';
|
||||
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
|
||||
}
|
||||
else if (aData.empty === true) {
|
||||
|
||||
sSeparatorHTML = '<span>Show Empty</span>';
|
||||
cl = cl + " sb-empty odd";
|
||||
|
||||
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
|
||||
}
|
||||
else if (aData.record === true) {
|
||||
|
||||
sSeparatorHTML = '<span>Recording From Line In</span>';
|
||||
cl = cl + " sb-record odd";
|
||||
|
||||
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
|
||||
}
|
||||
else {
|
||||
|
||||
node = nRow.children[0];
|
||||
if (aData.allowed === true) {
|
||||
node.innerHTML = '<input type="checkbox" name="'+aData.id+'"></input>';
|
||||
}
|
||||
else {
|
||||
node.innerHTML = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
"fnDrawCallback": function(oSettings, json) {
|
||||
var wrapperDiv,
|
||||
markerDiv,
|
||||
td;
|
||||
|
||||
//create cursor arrows.
|
||||
tableDiv.find("tr:not(:first, .sb-footer, .sb-empty, .sb-not-allowed)").each(function(i, el) {
|
||||
td = $(el).find("td:first");
|
||||
if (td.hasClass("dataTables_empty")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wrapperDiv = $("<div />", {
|
||||
"class": "innerWrapper",
|
||||
"css": {
|
||||
"height": td.height()
|
||||
}
|
||||
});
|
||||
markerDiv = $("<div />", {
|
||||
"class": "marker"
|
||||
});
|
||||
|
||||
td.append(markerDiv).wrapInner(wrapperDiv);
|
||||
});
|
||||
},
|
||||
"fnHeaderCallback": function(nHead) {
|
||||
$(nHead).find("input[type=checkbox]").attr("checked", false);
|
||||
},
|
||||
//remove any selected nodes before the draw.
|
||||
"fnPreDrawCallback": function( oSettings ) {
|
||||
var oTT = TableTools.fnGetInstance('show_builder_table');
|
||||
oTT.fnSelectNone();
|
||||
},
|
||||
|
||||
"oColVis": {
|
||||
"aiExclude": [ 0, 1 ]
|
||||
},
|
||||
|
||||
"oColReorder": {
|
||||
"iFixedColumns": 2
|
||||
},
|
||||
|
||||
"oTableTools": {
|
||||
"sRowSelect": "multi",
|
||||
"aButtons": [],
|
||||
"fnPreRowSelect": function ( e ) {
|
||||
var node = e.currentTarget;
|
||||
//don't select separating rows, or shows without privileges.
|
||||
if ($(node).hasClass("sb-header")
|
||||
|| $(node).hasClass("sb-footer")
|
||||
|| $(node).hasClass("sb-empty")
|
||||
|| $(node).hasClass("sb-not-allowed")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
"fnRowSelected": function ( node ) {
|
||||
|
||||
//seems to happen if everything is selected
|
||||
if ( node === null) {
|
||||
oTable.find("input[type=checkbox]").attr("checked", true);
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", true);
|
||||
}
|
||||
|
||||
//checking to enable buttons
|
||||
AIRTIME.button.enableButton("sb_delete");
|
||||
},
|
||||
"fnRowDeselected": function ( node ) {
|
||||
var selected;
|
||||
|
||||
//seems to happen if everything is deselected
|
||||
if ( node === null) {
|
||||
tableDiv.find("input[type=checkbox]").attr("checked", false);
|
||||
selected = [];
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", false);
|
||||
selected = tableDiv.find("input[type=checkbox]").filter(":checked");
|
||||
}
|
||||
|
||||
//checking to disable buttons
|
||||
if (selected.length === 0) {
|
||||
AIRTIME.button.disableButton("sb_delete");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// R = ColReorderResize, C = ColVis, T = TableTools
|
||||
"sDom": 'Rr<"H"CT>t<"F">',
|
||||
|
||||
"sAjaxDataProp": "schedule",
|
||||
"sAjaxSource": "/showbuilder/builder-feed"
|
||||
});
|
||||
|
||||
$('[name="sb_cb_all"]').click(function(){
|
||||
var oTT = TableTools.fnGetInstance('show_builder_table');
|
||||
|
||||
if ($(this).is(":checked")) {
|
||||
var allowedNodes;
|
||||
|
||||
allowedNodes = oTable.find('tr:not(:first, .sb-header, .sb-empty, .sb-footer, .sb-not-allowed)');
|
||||
|
||||
allowedNodes.each(function(i, el){
|
||||
oTT.fnSelect(el);
|
||||
});
|
||||
}
|
||||
else {
|
||||
oTT.fnSelectNone();
|
||||
}
|
||||
});
|
||||
|
||||
var sortableConf = (function(){
|
||||
var origTrs,
|
||||
aItemData = [],
|
||||
oPrevData,
|
||||
fnAdd,
|
||||
fnMove,
|
||||
fnReceive,
|
||||
fnUpdate,
|
||||
i,
|
||||
html;
|
||||
|
||||
fnAdd = function() {
|
||||
var aMediaIds = [],
|
||||
aSchedIds = [];
|
||||
|
||||
for(i = 0; i < aItemData.length; i++) {
|
||||
aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype});
|
||||
}
|
||||
aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
|
||||
|
||||
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
|
||||
};
|
||||
|
||||
if (aData.header === true) {
|
||||
cl = 'sb-header';
|
||||
fnMove = function() {
|
||||
var aSelect = [],
|
||||
aAfter = [];
|
||||
|
||||
aSelect.push({"id": aItemData[0].id, "instance": aItemData[0].instance, "timestamp": aItemData[0].timestamp});
|
||||
aAfter.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
|
||||
|
||||
AIRTIME.showbuilder.fnMove(aSelect, aAfter);
|
||||
};
|
||||
|
||||
fnReceive = function(event, ui) {
|
||||
var aItems = [],
|
||||
oLibTT = TableTools.fnGetInstance('library_display');
|
||||
|
||||
aItems = oLibTT.fnGetSelectedData();
|
||||
|
||||
sSeparatorHTML = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>';
|
||||
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
|
||||
}
|
||||
else if (aData.footer === true) {
|
||||
node = nRow.children[0];
|
||||
cl = 'sb-footer';
|
||||
//if nothing is checked select the dragged item.
|
||||
if (aItems.length === 0) {
|
||||
aItems.push(ui.item.data("aData"));
|
||||
}
|
||||
|
||||
origTrs = aItems;
|
||||
html = ui.helper.html();
|
||||
};
|
||||
|
||||
fnUpdate = function(event, ui) {
|
||||
var prev = ui.item.prev();
|
||||
|
||||
//check the show's content status.
|
||||
if (aData.runtime > 0) {
|
||||
node.innerHTML = '<span class="ui-icon ui-icon-check"></span>';
|
||||
cl = cl + ' ui-state-highlight';
|
||||
}
|
||||
else {
|
||||
node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
|
||||
cl = cl + ' ui-state-error';
|
||||
//can't add items outside of shows.
|
||||
if (!prev.hasClass("sb-allowed")) {
|
||||
alert("Cannot schedule outside a show.");
|
||||
ui.item.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
aItemData = [];
|
||||
oPrevData = prev.data("aData");
|
||||
|
||||
//item was dragged in
|
||||
if (origTrs !== undefined) {
|
||||
|
||||
sSeparatorHTML = '<span>'+aData.fRuntime+'</span>';
|
||||
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
|
||||
}
|
||||
else if (aData.empty === true) {
|
||||
|
||||
sSeparatorHTML = '<span>Show Empty</span>';
|
||||
cl = cl + " sb-empty odd";
|
||||
|
||||
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
|
||||
}
|
||||
else {
|
||||
|
||||
node = nRow.children[0];
|
||||
if (aData.allowed === true) {
|
||||
node.innerHTML = '<input type="checkbox" name="'+aData.id+'"></input>';
|
||||
$("#show_builder_table tr.ui-draggable")
|
||||
.empty()
|
||||
.after(html);
|
||||
|
||||
aItemData = origTrs;
|
||||
origTrs = undefined;
|
||||
fnAdd();
|
||||
}
|
||||
//item was reordered.
|
||||
else {
|
||||
node.innerHTML = '';
|
||||
aItemData.push(ui.item.data("aData"));
|
||||
fnMove();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
placeholder: "placeholder show-builder-placeholder ui-state-highlight",
|
||||
forcePlaceholderSize: true,
|
||||
items: 'tr:not(:first, :last, .sb-header, .sb-footer, .sb-not-allowed)',
|
||||
receive: fnReceive,
|
||||
update: fnUpdate
|
||||
};
|
||||
}());
|
||||
|
||||
tableDiv.sortable(sortableConf);
|
||||
|
||||
$("#show_builder .fg-toolbar")
|
||||
.append('<div class="ColVis TableTools sb_delete"><button class="ui-button ui-state-default ui-state-disabled"><span>Delete</span></button></div>')
|
||||
.click(fnRemoveSelectedItems);
|
||||
|
||||
//set things like a reference to the table.
|
||||
AIRTIME.showbuilder.init(oTable);
|
||||
|
||||
//add event to cursors.
|
||||
tableDiv.find("tbody").on("click", "div.marker", function(event){
|
||||
var tr = $(this).parents("tr"),
|
||||
cursorSelClass = "cursor-selected-row";
|
||||
|
||||
if (tr.hasClass(cursorSelClass)) {
|
||||
tr.removeClass(cursorSelClass);
|
||||
}
|
||||
},
|
||||
"fnDrawCallback": function(oSettings, json) {
|
||||
var wrapperDiv,
|
||||
markerDiv,
|
||||
td;
|
||||
|
||||
//create cursor arrows.
|
||||
tableDiv.find("tr:not(:first, .sb-footer, .sb-empty, .sb-not-allowed)").each(function(i, el) {
|
||||
td = $(el).find("td:first");
|
||||
if (td.hasClass("dataTables_empty")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wrapperDiv = $("<div />", {
|
||||
"class": "innerWrapper",
|
||||
"css": {
|
||||
"height": td.height()
|
||||
}
|
||||
});
|
||||
markerDiv = $("<div />", {
|
||||
"class": "marker"
|
||||
});
|
||||
|
||||
td.append(markerDiv).wrapInner(wrapperDiv);
|
||||
});
|
||||
},
|
||||
"fnHeaderCallback": function(nHead) {
|
||||
$(nHead).find("input[type=checkbox]").attr("checked", false);
|
||||
},
|
||||
//remove any selected nodes before the draw.
|
||||
"fnPreDrawCallback": function( oSettings ) {
|
||||
var oTT = TableTools.fnGetInstance('show_builder_table');
|
||||
oTT.fnSelectNone();
|
||||
},
|
||||
|
||||
"oColVis": {
|
||||
"aiExclude": [ 0, 1 ]
|
||||
},
|
||||
|
||||
"oColReorder": {
|
||||
"iFixedColumns": 2
|
||||
},
|
||||
|
||||
"oTableTools": {
|
||||
"sRowSelect": "multi",
|
||||
"aButtons": [],
|
||||
"fnPreRowSelect": function ( e ) {
|
||||
var node = e.currentTarget;
|
||||
//don't select separating rows, or shows without privileges.
|
||||
if ($(node).hasClass("sb-header")
|
||||
|| $(node).hasClass("sb-footer")
|
||||
|| $(node).hasClass("sb-empty")
|
||||
|| $(node).hasClass("sb-not-allowed")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
"fnRowSelected": function ( node ) {
|
||||
|
||||
//seems to happen if everything is selected
|
||||
if ( node === null) {
|
||||
oTable.find("input[type=checkbox]").attr("checked", true);
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", true);
|
||||
}
|
||||
},
|
||||
"fnRowDeselected": function ( node ) {
|
||||
|
||||
//seems to happen if everything is deselected
|
||||
if ( node === null) {
|
||||
var oTable = $("#show_builder_table").dataTable();
|
||||
oTable.find("input[type=checkbox]").attr("checked", false);
|
||||
}
|
||||
else {
|
||||
$(node).find("input[type=checkbox]").attr("checked", false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// R = ColReorderResize, C = ColVis, T = TableTools
|
||||
"sDom": 'Rr<"H"CT>t<"F">',
|
||||
|
||||
"sAjaxDataProp": "schedule",
|
||||
"sAjaxSource": "/showbuilder/builder-feed"
|
||||
});
|
||||
|
||||
$('[name="sb_cb_all"]').click(function(){
|
||||
var oTT = TableTools.fnGetInstance('show_builder_table');
|
||||
|
||||
if ($(this).is(":checked")) {
|
||||
var allowedNodes;
|
||||
|
||||
allowedNodes = oTable.find('tr:not(:first, .sb-header, .sb-empty, .sb-footer, .sb-not-allowed)');
|
||||
|
||||
allowedNodes.each(function(i, el){
|
||||
oTT.fnSelect(el);
|
||||
});
|
||||
}
|
||||
else {
|
||||
oTT.fnSelectNone();
|
||||
}
|
||||
});
|
||||
|
||||
$("#sb_date_start").datepicker(oBaseDatePickerSettings);
|
||||
$("#sb_time_start").timepicker(oBaseTimePickerSettings);
|
||||
$("#sb_date_end").datepicker(oBaseDatePickerSettings);
|
||||
$("#sb_time_end").timepicker(oBaseTimePickerSettings);
|
||||
|
||||
$("#sb_submit").click(function(ev){
|
||||
var fn,
|
||||
oRange,
|
||||
op;
|
||||
|
||||
oRange = fnGetScheduleRange();
|
||||
|
||||
fn = oTable.fnSettings().fnServerData;
|
||||
fn.start = oRange.start;
|
||||
fn.end = oRange.end;
|
||||
|
||||
op = $("div.sb-advanced-options");
|
||||
if (op.is(":visible")) {
|
||||
|
||||
if (fn.ops === undefined) {
|
||||
fn.ops = {};
|
||||
}
|
||||
fn.ops.showFilter = op.find("#sb_show_filter").val();
|
||||
fn.ops.myShows = op.find("#sb_my_shows").is(":checked") ? 1 : 0;
|
||||
}
|
||||
|
||||
oTable.fnDraw();
|
||||
});
|
||||
|
||||
var sortableConf = (function(){
|
||||
var origTrs,
|
||||
aItemData = [],
|
||||
oPrevData,
|
||||
fnAdd,
|
||||
fnMove,
|
||||
fnReceive,
|
||||
fnUpdate,
|
||||
i,
|
||||
html;
|
||||
|
||||
fnAdd = function() {
|
||||
var aMediaIds = [],
|
||||
aSchedIds = [],
|
||||
oLibTT = TableTools.fnGetInstance('library_display');
|
||||
|
||||
for(i=0; i < aItemData.length; i++) {
|
||||
aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype});
|
||||
}
|
||||
aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
|
||||
|
||||
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
|
||||
};
|
||||
|
||||
fnMove = function() {
|
||||
var aSelect = [],
|
||||
aAfter = [];
|
||||
|
||||
aSelect.push({"id": aItemData[0].id, "instance": aItemData[0].instance, "timestamp": aItemData[0].timestamp});
|
||||
aAfter.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
|
||||
|
||||
AIRTIME.showbuilder.fnMove(aSelect, aAfter);
|
||||
};
|
||||
|
||||
fnReceive = function(event, ui) {
|
||||
var selected = $('#library_display tr:not(:first) input:checked').parents('tr'),
|
||||
aItems = [];
|
||||
|
||||
//if nothing is checked select the dragged item.
|
||||
if (selected.length === 0) {
|
||||
selected = ui.item;
|
||||
}
|
||||
|
||||
selected.each(function(i, el) {
|
||||
aItems.push($(el).data("aData"));
|
||||
});
|
||||
|
||||
origTrs = aItems;
|
||||
html = ui.helper.html();
|
||||
};
|
||||
|
||||
fnUpdate = function(event, ui) {
|
||||
var prev = ui.item.prev();
|
||||
|
||||
//can't add items outside of shows.
|
||||
if (prev.hasClass("sb-footer")) {
|
||||
alert("Cannot add an item outside a show.");
|
||||
ui.item.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
aItemData = [];
|
||||
oPrevData = prev.data("aData");
|
||||
|
||||
//item was dragged in
|
||||
if (origTrs !== undefined) {
|
||||
|
||||
$("#show_builder_table tr.ui-draggable")
|
||||
.empty()
|
||||
.after(html);
|
||||
|
||||
aItemData = origTrs;
|
||||
origTrs = undefined;
|
||||
fnAdd();
|
||||
}
|
||||
//item was reordered.
|
||||
else {
|
||||
aItemData.push(ui.item.data("aData"));
|
||||
fnMove();
|
||||
tr.addClass(cursorSelClass);
|
||||
}
|
||||
};
|
||||
|
||||
//check if add button can still be enabled.
|
||||
AIRTIME.library.events.enableAddButtonCheck();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
placeholder: "placeholder show-builder-placeholder ui-state-highlight",
|
||||
forcePlaceholderSize: true,
|
||||
items: 'tr:not(:first, :last, .sb-header, .sb-footer, .sb-not-allowed)',
|
||||
receive: fnReceive,
|
||||
update: fnUpdate
|
||||
};
|
||||
}());
|
||||
};
|
||||
|
||||
tableDiv.sortable(sortableConf);
|
||||
mod.init = function(oTable) {
|
||||
oSchedTable = oTable;
|
||||
};
|
||||
|
||||
$("#show_builder .fg-toolbar")
|
||||
.append('<div class="ColVis TableTools"><button class="ui-button ui-state-default"><span>Delete</span></button></div>')
|
||||
.click(fnRemoveSelectedItems);
|
||||
return AIRTIME;
|
||||
|
||||
//set things like a reference to the table.
|
||||
AIRTIME.showbuilder.init(oTable);
|
||||
|
||||
//add event to cursors.
|
||||
tableDiv.find("tbody").on("click", "div.marker", function(event){
|
||||
var tr = $(this).parents("tr");
|
||||
|
||||
if (tr.hasClass("cursor-selected-row")) {
|
||||
tr.removeClass("cursor-selected-row");
|
||||
}
|
||||
else {
|
||||
tr.addClass("cursor-selected-row");
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
}(AIRTIME || {}));
|
|
@ -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();
|
||||
});
|
|
@ -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;
|
|
@ -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
|
|
@ -81,14 +81,6 @@ class ApiClientInterface:
|
|||
def get_media(self, src, dst):
|
||||
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
|
||||
# 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"])
|
||||
|
||||
response = ""
|
||||
status = 0
|
||||
try:
|
||||
response_json = self.get_response_from_server(export_url)
|
||||
response = json.loads(response_json)
|
||||
status = response['check']
|
||||
success = True
|
||||
except Exception, e:
|
||||
logger.error(e)
|
||||
success = False
|
||||
|
||||
return status, response
|
||||
return success, response
|
||||
|
||||
|
||||
def get_media(self, uri, dst):
|
||||
|
@ -285,32 +277,6 @@ class AirTimeApiClient(ApiClientInterface):
|
|||
except Exception, 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
|
||||
currently playing *song*. We get passed a JSON string which we handed to
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
[loggers]
|
||||
keys=root,fetch,push,recorder
|
||||
keys=root,fetch,push,recorder,message_h
|
||||
|
||||
[handlers]
|
||||
keys=pypo,recorder
|
||||
keys=pypo,recorder,message_h
|
||||
|
||||
[formatters]
|
||||
keys=simpleFormatter
|
||||
|
@ -29,6 +29,12 @@ handlers=recorder
|
|||
qualname=recorder
|
||||
propagate=0
|
||||
|
||||
[logger_message_h]
|
||||
level=DEBUG
|
||||
handlers=message_h
|
||||
qualname=message_h
|
||||
propagate=0
|
||||
|
||||
[handler_pypo]
|
||||
class=logging.handlers.RotatingFileHandler
|
||||
level=DEBUG
|
||||
|
@ -41,6 +47,12 @@ level=DEBUG
|
|||
formatter=simpleFormatter
|
||||
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]
|
||||
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
|
||||
datefmt=
|
||||
|
|
|
@ -16,6 +16,7 @@ from Queue import Queue
|
|||
from pypopush import PypoPush
|
||||
from pypofetch import PypoFetch
|
||||
from recorder import Recorder
|
||||
from pypomessagehandler import PypoMessageHandler
|
||||
|
||||
from configobj import ConfigObj
|
||||
|
||||
|
@ -55,23 +56,16 @@ except Exception, e:
|
|||
class Global:
|
||||
def __init__(self):
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
self.set_export_source('scheduler')
|
||||
|
||||
def selfcheck(self):
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
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):
|
||||
self.api_client.test()
|
||||
|
||||
"""
|
||||
def check_schedule(self, export_source):
|
||||
def check_schedule(self):
|
||||
logger = logging.getLogger()
|
||||
|
||||
try:
|
||||
|
@ -127,11 +121,19 @@ if __name__ == '__main__':
|
|||
api_client = api_client.api_client_factory(config)
|
||||
api_client.register_component("pypo")
|
||||
|
||||
q = Queue()
|
||||
|
||||
pypoFetch_q = Queue()
|
||||
recorder_q = Queue()
|
||||
|
||||
pp = PypoPush(q)
|
||||
pypoPush_q = Queue()
|
||||
|
||||
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.start()
|
||||
|
||||
|
@ -139,12 +141,11 @@ if __name__ == '__main__':
|
|||
recorder.daemon = True
|
||||
recorder.start()
|
||||
|
||||
pf = PypoFetch(q, recorder_q)
|
||||
pf.daemon = True
|
||||
pf.start()
|
||||
|
||||
#pp.join()
|
||||
pmh.join()
|
||||
pp.join()
|
||||
pf.join()
|
||||
recorder.join()
|
||||
|
||||
logger.info("pypo fetch exit")
|
||||
sys.exit()
|
||||
"""
|
||||
|
|
|
@ -34,7 +34,7 @@ import json
|
|||
from configobj import ConfigObj
|
||||
|
||||
# custom imports
|
||||
from util import *
|
||||
#from util import *
|
||||
from api_clients import *
|
||||
|
||||
# Set up command-line options
|
||||
|
|
|
@ -16,12 +16,6 @@ from datetime import datetime
|
|||
from datetime import timedelta
|
||||
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 configobj import ConfigObj
|
||||
|
@ -42,30 +36,31 @@ except Exception, e:
|
|||
sys.exit()
|
||||
|
||||
class PypoFetch(Thread):
|
||||
def __init__(self, q, recorder_q):
|
||||
def __init__(self, pypoFetch_q, pypoPush_q):
|
||||
Thread.__init__(self)
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
self.set_export_source('scheduler')
|
||||
self.queue = q
|
||||
self.recorder_queue = recorder_q
|
||||
self.schedule_data = []
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.info("PypoFetch: init complete")
|
||||
self.fetch_queue = pypoFetch_q
|
||||
self.push_queue = pypoPush_q
|
||||
|
||||
self.logger = logging.getLogger();
|
||||
|
||||
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:
|
||||
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)
|
||||
if not os.path.isdir(dir):
|
||||
"""
|
||||
We get here if path does not exist, or path does exist but
|
||||
is a file. We are not handling the second case, but don't
|
||||
think we actually care about handling it.
|
||||
"""
|
||||
self.logger.debug("Cache dir does not exist. Creating...")
|
||||
os.makedirs(dir)
|
||||
except Exception, e:
|
||||
logger.error(e)
|
||||
return False
|
||||
|
||||
return True
|
||||
pass
|
||||
|
||||
self.schedule_data = []
|
||||
self.logger.info("PypoFetch: init complete")
|
||||
|
||||
"""
|
||||
Handle a message from RabbitMQ, put it into our yucky global var.
|
||||
|
@ -73,55 +68,51 @@ class PypoFetch(Thread):
|
|||
"""
|
||||
def handle_message(self, message):
|
||||
try:
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.info("Received event from RabbitMQ: %s" % message)
|
||||
self.logger.info("Received event from Pypo Message Handler: %s" % message)
|
||||
|
||||
m = json.loads(message)
|
||||
command = m['event_type']
|
||||
logger.info("Handling command: " + command)
|
||||
self.logger.info("Handling command: " + command)
|
||||
|
||||
if command == 'update_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':
|
||||
logger.info("Updating stream setting...")
|
||||
self.logger.info("Updating stream setting...")
|
||||
self.regenerateLiquidsoapConf(m['setting'])
|
||||
elif command == 'update_stream_format':
|
||||
logger.info("Updating stream format...")
|
||||
self.logger.info("Updating stream format...")
|
||||
self.update_liquidsoap_stream_format(m['stream_format'])
|
||||
elif command == 'update_station_name':
|
||||
logger.info("Updating station name...")
|
||||
self.logger.info("Updating station name...")
|
||||
self.update_liquidsoap_station_name(m['station_name'])
|
||||
elif command == 'cancel_current_show':
|
||||
logger.info("Cancel current show command received...")
|
||||
self.logger.info("Cancel current show command received...")
|
||||
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:
|
||||
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):
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.debug('Notifying Liquidsoap to stop playback.')
|
||||
self.logger.debug('Notifying Liquidsoap to stop playback.')
|
||||
try:
|
||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||
tn.write('source.skip\n')
|
||||
tn.write('exit\n')
|
||||
tn.read_all()
|
||||
except Exception, e:
|
||||
logger.debug(e)
|
||||
logger.debug('Could not connect to liquidsoap')
|
||||
self.logger.debug(e)
|
||||
self.logger.debug('Could not connect to liquidsoap')
|
||||
|
||||
def regenerateLiquidsoapConf(self, setting):
|
||||
logger = logging.getLogger('fetch')
|
||||
existing = {}
|
||||
# create a temp file
|
||||
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
|
||||
while 1:
|
||||
line = fh.readline()
|
||||
|
@ -151,7 +142,7 @@ class PypoFetch(Thread):
|
|||
#restart flag
|
||||
restart = False
|
||||
|
||||
logger.info("Looking for changes...")
|
||||
self.logger.info("Looking for changes...")
|
||||
# look for changes
|
||||
for s in setting:
|
||||
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
|
||||
# This is the case where restart is required no matter what
|
||||
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;
|
||||
else:
|
||||
stream, dump = s[u'keyname'].split('_',1)
|
||||
if "_output" in s[u'keyname']:
|
||||
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;
|
||||
state_change_restart[stream] = True
|
||||
elif ( s[u'value'] != 'disabled'):
|
||||
|
@ -177,22 +168,22 @@ class PypoFetch(Thread):
|
|||
if stream not in change:
|
||||
change[stream] = False
|
||||
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
|
||||
|
||||
# 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():
|
||||
if k == "sound_device" and v:
|
||||
restart = True
|
||||
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
|
||||
# rewrite
|
||||
if restart:
|
||||
fh = open('/etc/airtime/liquidsoap.cfg', 'w')
|
||||
logger.info("Rewriting liquidsoap.cfg...")
|
||||
self.logger.info("Rewriting liquidsoap.cfg...")
|
||||
fh.write("################################################\n")
|
||||
fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n")
|
||||
fh.write("################################################\n")
|
||||
|
@ -214,17 +205,18 @@ class PypoFetch(Thread):
|
|||
fh.close()
|
||||
# restarting pypo.
|
||||
# we could just restart liquidsoap but it take more time somehow.
|
||||
logger.info("Restarting pypo...")
|
||||
self.logger.info("Restarting pypo...")
|
||||
sys.exit(0)
|
||||
else:
|
||||
logger.info("No change detected in setting...")
|
||||
self.logger.info("No change detected in setting...")
|
||||
self.update_liquidsoap_connection_status()
|
||||
"""
|
||||
|
||||
def update_liquidsoap_connection_status(self):
|
||||
"""
|
||||
updates the status of liquidsoap connection to the streaming server
|
||||
This fucntion updates the bootup time variable in liquidsoap script
|
||||
"""
|
||||
def update_liquidsoap_connection_status(self):
|
||||
logger = logging.getLogger('fetch')
|
||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||
# 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
|
||||
|
@ -242,7 +234,7 @@ class PypoFetch(Thread):
|
|||
# streamin info is in the form of:
|
||||
# eg. s1:true,2:true,3:false
|
||||
streams = stream_info.split(",")
|
||||
logger.info(streams)
|
||||
self.logger.info(streams)
|
||||
|
||||
fake_time = current_time + 1
|
||||
for s in streams:
|
||||
|
@ -252,46 +244,35 @@ class PypoFetch(Thread):
|
|||
if(status == "true"):
|
||||
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):
|
||||
# Push stream metadata to liquidsoap
|
||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||
try:
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.info(LS_HOST)
|
||||
logger.info(LS_PORT)
|
||||
self.logger.info(LS_HOST)
|
||||
self.logger.info(LS_PORT)
|
||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||
command = ('vars.stream_metadata_type %s\n' % stream_format).encode('utf-8')
|
||||
logger.info(command)
|
||||
self.logger.info(command)
|
||||
tn.write(command)
|
||||
tn.write('exit\n')
|
||||
tn.read_all()
|
||||
except Exception, e:
|
||||
logger.error("Exception %s", e)
|
||||
self.logger.error("Exception %s", e)
|
||||
|
||||
def update_liquidsoap_station_name(self, station_name):
|
||||
# Push stream metadata to liquidsoap
|
||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||
try:
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.info(LS_HOST)
|
||||
logger.info(LS_PORT)
|
||||
self.logger.info(LS_HOST)
|
||||
self.logger.info(LS_PORT)
|
||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||
command = ('vars.station_name %s\n' % station_name).encode('utf-8')
|
||||
logger.info(command)
|
||||
self.logger.info(command)
|
||||
tn.write(command)
|
||||
tn.write('exit\n')
|
||||
tn.read_all()
|
||||
except Exception, e:
|
||||
logger.error("Exception %s", e)
|
||||
self.logger.error("Exception %s", e)
|
||||
|
||||
"""
|
||||
Process the schedule
|
||||
|
@ -301,191 +282,157 @@ class PypoFetch(Thread):
|
|||
to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
|
||||
- runs the cleanup routine, to get rid of unused cached files
|
||||
"""
|
||||
def process_schedule(self, schedule_data, export_source, bootstrapping):
|
||||
logger = logging.getLogger('fetch')
|
||||
playlists = schedule_data["playlists"]
|
||||
def process_schedule(self, schedule_data, bootstrapping):
|
||||
self.logger.debug(schedule_data)
|
||||
media = schedule_data["media"]
|
||||
|
||||
# Download all the media and put playlists in liquidsoap "annotate" format
|
||||
try:
|
||||
liquidsoap_playlists = self.prepare_playlists(playlists, bootstrapping)
|
||||
except Exception, e: logger.error("%s", e)
|
||||
media = self.prepare_media(media, bootstrapping)
|
||||
except Exception, e: self.logger.error("%s", e)
|
||||
|
||||
# Send the data to pypo-push
|
||||
scheduled_data = dict()
|
||||
scheduled_data['liquidsoap_playlists'] = liquidsoap_playlists
|
||||
scheduled_data['schedule'] = playlists
|
||||
self.queue.put(scheduled_data)
|
||||
self.logger.debug("Pushing to pypo-push: "+ str(media))
|
||||
self.push_queue.put(media)
|
||||
|
||||
"""
|
||||
TODO
|
||||
# cleanup
|
||||
try: self.cleanup(self.export_source)
|
||||
except Exception, e: 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)
|
||||
try: self.cleanup()
|
||||
except Exception, e: self.logger.error("%s", e)
|
||||
"""
|
||||
|
||||
def parse_shows(self, m):
|
||||
logger = logging.getLogger('fetch')
|
||||
logger.info("Parsing recording show schedules...")
|
||||
shows_to_record = {}
|
||||
shows = m['shows']
|
||||
for show in shows:
|
||||
show_starts = self.getDateTimeObj(show[u'starts'])
|
||||
show_end = self.getDateTimeObj(show[u'ends'])
|
||||
time_delta = show_end - show_starts
|
||||
|
||||
shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']]
|
||||
self.recorder_queue.put(shows_to_record)
|
||||
logger.info(shows_to_record)
|
||||
|
||||
|
||||
"""
|
||||
In this function every audio file is cut as necessary (cue_in/cue_out != 0)
|
||||
and stored in a playlist folder.
|
||||
file is e.g. 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3
|
||||
"""
|
||||
def prepare_playlists(self, playlists, bootstrapping):
|
||||
logger = logging.getLogger('fetch')
|
||||
|
||||
liquidsoap_playlists = dict()
|
||||
|
||||
# Dont do anything if playlists is empty
|
||||
if not playlists:
|
||||
logger.debug("Schedule is empty.")
|
||||
return liquidsoap_playlists
|
||||
|
||||
scheduleKeys = sorted(playlists.iterkeys())
|
||||
|
||||
|
||||
def prepare_media(self, media, bootstrapping):
|
||||
"""
|
||||
Iterate through the list of media items in "media" and
|
||||
download them.
|
||||
"""
|
||||
try:
|
||||
for pkey in scheduleKeys:
|
||||
logger.info("Playlist starting at %s", pkey)
|
||||
playlist = playlists[pkey]
|
||||
mediaKeys = sorted(media.iterkeys())
|
||||
for mkey in mediaKeys:
|
||||
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
|
||||
try:
|
||||
os.mkdir(self.cache_dir + str(pkey))
|
||||
"""
|
||||
Extract year, month, date from mkey
|
||||
"""
|
||||
y_m_d = mkey[0:10]
|
||||
download_dir = os.path.join(self.cache_dir, y_m_d)
|
||||
try:
|
||||
os.makedirs(os.path.join(self.cache_dir, y_m_d))
|
||||
except Exception, e:
|
||||
pass
|
||||
fileExt = os.path.splitext(media_item['uri'])[1]
|
||||
dst = os.path.join(download_dir, media_item['id']+fileExt)
|
||||
except Exception, e:
|
||||
logger.warning(e)
|
||||
self.logger.warning(e)
|
||||
|
||||
if self.handle_media_file(media_item, dst):
|
||||
entry = self.create_liquidsoap_annotation(media_item, dst)
|
||||
media_item['show_name'] = "TODO"
|
||||
media_item["annotation"] = entry
|
||||
|
||||
ls_playlist = self.handle_media_file(playlist, pkey, bootstrapping)
|
||||
|
||||
liquidsoap_playlists[pkey] = ls_playlist
|
||||
except Exception, e:
|
||||
logger.error("%s", e)
|
||||
return liquidsoap_playlists
|
||||
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_out']) / 1000, \
|
||||
float(media['cue_in']), \
|
||||
float(media['cue_out']), \
|
||||
media['row_id'], dst)
|
||||
|
||||
"""
|
||||
Download and cache the media files.
|
||||
This handles both remote and local files.
|
||||
Returns an updated ls_playlist string.
|
||||
"""
|
||||
def handle_media_file(self, playlist, pkey, bootstrapping):
|
||||
logger = logging.getLogger('fetch')
|
||||
return entry
|
||||
|
||||
ls_playlist = []
|
||||
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')
|
||||
|
||||
sortedKeys = sorted(playlist['medias'].iterkeys())
|
||||
|
||||
for key in sortedKeys:
|
||||
media = playlist['medias'][key]
|
||||
logger.debug("Processing track %s", media['uri'])
|
||||
|
||||
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)
|
||||
|
||||
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'))
|
||||
delta = dtnow - media_start #we get a TimeDelta object from this operation
|
||||
self.logger.info("Starting media item at %d second point", delta.seconds)
|
||||
|
||||
"""
|
||||
Set the cue_in. This is used by Liquidsoap to determine at what point in the media
|
||||
item it should start playing. If the cue_in happens to be > cue_out, then make cue_in = cue_out
|
||||
"""
|
||||
media_item['cue_in'] = delta.seconds + 10 if delta.seconds + 10 < media_item['cue_out'] else media_item['cue_out']
|
||||
|
||||
"""
|
||||
Set the start time, which is used by pypo-push to determine when a media item is scheduled.
|
||||
Pushing the start time into the future will ensure pypo-push will push this to Liquidsoap.
|
||||
"""
|
||||
td = timedelta(seconds=10)
|
||||
media_item['start'] = (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S')
|
||||
self.logger.info("Crash detected, setting playlist to restart at %s", (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S'))
|
||||
|
||||
def handle_media_file(self, media_item, dst):
|
||||
"""
|
||||
Download and cache the media item.
|
||||
"""
|
||||
|
||||
self.logger.debug("Processing track %s", media_item['uri'])
|
||||
|
||||
fileExt = os.path.splitext(media['uri'])[1]
|
||||
try:
|
||||
dst = "%s%s/%s%s" % (self.cache_dir, pkey, media['id'], fileExt)
|
||||
|
||||
# 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:
|
||||
logger.error("%s", e)
|
||||
fsize = 0
|
||||
|
||||
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:
|
||||
pl_entry = \
|
||||
'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['export_source'], media['id'], 0, \
|
||||
float(media['fade_in']) / 1000, \
|
||||
float(media['fade_out']) / 1000, \
|
||||
float(media['cue_in']), \
|
||||
float(media['cue_out']), \
|
||||
media['row_id'], dst)
|
||||
return True
|
||||
except Exception, e:
|
||||
self.logger.error("%s", e)
|
||||
fsize = 0
|
||||
else:
|
||||
self.logger.warning("Cannot read file %s.", dst)
|
||||
|
||||
"""
|
||||
Tracks are only added to the playlist if they are accessible
|
||||
on the file system and larger than 0 bytes.
|
||||
So this can lead to playlists shorter than expectet.
|
||||
(there is a hardware silence detector for this cases...)
|
||||
"""
|
||||
entry = dict()
|
||||
entry['type'] = 'file'
|
||||
entry['annotate'] = pl_entry
|
||||
entry['show_name'] = playlist['show_name']
|
||||
ls_playlist.append(entry)
|
||||
|
||||
else:
|
||||
logger.warning("zero-size file - skipping %s. will not add it to playlist at %s", media['uri'], dst)
|
||||
|
||||
else:
|
||||
logger.warning("something went wrong. file %s not available. will not add it to playlist", dst)
|
||||
|
||||
except Exception, e: logger.info("%s", e)
|
||||
return ls_playlist
|
||||
except Exception, e:
|
||||
self.logger.info("%s", e)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
"""
|
||||
Download a file from a remote server and store it in the cache.
|
||||
"""
|
||||
def handle_remote_file(self, media, dst):
|
||||
logger = logging.getLogger('fetch')
|
||||
def download_file(self, media_item, dst):
|
||||
if os.path.isfile(dst):
|
||||
pass
|
||||
#logger.debug("file already in cache: %s", dst)
|
||||
#self.logger.debug("file already in cache: %s", dst)
|
||||
else:
|
||||
logger.debug("try to download %s", media['uri'])
|
||||
self.api_client.get_media(media['uri'], dst)
|
||||
self.logger.debug("try to download %s", media_item['uri'])
|
||||
self.api_client.get_media(media_item['uri'], dst)
|
||||
|
||||
"""
|
||||
Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
|
||||
and deletes them.
|
||||
"""
|
||||
def cleanup(self, export_source):
|
||||
logger = logging.getLogger('fetch')
|
||||
|
||||
def cleanup(self):
|
||||
offset = 3600 * int(config["cache_for"])
|
||||
now = time.time()
|
||||
|
||||
|
@ -495,86 +442,42 @@ class PypoFetch(Thread):
|
|||
timestamp = calendar.timegm(time.strptime(dir, "%Y-%m-%d-%H-%M-%S"))
|
||||
if (now - timestamp) > offset:
|
||||
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))
|
||||
except Exception, e:
|
||||
logger.error("%s", e)
|
||||
self.logger.error("%s", e)
|
||||
pass
|
||||
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:
|
||||
logger.error(e)
|
||||
self.logger.error(e)
|
||||
|
||||
|
||||
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
|
||||
# most recent schedule. After that we can just wait for updates.
|
||||
status, self.schedule_data = self.api_client.get_schedule()
|
||||
if status == 1:
|
||||
logger.info("Bootstrap schedule received: %s", self.schedule_data)
|
||||
self.process_schedule(self.schedule_data, "scheduler", 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)
|
||||
success, self.schedule_data = self.api_client.get_schedule()
|
||||
if success:
|
||||
self.logger.info("Bootstrap schedule received: %s", self.schedule_data)
|
||||
self.process_schedule(self.schedule_data, True)
|
||||
|
||||
loops = 1
|
||||
while True:
|
||||
logger.info("Loop #%s", loops)
|
||||
self.logger.info("Loop #%s", loops)
|
||||
try:
|
||||
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 MessageStateError, m:
|
||||
logger.error("Message ACK error: %s", m)
|
||||
message = self.fetch_queue.get(block=True, timeout=3600)
|
||||
self.handle_message(message)
|
||||
except Exception, 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)
|
||||
self.logger.error("Exception, %s", e)
|
||||
|
||||
status, self.schedule_data = self.api_client.get_schedule()
|
||||
if status == 1:
|
||||
self.process_schedule(self.schedule_data, "scheduler", 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)
|
||||
success, self.schedule_data = self.api_client.get_schedule()
|
||||
if success:
|
||||
self.process_schedule(self.schedule_data, False)
|
||||
|
||||
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()
|
||||
"""
|
||||
Entry point of the thread
|
||||
"""
|
||||
self.main()
|
||||
|
|
|
@ -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()
|
||||
|
|
@ -34,166 +34,147 @@ class PypoPush(Thread):
|
|||
def __init__(self, q):
|
||||
Thread.__init__(self)
|
||||
self.api_client = api_client.api_client_factory(config)
|
||||
self.set_export_source('scheduler')
|
||||
self.queue = q
|
||||
|
||||
self.schedule = dict()
|
||||
self.playlists = dict()
|
||||
self.media = dict()
|
||||
|
||||
self.liquidsoap_state_play = True
|
||||
self.push_ahead = 10
|
||||
|
||||
def set_export_source(self, export_source):
|
||||
self.export_source = export_source
|
||||
self.cache_dir = config["cache_dir"] + self.export_source + '/'
|
||||
self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle"
|
||||
self.last_end_time = 0
|
||||
|
||||
"""
|
||||
The Push Loop - the push loop periodically checks if there is a playlist
|
||||
that should be scheduled at the current time.
|
||||
If yes, the current liquidsoap playlist gets replaced with the corresponding one,
|
||||
then liquidsoap is asked (via telnet) to reload and immediately play it.
|
||||
"""
|
||||
def push(self, export_source):
|
||||
logger = logging.getLogger('push')
|
||||
self.logger = logging.getLogger('push')
|
||||
|
||||
def push(self):
|
||||
"""
|
||||
The Push Loop - the push loop periodically checks if there is a playlist
|
||||
that should be scheduled at the current time.
|
||||
If yes, the current liquidsoap playlist gets replaced with the corresponding one,
|
||||
then liquidsoap is asked (via telnet) to reload and immediately play it.
|
||||
"""
|
||||
|
||||
timenow = time.time()
|
||||
# get a new schedule from pypo-fetch
|
||||
if not self.queue.empty():
|
||||
# make sure we get the latest schedule
|
||||
while not self.queue.empty():
|
||||
scheduled_data = self.queue.get()
|
||||
logger.debug("Received data from pypo-fetch")
|
||||
self.schedule = scheduled_data['schedule']
|
||||
self.playlists = scheduled_data['liquidsoap_playlists']
|
||||
|
||||
logger.debug('schedule %s' % json.dumps(self.schedule))
|
||||
logger.debug('playlists %s' % json.dumps(self.playlists))
|
||||
self.media = self.queue.get()
|
||||
self.logger.debug("Received data from pypo-fetch")
|
||||
self.logger.debug('media %s' % json.dumps(self.media))
|
||||
|
||||
schedule = self.schedule
|
||||
playlists = self.playlists
|
||||
media = self.media
|
||||
|
||||
currently_on_air = False
|
||||
if schedule:
|
||||
if media:
|
||||
tnow = time.gmtime(timenow)
|
||||
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_tcoming_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming[0], tcoming[1], tcoming[2], tcoming[3], tcoming[4], tcoming[5])
|
||||
|
||||
for pkey in schedule:
|
||||
plstart = schedule[pkey]['start'][0:19]
|
||||
|
||||
if str_tnow_s <= plstart and plstart < str_tcoming_s:
|
||||
logger.debug('Preparing to push playlist scheduled at: %s', pkey)
|
||||
playlist = schedule[pkey]
|
||||
|
||||
|
||||
# We have a match, replace the current playlist and
|
||||
# force liquidsoap to refresh.
|
||||
if (self.push_liquidsoap(pkey, schedule, playlists) == 1):
|
||||
logger.debug("Pushed to liquidsoap, updating 'played' status.")
|
||||
for key in media:
|
||||
media_item = media[key]
|
||||
item_start = media_item['start'][0:19]
|
||||
|
||||
if str_tnow_s <= item_start and item_start < str_tcoming_s:
|
||||
"""
|
||||
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):
|
||||
self.logger.debug("Pushed to liquidsoap, updating 'played' status.")
|
||||
|
||||
currently_on_air = True
|
||||
self.liquidsoap_state_play = True
|
||||
|
||||
# Call API to update schedule states
|
||||
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
|
||||
still be playing audio even though the show has ended ('currently_on_air = False' means no show is scheduled)
|
||||
See CC-3231.
|
||||
This is a temporary solution for Airtime 2.0
|
||||
"""
|
||||
if not currently_on_air and self.liquidsoap_state_play:
|
||||
logger.debug('Notifying Liquidsoap to stop playback.')
|
||||
try:
|
||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||
tn.write('source.skip\n')
|
||||
tn.write('exit\n')
|
||||
tn.read_all()
|
||||
except Exception, e:
|
||||
logger.debug(e)
|
||||
logger.debug('Could not connect to liquidsoap')
|
||||
|
||||
self.liquidsoap_state_play = False
|
||||
|
||||
|
||||
def push_liquidsoap(self, pkey, schedule, playlists):
|
||||
logger = logging.getLogger('push')
|
||||
|
||||
|
||||
def push_to_liquidsoap(self, media_item):
|
||||
"""
|
||||
This function looks at the media item, and either pushes it to the Liquidsoap
|
||||
queue immediately, or if the queue is empty - waits until the start time of the
|
||||
media item before pushing it.
|
||||
"""
|
||||
try:
|
||||
playlist = playlists[pkey]
|
||||
plstart = schedule[pkey]['start'][0:19]
|
||||
|
||||
#strptime returns struct_time in local time
|
||||
#mktime takes a time_struct and returns a floating point
|
||||
#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.
|
||||
epoch_now = time.time()
|
||||
|
||||
logger.debug("Epoch start: %s" % epoch_start)
|
||||
logger.debug("Epoch now: %s" % epoch_now)
|
||||
|
||||
sleep_time = epoch_start - epoch_now;
|
||||
|
||||
if sleep_time < 0:
|
||||
sleep_time = 0
|
||||
|
||||
logger.debug('sleeping for %s s' % (sleep_time))
|
||||
time.sleep(sleep_time)
|
||||
|
||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||
|
||||
#skip the currently playing song if any.
|
||||
logger.debug("source.skip\n")
|
||||
tn.write("source.skip\n")
|
||||
|
||||
# Get any extra information for liquidsoap (which will be sent back to us)
|
||||
liquidsoap_data = self.api_client.get_liquidsoap_data(pkey, schedule)
|
||||
|
||||
#Sending schedule table row id string.
|
||||
logger.debug("vars.pypo_data %s\n"%(liquidsoap_data["schedule_id"]))
|
||||
tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
|
||||
|
||||
logger.debug('Preparing to push playlist %s' % pkey)
|
||||
for item in playlist:
|
||||
annotate = item['annotate']
|
||||
tn.write(str('queue.push %s\n' % annotate.encode('utf-8')))
|
||||
|
||||
show_name = item['show_name']
|
||||
tn.write(str('vars.show_name %s\n' % show_name.encode('utf-8')))
|
||||
|
||||
tn.write("exit\n")
|
||||
logger.debug(tn.read_all())
|
||||
|
||||
status = 1
|
||||
if media_item["start"] == self.last_end_time:
|
||||
"""
|
||||
this media item is attached to the end of the last
|
||||
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:
|
||||
logger.error('%s', e)
|
||||
status = 0
|
||||
return status
|
||||
return 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.
|
||||
"""
|
||||
|
||||
mi_start = media_item['start'][0:19]
|
||||
|
||||
#strptime returns struct_time in local time
|
||||
epoch_start = calendar.timegm(time.strptime(mi_start, '%Y-%m-%d-%H-%M-%S'))
|
||||
|
||||
#Return the time as a floating point number expressed in seconds since the epoch, in UTC.
|
||||
epoch_now = time.time()
|
||||
|
||||
self.logger.debug("Epoch start: %s" % epoch_start)
|
||||
self.logger.debug("Epoch now: %s" % epoch_now)
|
||||
|
||||
sleep_time = epoch_start - epoch_now
|
||||
|
||||
if sleep_time < 0:
|
||||
sleep_time = 0
|
||||
|
||||
self.logger.debug('sleeping for %s s' % (sleep_time))
|
||||
time.sleep(sleep_time)
|
||||
|
||||
def telnet_to_liquidsoap(self, media_item):
|
||||
"""
|
||||
telnets to liquidsoap and pushes the media_item to its queue. Push the
|
||||
show name of every media_item as well, just to keep Liquidsoap up-to-date
|
||||
about which show is playing.
|
||||
"""
|
||||
|
||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||
|
||||
#tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
|
||||
|
||||
annotation = media_item['annotation']
|
||||
msg = 'queue.push %s\n' % annotation.encode('utf-8')
|
||||
tn.write(msg)
|
||||
self.logger.debug(msg)
|
||||
|
||||
show_name = media_item['show_name']
|
||||
msg = 'vars.show_name %s\n' % show_name.encode('utf-8')
|
||||
tn.write(msg)
|
||||
self.logger.debug(msg)
|
||||
|
||||
tn.write("exit\n")
|
||||
self.logger.debug(tn.read_all())
|
||||
|
||||
def run(self):
|
||||
loops = 0
|
||||
heartbeat_period = math.floor(30/PUSH_INTERVAL)
|
||||
logger = logging.getLogger('push')
|
||||
|
||||
while True:
|
||||
if loops % heartbeat_period == 0:
|
||||
logger.info("heartbeat")
|
||||
self.logger.info("heartbeat")
|
||||
loops = 0
|
||||
try: self.push('scheduler')
|
||||
try: self.push()
|
||||
except Exception, e:
|
||||
logger.error('Pypo Push Exception: %s', e)
|
||||
self.logger.error('Pypo Push Exception: %s', e)
|
||||
time.sleep(PUSH_INTERVAL)
|
||||
loops += 1
|
||||
|
|
|
@ -169,26 +169,42 @@ class Recorder(Thread):
|
|||
def __init__(self, q):
|
||||
Thread.__init__(self)
|
||||
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.sr = None
|
||||
self.shows_to_record = {}
|
||||
self.server_timezone = ''
|
||||
self.queue = q
|
||||
self.logger.info("RecorderFetch: init complete")
|
||||
self.loops = 0
|
||||
|
||||
def handle_message(self):
|
||||
if not self.queue.empty():
|
||||
msg = self.queue.get()
|
||||
self.logger.info("Receivied msg from Pypo Fetch: %s", msg)
|
||||
if msg == 'cancel_recording':
|
||||
message = self.queue.get()
|
||||
msg = json.loads(message)
|
||||
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():
|
||||
self.sr.cancel_recording()
|
||||
else:
|
||||
self.shows_to_record = msg
|
||||
self.process_recorder_schedule(msg)
|
||||
self.loops = 0
|
||||
|
||||
if self.shows_to_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):
|
||||
if len(self.shows_to_record) != 0:
|
||||
|
@ -247,21 +263,43 @@ class Recorder(Thread):
|
|||
def run(self):
|
||||
try:
|
||||
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
|
||||
|
||||
loops = 0
|
||||
self.loops = 0
|
||||
heartbeat_period = math.floor(30/PUSH_INTERVAL)
|
||||
|
||||
while True:
|
||||
if loops % heartbeat_period == 0:
|
||||
if self.loops % heartbeat_period == 0:
|
||||
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()
|
||||
except Exception, e:
|
||||
self.logger.error('Pypo Recorder Exception: %s', e)
|
||||
time.sleep(PUSH_INTERVAL)
|
||||
loops += 1
|
||||
self.loops += 1
|
||||
except Exception,e :
|
||||
import traceback
|
||||
top = traceback.format_exc()
|
||||
|
|
Loading…
Reference in New Issue