Merge branch 'devel' of dev.sourcefabric.org:airtime
This commit is contained in:
commit
b87e661f96
|
@ -71,8 +71,8 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
|
||||||
$view->headScript()->appendFile($baseUrl.'/js/qtip/jquery.qtip-1.0.0.min.js','text/javascript');
|
$view->headScript()->appendFile($baseUrl.'/js/qtip/jquery.qtip-1.0.0.min.js','text/javascript');
|
||||||
|
|
||||||
//scripts for now playing bar
|
//scripts for now playing bar
|
||||||
$view->headScript()->appendFile($baseUrl.'/js/playlist/helperfunctions.js','text/javascript');
|
$view->headScript()->appendFile($baseUrl.'/js/airtime/dashboard/helperfunctions.js','text/javascript');
|
||||||
$view->headScript()->appendFile($baseUrl.'/js/playlist/playlist.js','text/javascript');
|
$view->headScript()->appendFile($baseUrl.'/js/airtime/dashboard/playlist.js','text/javascript');
|
||||||
|
|
||||||
$view->headScript()->appendFile($baseUrl.'/js/airtime/common/common.js','text/javascript');
|
$view->headScript()->appendFile($baseUrl.'/js/airtime/common/common.js','text/javascript');
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ class ApiController extends Zend_Controller_Action
|
||||||
$context->addActionContext('version', 'json')
|
$context->addActionContext('version', 'json')
|
||||||
->addActionContext('recorded-shows', 'json')
|
->addActionContext('recorded-shows', 'json')
|
||||||
->addActionContext('upload-recorded', 'json')
|
->addActionContext('upload-recorded', 'json')
|
||||||
|
->addActionContext('reload-metadata', 'json')
|
||||||
->initContext();
|
->initContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,8 +114,6 @@ class ApiController extends Zend_Controller_Action
|
||||||
$this->view->layout()->disableLayout();
|
$this->view->layout()->disableLayout();
|
||||||
$this->_helper->viewRenderer->setNoRender(true);
|
$this->_helper->viewRenderer->setNoRender(true);
|
||||||
|
|
||||||
$result = Schedule::GetPlayOrderRange(0, 1);
|
|
||||||
|
|
||||||
$date = new DateHelper;
|
$date = new DateHelper;
|
||||||
$timeNow = $date->getTimestamp();
|
$timeNow = $date->getTimestamp();
|
||||||
$result = array("env"=>APPLICATION_ENV,
|
$result = array("env"=>APPLICATION_ENV,
|
||||||
|
@ -318,5 +317,35 @@ class ApiController extends Zend_Controller_Action
|
||||||
|
|
||||||
$this->view->id = $file->getId();
|
$this->view->id = $file->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function reloadMetadataAction() {
|
||||||
|
|
||||||
|
global $CC_CONFIG;
|
||||||
|
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$md = $this->_getParam('md');
|
||||||
|
|
||||||
|
$file = StoredFile::Recall(null, $md['gunid']);
|
||||||
|
if (PEAR::isError($file) || is_null($file)) {
|
||||||
|
$this->view->response = "File not in Airtime's Database";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $file->replaceDbMetadata($md);
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
$this->view->response = "Metadata Change Failed";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->view->response = "Success!";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class DashboardController extends Zend_Controller_Action
|
||||||
|
{
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function indexAction()
|
||||||
|
{
|
||||||
|
// action body
|
||||||
|
}
|
||||||
|
|
||||||
|
public function helpAction()
|
||||||
|
{
|
||||||
|
// action body
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -168,6 +168,10 @@ class LibraryController extends Zend_Controller_Action
|
||||||
$formdata = $form->getValues();
|
$formdata = $form->getValues();
|
||||||
$file->replaceDbMetadata($formdata);
|
$file->replaceDbMetadata($formdata);
|
||||||
|
|
||||||
|
$data = $formdata;
|
||||||
|
$data['filepath'] = $file->getRealFilePath();
|
||||||
|
RabbitMq::SendFileMetaData($data);
|
||||||
|
|
||||||
$this->_helper->redirector('index');
|
$this->_helper->redirector('index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,24 @@
|
||||||
|
|
||||||
class Application_Form_EditAudioMD extends Zend_Form
|
class Application_Form_EditAudioMD extends Zend_Form
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
"title": "track_title",\
|
||||||
|
"artist": "artist_name",\
|
||||||
|
"album": "album_title",\
|
||||||
|
"genre": "genre",\
|
||||||
|
"mood": "mood",\
|
||||||
|
"tracknumber": "track_number",\
|
||||||
|
"bpm": "bpm",\
|
||||||
|
"organization": "label",\
|
||||||
|
"composer": "composer",\
|
||||||
|
"encodedby": "encoded_by",\
|
||||||
|
"conductor": "conductor",\
|
||||||
|
"date": "year",\
|
||||||
|
"website": "info_url",\
|
||||||
|
"isrc": "isrc_number",\
|
||||||
|
"copyright": "copyright",\
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
|
@ -37,6 +55,13 @@ class Application_Form_EditAudioMD extends Zend_Form
|
||||||
'filters' => array('StringTrim')
|
'filters' => array('StringTrim')
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Add mood field
|
||||||
|
$this->addElement('text', 'track_number', array(
|
||||||
|
'label' => 'Track:',
|
||||||
|
'class' => 'input_text',
|
||||||
|
'filters' => array('StringTrim')
|
||||||
|
));
|
||||||
|
|
||||||
// Add genre field
|
// Add genre field
|
||||||
$this->addElement('text', 'genre', array(
|
$this->addElement('text', 'genre', array(
|
||||||
'label' => 'Genre:',
|
'label' => 'Genre:',
|
||||||
|
@ -77,9 +102,30 @@ class Application_Form_EditAudioMD extends Zend_Form
|
||||||
'filters' => array('StringTrim')
|
'filters' => array('StringTrim')
|
||||||
));
|
));
|
||||||
|
|
||||||
// Add language field
|
// Add mood field
|
||||||
$this->addElement('text', 'language', array(
|
$this->addElement('text', 'bpm', array(
|
||||||
'label' => 'Language:',
|
'label' => 'BPM:',
|
||||||
|
'class' => 'input_text',
|
||||||
|
'filters' => array('StringTrim')
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add mood field
|
||||||
|
$this->addElement('text', 'copyright', array(
|
||||||
|
'label' => 'Copyright:',
|
||||||
|
'class' => 'input_text',
|
||||||
|
'filters' => array('StringTrim')
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add mood field
|
||||||
|
$this->addElement('text', 'isrc_number', array(
|
||||||
|
'label' => 'ISRC Number:',
|
||||||
|
'class' => 'input_text',
|
||||||
|
'filters' => array('StringTrim')
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add mood field
|
||||||
|
$this->addElement('text', 'info_url', array(
|
||||||
|
'label' => 'Website:',
|
||||||
'class' => 'input_text',
|
'class' => 'input_text',
|
||||||
'filters' => array('StringTrim')
|
'filters' => array('StringTrim')
|
||||||
));
|
));
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Application_Model_Dashboard
|
||||||
|
{
|
||||||
|
|
||||||
|
public static function GetPreviousItem($p_timeNow){
|
||||||
|
//get previous show and previous item in the schedule table.
|
||||||
|
//Compare the two and if the last show was recorded and started
|
||||||
|
//after the last item in the schedule table, then return the show's
|
||||||
|
//name. Else return the last item from the schedule.
|
||||||
|
|
||||||
|
$showInstance = ShowInstance::GetLastShowInstance($p_timeNow);
|
||||||
|
$row = Schedule::GetLastScheduleItem($p_timeNow);
|
||||||
|
|
||||||
|
if (is_null($showInstance)){
|
||||||
|
if (count($row) == 0){
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
//should never reach here. Doesnt make sense to have
|
||||||
|
//a schedule item not within a show_instance.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (count($row) == 0){
|
||||||
|
//last item is a show instance
|
||||||
|
return array("name"=>$showInstance->getName(),
|
||||||
|
"starts"=>$showInstance->getShowStart(),
|
||||||
|
"ends"=>$showInstance->getShowEnd());
|
||||||
|
} else {
|
||||||
|
//return the one that started later.
|
||||||
|
if ($row[0]["starts"] >= $showInstance->getShowStart()){
|
||||||
|
return array("name"=>$row[0]["artist_name"]." - ".$row[0]["track_title"],
|
||||||
|
"starts"=>$row[0]["starts"],
|
||||||
|
"ends"=>$row[0]["ends"]);
|
||||||
|
} else {
|
||||||
|
return array("name"=>$showInstance->getName(),
|
||||||
|
"starts"=>$showInstance->getShowStart(),
|
||||||
|
"ends"=>$showInstance->getShowEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetCurrentItem($p_timeNow){
|
||||||
|
//get previous show and previous item in the schedule table.
|
||||||
|
//Compare the two and if the last show was recorded and started
|
||||||
|
//after the last item in the schedule table, then return the show's
|
||||||
|
//name. Else return the last item from the schedule.
|
||||||
|
|
||||||
|
$showInstance = ShowInstance::GetCurrentShowInstance($p_timeNow);
|
||||||
|
$row = Schedule::GetCurrentScheduleItem($p_timeNow);
|
||||||
|
|
||||||
|
if (is_null($showInstance)){
|
||||||
|
if (count($row) == 0){
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
//should never reach here. Doesnt make sense to have
|
||||||
|
//a schedule item not within a show_instance.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (count($row) == 0){
|
||||||
|
//last item is a show instance
|
||||||
|
return array("name"=>$showInstance->getName(),
|
||||||
|
"starts"=>$showInstance->getShowStart(),
|
||||||
|
"ends"=>$showInstance->getShowEnd(),
|
||||||
|
"media_item_played"=>false, //TODO
|
||||||
|
"record"=>$showInstance->isRecorded()); //TODO
|
||||||
|
} else {
|
||||||
|
return array("name"=>$row[0]["artist_name"]." - ".$row[0]["track_title"],
|
||||||
|
"starts"=>$row[0]["starts"],
|
||||||
|
"ends"=>$row[0]["ends"],
|
||||||
|
"media_item_played"=>$row[0]["media_item_played"],
|
||||||
|
"record"=>0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetNextItem($p_timeNow){
|
||||||
|
//get previous show and previous item in the schedule table.
|
||||||
|
//Compare the two and if the last show was recorded and started
|
||||||
|
//after the last item in the schedule table, then return the show's
|
||||||
|
//name. Else return the last item from the schedule.
|
||||||
|
|
||||||
|
$showInstance = ShowInstance::GetNextShowInstance($p_timeNow);
|
||||||
|
$row = Schedule::GetNextScheduleItem($p_timeNow);
|
||||||
|
|
||||||
|
if (is_null($showInstance)){
|
||||||
|
if (count($row) == 0){
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
//should never reach here. Doesnt make sense to have
|
||||||
|
//a schedule item not within a show_instance.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (count($row) == 0){
|
||||||
|
//last item is a show instance
|
||||||
|
return array("name"=>$showInstance->getName(),
|
||||||
|
"starts"=>$showInstance->getShowStart(),
|
||||||
|
"ends"=>$showInstance->getShowEnd());
|
||||||
|
} else {
|
||||||
|
//return the one that starts sooner.
|
||||||
|
|
||||||
|
if ($row[0]["starts"] <= $showInstance->getShowStart()){
|
||||||
|
return array("name"=>$row[0]["artist_name"]." - ".$row[0]["track_title"],
|
||||||
|
"starts"=>$row[0]["starts"],
|
||||||
|
"ends"=>$row[0]["ends"]);
|
||||||
|
} else {
|
||||||
|
return array("name"=>$showInstance->getName(),
|
||||||
|
"starts"=>$showInstance->getShowStart(),
|
||||||
|
"ends"=>$showInstance->getShowEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -40,5 +40,27 @@ class RabbitMq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function SendFileMetaData($md)
|
||||||
|
{
|
||||||
|
global $CC_CONFIG;
|
||||||
|
|
||||||
|
$conn = new AMQPConnection($CC_CONFIG["rabbitmq"]["host"],
|
||||||
|
$CC_CONFIG["rabbitmq"]["port"],
|
||||||
|
$CC_CONFIG["rabbitmq"]["user"],
|
||||||
|
$CC_CONFIG["rabbitmq"]["password"]);
|
||||||
|
$channel = $conn->channel();
|
||||||
|
$channel->access_request($CC_CONFIG["rabbitmq"]["vhost"], false, false, true, true);
|
||||||
|
|
||||||
|
$EXCHANGE = 'airtime-media-monitor';
|
||||||
|
$channel->exchange_declare($EXCHANGE, 'direct', false, true);
|
||||||
|
|
||||||
|
$data = json_encode($md);
|
||||||
|
$msg = new AMQPMessage($data, array('content_type' => 'text/plain'));
|
||||||
|
|
||||||
|
$channel->basic_publish($msg, $EXCHANGE);
|
||||||
|
$channel->close();
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -365,9 +365,12 @@ class Schedule {
|
||||||
$timeNow = $date->getTimestamp();
|
$timeNow = $date->getTimestamp();
|
||||||
return array("env"=>APPLICATION_ENV,
|
return array("env"=>APPLICATION_ENV,
|
||||||
"schedulerTime"=>gmdate("Y-m-d H:i:s"),
|
"schedulerTime"=>gmdate("Y-m-d H:i:s"),
|
||||||
"previous"=>Schedule::GetScheduledItemData($timeNow, -1, $prev, "24 hours"),
|
//"previous"=>Schedule::GetScheduledItemData($timeNow, -1, $prev, "24 hours"),
|
||||||
"current"=>Schedule::GetScheduledItemData($timeNow, 0),
|
//"current"=>Schedule::GetScheduledItemData($timeNow, 0),
|
||||||
"next"=>Schedule::GetScheduledItemData($timeNow, 1, $next, "48 hours"),
|
//"next"=>Schedule::GetScheduledItemData($timeNow, 1, $next, "48 hours"),
|
||||||
|
"previous"=>Application_Model_Dashboard::GetPreviousItem($timeNow),
|
||||||
|
"current"=>Application_Model_Dashboard::GetCurrentItem($timeNow),
|
||||||
|
"next"=>Application_Model_Dashboard::GetNextItem($timeNow),
|
||||||
"currentShow"=>Show_DAL::GetCurrentShow($timeNow),
|
"currentShow"=>Show_DAL::GetCurrentShow($timeNow),
|
||||||
"nextShow"=>Show_DAL::GetNextShows($timeNow, 1),
|
"nextShow"=>Show_DAL::GetNextShows($timeNow, 1),
|
||||||
"timezone"=> date("T"),
|
"timezone"=> date("T"),
|
||||||
|
@ -375,6 +378,52 @@ class Schedule {
|
||||||
"apiKey"=>$CC_CONFIG['apiKey'][0]);
|
"apiKey"=>$CC_CONFIG['apiKey'][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function GetLastScheduleItem($p_timeNow){
|
||||||
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
$sql = "SELECT *"
|
||||||
|
." FROM $CC_CONFIG[scheduleTable] st"
|
||||||
|
." LEFT JOIN $CC_CONFIG[filesTable] ft"
|
||||||
|
." ON st.file_id = ft.id"
|
||||||
|
." WHERE st.ends < TIMESTAMP '$p_timeNow'"
|
||||||
|
." ORDER BY st.ends DESC"
|
||||||
|
." LIMIT 1";
|
||||||
|
|
||||||
|
$row = $CC_DBC->GetAll($sql);
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function GetCurrentScheduleItem($p_timeNow){
|
||||||
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
$sql = "SELECT *"
|
||||||
|
." FROM $CC_CONFIG[scheduleTable] st"
|
||||||
|
." LEFT JOIN $CC_CONFIG[filesTable] ft"
|
||||||
|
." ON st.file_id = ft.id"
|
||||||
|
." WHERE st.starts <= TIMESTAMP '$p_timeNow'"
|
||||||
|
." AND st.ends > TIMESTAMP '$p_timeNow'"
|
||||||
|
." LIMIT 1";
|
||||||
|
|
||||||
|
$row = $CC_DBC->GetAll($sql);
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetNextScheduleItem($p_timeNow){
|
||||||
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
$sql = "SELECT *"
|
||||||
|
." FROM $CC_CONFIG[scheduleTable] st"
|
||||||
|
." LEFT JOIN $CC_CONFIG[filesTable] ft"
|
||||||
|
." ON st.file_id = ft.id"
|
||||||
|
." WHERE st.starts > TIMESTAMP '$p_timeNow'"
|
||||||
|
." ORDER BY st.starts"
|
||||||
|
." LIMIT 1";
|
||||||
|
|
||||||
|
$row = $CC_DBC->GetAll($sql);
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an SQL Query for accessing scheduled item information from
|
* Builds an SQL Query for accessing scheduled item information from
|
||||||
* the database.
|
* the database.
|
||||||
|
|
|
@ -1718,6 +1718,57 @@ class ShowInstance {
|
||||||
|
|
||||||
return ($diff < 0) ? 0 : $diff;
|
return ($diff < 0) ? 0 : $diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function GetLastShowInstance($p_timeNow){
|
||||||
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
$sql = "SELECT si.id"
|
||||||
|
." FROM $CC_CONFIG[showInstances] si"
|
||||||
|
." WHERE si.ends < TIMESTAMP '$p_timeNow'"
|
||||||
|
." ORDER BY si.ends DESC"
|
||||||
|
." LIMIT 1";
|
||||||
|
|
||||||
|
$id = $CC_DBC->GetOne($sql);
|
||||||
|
if (is_null($id)){
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new ShowInstance($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetCurrentShowInstance($p_timeNow){
|
||||||
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
$sql = "SELECT si.id"
|
||||||
|
." FROM $CC_CONFIG[showInstances] si"
|
||||||
|
." WHERE si.starts <= TIMESTAMP '$p_timeNow'"
|
||||||
|
." AND si.ends > TIMESTAMP '$p_timeNow'"
|
||||||
|
." LIMIT 1";
|
||||||
|
|
||||||
|
$id = $CC_DBC->GetOne($sql);
|
||||||
|
if (is_null($id)){
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new ShowInstance($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetNextShowInstance($p_timeNow){
|
||||||
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
$sql = "SELECT si.id"
|
||||||
|
." FROM $CC_CONFIG[showInstances] si"
|
||||||
|
." WHERE si.starts > TIMESTAMP '$p_timeNow'"
|
||||||
|
." ORDER BY si.starts"
|
||||||
|
." LIMIT 1";
|
||||||
|
|
||||||
|
$id = $CC_DBC->GetOne($sql);
|
||||||
|
if (is_null($id)){
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new ShowInstance($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show Data Access Layer */
|
/* Show Data Access Layer */
|
||||||
|
|
|
@ -543,16 +543,25 @@ class StoredFile {
|
||||||
public function replaceDbMetadata($p_values)
|
public function replaceDbMetadata($p_values)
|
||||||
{
|
{
|
||||||
global $CC_CONFIG, $CC_DBC;
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
$data = array();
|
||||||
foreach ($p_values as $category => $value) {
|
foreach ($p_values as $category => $value) {
|
||||||
$escapedValue = pg_escape_string($value);
|
$escapedValue = pg_escape_string($value);
|
||||||
$columnName = $category;
|
$columnName = $category;
|
||||||
if (!is_null($columnName)) {
|
if (!is_null($columnName)) {
|
||||||
$sql = "UPDATE ".$CC_CONFIG["filesTable"]
|
$data[] = "$columnName='$escapedValue'";
|
||||||
." SET $columnName='$escapedValue'"
|
|
||||||
." WHERE gunid = '".$this->gunid."'";
|
|
||||||
$CC_DBC->query($sql);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data = join(",", $data);
|
||||||
|
$sql = "UPDATE ".$CC_CONFIG["filesTable"]
|
||||||
|
." SET $data"
|
||||||
|
." WHERE gunid = '".$this->gunid."'";
|
||||||
|
$res = $CC_DBC->query($sql);
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
$CC_DBC->query("ROLLBACK");
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clearMetadata()
|
public function clearMetadata()
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
var estimatedSchedulePosixTime = null;
|
var estimatedSchedulePosixTime = null;
|
||||||
var localRemoteTimeOffset = null;
|
var localRemoteTimeOffset = null;
|
||||||
|
|
||||||
var previousSongs = new Array();
|
var previousSong = null;
|
||||||
var currentSong = new Array();
|
var currentSong = null;
|
||||||
var nextSongs = new Array();
|
var nextSong = null;
|
||||||
|
|
||||||
var currentShow = new Array();
|
var currentShow = new Array();
|
||||||
var nextShow = new Array();
|
var nextShow = new Array();
|
||||||
|
@ -25,21 +25,8 @@ var nextShowPrepare = true;
|
||||||
|
|
||||||
var apiKey = "";
|
var apiKey = "";
|
||||||
|
|
||||||
function getTrackInfo(song){
|
|
||||||
var str = "";
|
|
||||||
|
|
||||||
if (song.track_title != null)
|
|
||||||
str += song.track_title;
|
|
||||||
if (song.artist_name != null)
|
|
||||||
str += " - " + song.artist_name;
|
|
||||||
|
|
||||||
str += ","
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function secondsTimer(){
|
function secondsTimer(){
|
||||||
if (localRemoteTimeOffset != null){
|
if (localRemoteTimeOffset !== null){
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
estimatedSchedulePosixTime = date.getTime() - localRemoteTimeOffset;
|
estimatedSchedulePosixTime = date.getTime() - localRemoteTimeOffset;
|
||||||
updateProgressBarValue();
|
updateProgressBarValue();
|
||||||
|
@ -50,7 +37,8 @@ function secondsTimer(){
|
||||||
|
|
||||||
function newSongStart(){
|
function newSongStart(){
|
||||||
nextSongPrepare = true;
|
nextSongPrepare = true;
|
||||||
currentSong[0] = nextSongs.shift();
|
currentSong = nextSong;
|
||||||
|
nextSong = null;
|
||||||
|
|
||||||
if (typeof notifySongStart == "function")
|
if (typeof notifySongStart == "function")
|
||||||
notifySongStart();
|
notifySongStart();
|
||||||
|
@ -80,13 +68,13 @@ function updateProgressBarValue(){
|
||||||
$('#progress-show').attr("style", "width:"+showPercentDone+"%");
|
$('#progress-show').attr("style", "width:"+showPercentDone+"%");
|
||||||
|
|
||||||
var songPercentDone = 0;
|
var songPercentDone = 0;
|
||||||
if (currentSong.length > 0){
|
if (currentSong !== null){
|
||||||
songPercentDone = (estimatedSchedulePosixTime - currentSong[0].songStartPosixTime)/currentSong[0].songLengthMs*100;
|
songPercentDone = (estimatedSchedulePosixTime - currentSong.songStartPosixTime)/currentSong.songLengthMs*100;
|
||||||
if (songPercentDone < 0 || songPercentDone > 100){
|
if (songPercentDone < 0 || songPercentDone > 100){
|
||||||
songPercentDone = 0;
|
songPercentDone = 0;
|
||||||
currentSong = new Array();
|
currentSong = null;
|
||||||
} else {
|
} else {
|
||||||
if (currentSong[0].media_item_played == "t" && currentShow.length > 0)
|
if (currentSong.media_item_played == "t" && currentShow.length > 0)
|
||||||
$('#on-air-info').attr("class", "on-air-info on");
|
$('#on-air-info').attr("class", "on-air-info on");
|
||||||
else
|
else
|
||||||
$('#on-air-info').attr("class", "on-air-info off");
|
$('#on-air-info').attr("class", "on-air-info off");
|
||||||
|
@ -99,8 +87,8 @@ function updateProgressBarValue(){
|
||||||
$('#progress-bar').attr("style", "width:"+songPercentDone+"%");
|
$('#progress-bar').attr("style", "width:"+songPercentDone+"%");
|
||||||
|
|
||||||
//calculate how much time left to next song if there is any
|
//calculate how much time left to next song if there is any
|
||||||
if (nextSongs.length > 0 && nextSongPrepare){
|
if (nextSong !== null && nextSongPrepare){
|
||||||
var diff = nextSongs[0].songStartPosixTime - estimatedSchedulePosixTime;
|
var diff = nextSong.songStartPosixTime - estimatedSchedulePosixTime;
|
||||||
if (diff < serverUpdateInterval){
|
if (diff < serverUpdateInterval){
|
||||||
|
|
||||||
//sometimes the diff is negative (-100ms for example). Still looking
|
//sometimes the diff is negative (-100ms for example). Still looking
|
||||||
|
@ -133,20 +121,26 @@ function updatePlaybar(){
|
||||||
$('#current').html("Current: <span style='color:red; font-weight:bold'>Nothing Scheduled</span>");
|
$('#current').html("Current: <span style='color:red; font-weight:bold'>Nothing Scheduled</span>");
|
||||||
$('#next').empty();
|
$('#next').empty();
|
||||||
$('#next-length').empty();
|
$('#next-length').empty();
|
||||||
if (previousSongs.length > 0){
|
if (previousSong !== null){
|
||||||
$('#previous').text(getTrackInfo(previousSongs[previousSongs.length-1]));
|
$('#previous').text(previousSong.name+",");
|
||||||
$('#prev-length').text(convertToHHMMSSmm(previousSongs[previousSongs.length-1].songLengthMs));
|
$('#prev-length').text(convertToHHMMSSmm(previousSong.songLengthMs));
|
||||||
}
|
}
|
||||||
if (currentSong.length > 0){
|
if (currentSong !== null){
|
||||||
$('#current').text(getTrackInfo(currentSong[0]));
|
if (currentSong.record == "1")
|
||||||
} else if (currentShow.length > 0){
|
$('#current').html("<span style='color:red; font-weight:bold'>Recording: </span>"+currentSong.name+",");
|
||||||
|
else
|
||||||
|
$('#current').text(currentSong.name+",");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
else if (currentShow.length > 0){
|
||||||
if (currentShow[0].record == "1"){
|
if (currentShow[0].record == "1"){
|
||||||
$('#current').html("Current: <span style='color:red; font-weight:bold'>Recording</span>");
|
$('#current').html("Current: <span style='color:red; font-weight:bold'>Recording</span>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextSongs.length > 0){
|
* */
|
||||||
$('#next').text(getTrackInfo(nextSongs[0]));
|
if (nextSong !== null){
|
||||||
$('#next-length').text(convertToHHMMSSmm(nextSongs[0].songLengthMs));
|
$('#next').text(nextSong.name+",");
|
||||||
|
$('#next-length').text(convertToHHMMSSmm(nextSong.songLengthMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#start').empty();
|
$('#start').empty();
|
||||||
|
@ -154,18 +148,18 @@ function updatePlaybar(){
|
||||||
$('#time-elapsed').empty();
|
$('#time-elapsed').empty();
|
||||||
$('#time-remaining').empty();
|
$('#time-remaining').empty();
|
||||||
$('#song-length').empty();
|
$('#song-length').empty();
|
||||||
for (var i=0; i<currentSong.length; i++){
|
if (currentSong !== null){
|
||||||
$('#start').text(currentSong[i].starts.substring(currentSong[i].starts.indexOf(" ")+1));
|
$('#start').text(currentSong.starts.substring(currentSong.starts.indexOf(" ")+1));
|
||||||
$('#end').text(currentSong[i].ends.substring(currentSong[i].starts.indexOf(" ")+1));
|
$('#end').text(currentSong.ends.substring(currentSong.starts.indexOf(" ")+1));
|
||||||
|
|
||||||
/* Get rid of the millisecond accuracy so that the second counters for both
|
/* Get rid of the millisecond accuracy so that the second counters for both
|
||||||
* show and song change at the same time. */
|
* show and song change at the same time. */
|
||||||
var songStartRoughly = parseInt(Math.round(currentSong[i].songStartPosixTime/1000))*1000;
|
var songStartRoughly = parseInt(Math.round(currentSong.songStartPosixTime/1000))*1000;
|
||||||
var songEndRoughly = parseInt(Math.round(currentSong[i].songEndPosixTime/1000))*1000;
|
var songEndRoughly = parseInt(Math.round(currentSong.songEndPosixTime/1000))*1000;
|
||||||
|
|
||||||
$('#time-elapsed').text(convertToHHMMSS(estimatedSchedulePosixTime - songStartRoughly));
|
$('#time-elapsed').text(convertToHHMMSS(estimatedSchedulePosixTime - songStartRoughly));
|
||||||
$('#time-remaining').text(convertToHHMMSS(songEndRoughly - estimatedSchedulePosixTime));
|
$('#time-remaining').text(convertToHHMMSS(songEndRoughly - estimatedSchedulePosixTime));
|
||||||
$('#song-length').text(convertToHHMMSSmm(currentSong[i].songLengthMs));
|
$('#song-length').text(convertToHHMMSSmm(currentSong.songLengthMs));
|
||||||
}
|
}
|
||||||
/* Column 1 update */
|
/* Column 1 update */
|
||||||
$('#playlist').text("Current Show:");
|
$('#playlist').text("Current Show:");
|
||||||
|
@ -187,11 +181,9 @@ function updatePlaybar(){
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcAdditionalData(currentItem){
|
function calcAdditionalData(currentItem){
|
||||||
for (var i=0; i<currentItem.length; i++){
|
currentItem.songStartPosixTime = convertDateToPosixTime(currentItem.starts);
|
||||||
currentItem[i].songStartPosixTime = convertDateToPosixTime(currentItem[i].starts);
|
currentItem.songEndPosixTime = convertDateToPosixTime(currentItem.ends);
|
||||||
currentItem[i].songEndPosixTime = convertDateToPosixTime(currentItem[i].ends);
|
currentItem.songLengthMs = currentItem.songEndPosixTime - currentItem.songStartPosixTime;
|
||||||
currentItem[i].songLengthMs = currentItem[i].songEndPosixTime - currentItem[i].songStartPosixTime;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcAdditionalShowData(show){
|
function calcAdditionalShowData(show){
|
||||||
|
@ -208,17 +200,18 @@ function parseItems(obj){
|
||||||
|
|
||||||
$('#time-zone').text(obj.timezone);
|
$('#time-zone').text(obj.timezone);
|
||||||
|
|
||||||
previousSongs = obj.previous;
|
previousSong = obj.previous;
|
||||||
nextSongs = obj.next;
|
currentSong = obj.current;
|
||||||
|
nextSong = obj.next;
|
||||||
|
|
||||||
calcAdditionalData(previousSongs);
|
if (previousSong !== null)
|
||||||
calcAdditionalData(nextSongs);
|
calcAdditionalData(previousSong);
|
||||||
|
if (currentSong !== null)
|
||||||
|
calcAdditionalData(currentSong);
|
||||||
|
if (nextSong !== null)
|
||||||
|
calcAdditionalData(nextSong);
|
||||||
|
|
||||||
currentShow = obj.currentShow;
|
currentShow = obj.currentShow;
|
||||||
if(currentShow.length > 0){
|
|
||||||
currentSong = obj.current;
|
|
||||||
calcAdditionalData(currentSong);
|
|
||||||
}
|
|
||||||
nextShow = obj.nextShow;
|
nextShow = obj.nextShow;
|
||||||
|
|
||||||
calcAdditionalShowData(obj.currentShow);
|
calcAdditionalShowData(obj.currentShow);
|
||||||
|
@ -238,17 +231,10 @@ function getScheduleFromServer(){
|
||||||
setTimeout(getScheduleFromServer, serverUpdateInterval);
|
setTimeout(getScheduleFromServer, serverUpdateInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupQtip(){
|
||||||
function init() {
|
|
||||||
//begin producer "thread"
|
|
||||||
getScheduleFromServer();
|
|
||||||
|
|
||||||
//begin consumer "thread"
|
|
||||||
secondsTimer();
|
|
||||||
|
|
||||||
var qtipElem = $('#about-link');
|
var qtipElem = $('#about-link');
|
||||||
|
|
||||||
if (qtipElem.length > 0)
|
if (qtipElem.length > 0){
|
||||||
qtipElem.qtip({
|
qtipElem.qtip({
|
||||||
content: $('#about-txt').html(),
|
content: $('#about-txt').html(),
|
||||||
show: 'mouseover',
|
show: 'mouseover',
|
||||||
|
@ -268,6 +254,17 @@ function init() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
//begin producer "thread"
|
||||||
|
getScheduleFromServer();
|
||||||
|
|
||||||
|
//begin consumer "thread"
|
||||||
|
secondsTimer();
|
||||||
|
|
||||||
|
setupQtip();
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
if ($('#master-panel').length > 0)
|
if ($('#master-panel').length > 0)
|
|
@ -209,11 +209,41 @@ function getId() {
|
||||||
function buildContentDialog(json){
|
function buildContentDialog(json){
|
||||||
var dialog = $(json.dialog);
|
var dialog = $(json.dialog);
|
||||||
|
|
||||||
|
var viewportwidth;
|
||||||
|
var viewportheight;
|
||||||
|
|
||||||
|
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use
|
||||||
|
// window.innerWidth and window.innerHeight
|
||||||
|
|
||||||
|
if (typeof window.innerWidth != 'undefined') {
|
||||||
|
viewportwidth = window.innerWidth, viewportheight = window.innerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IE6 in standards compliant mode (i.e. with a valid doctype as the first
|
||||||
|
// line in the document)
|
||||||
|
|
||||||
|
else if (typeof document.documentElement != 'undefined'
|
||||||
|
&& typeof document.documentElement.clientWidth != 'undefined'
|
||||||
|
&& document.documentElement.clientWidth != 0) {
|
||||||
|
viewportwidth = document.documentElement.clientWidth;
|
||||||
|
viewportheight = document.documentElement.clientHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// older versions of IE
|
||||||
|
|
||||||
|
else {
|
||||||
|
viewportwidth = document.getElementsByTagName('body')[0].clientWidth;
|
||||||
|
viewportheight = document.getElementsByTagName('body')[0].clientHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
var height = viewportheight * 2/3;
|
||||||
|
var width = viewportwidth * 4/5;
|
||||||
|
|
||||||
dialog.dialog({
|
dialog.dialog({
|
||||||
autoOpen: false,
|
autoOpen: false,
|
||||||
title: 'Show Contents',
|
title: 'Show Contents',
|
||||||
width: 1100,
|
width: width,
|
||||||
height: 500,
|
height: height,
|
||||||
modal: true,
|
modal: true,
|
||||||
close: closeDialog,
|
close: closeDialog,
|
||||||
buttons: {"Ok": function() {
|
buttons: {"Ok": function() {
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
function createDataGrid(datagridData){
|
|
||||||
|
|
||||||
var columnHeaders = [
|
|
||||||
{ "sTitle": "name" },
|
|
||||||
{ "sTitle": "date" },
|
|
||||||
{ "sTitle": "start time" },
|
|
||||||
{ "sTitle": "end time" }
|
|
||||||
];
|
|
||||||
|
|
||||||
$('#demo').html( '<table cellpadding="0" cellspacing="0" border="0" width="100%" id="nowplayingtable"></table>' );
|
|
||||||
$('#nowplayingtable').dataTable( {
|
|
||||||
"bSort" : false,
|
|
||||||
"bJQueryUI": true,
|
|
||||||
"bFilter": false,
|
|
||||||
"bInfo": false,
|
|
||||||
"bLengthChange": false,
|
|
||||||
"aaData": datagridData.rows,
|
|
||||||
"aoColumns": columnHeaders
|
|
||||||
} );
|
|
||||||
|
|
||||||
|
|
||||||
var options1 = [
|
|
||||||
|
|
||||||
{title:"Menu Item 1 - Go TO www.google.com", action:{type:"gourl",url:"http://www.google.com/"}},
|
|
||||||
{title:"Menu Item 2 - do <b style='color:red;'>nothing</b>"},
|
|
||||||
{title:"Menu Item 3 - submenu", type:"sub", src:[{title:"Submenu 1"},{title:"Submenu 2"},{title:"Submenu 3"}, {title:"Submenu 4 - submenu", type:"sub", src:[{title:"SubSubmenu 1"},{title:"SubSubmenu 2"}]}]},
|
|
||||||
{title:"Menu Item 4 - Js function", action:{type:"fn",callback:"(function(){ alert('THIS IS THE TEST'); })"}}
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
var userData = {};
|
|
||||||
|
|
||||||
var effects = {
|
|
||||||
show:"default", //type of show effect
|
|
||||||
orientation: "auto", //type of menu orientation - to top, to bottom, auto (to bottom, if doesn't fit on screen - to top)
|
|
||||||
xposition:"mouse", // position of menu (left side or right side of trigger element)
|
|
||||||
yposition:"mouse"
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#demo').jjmenu('both', options1, userData, effects );
|
|
||||||
}
|
|
||||||
|
|
||||||
function initShowListView(){
|
|
||||||
|
|
||||||
|
|
||||||
$.ajax({ url: "/Schedule/get-show-data/format/json", dataType:"text", success:function(data){
|
|
||||||
$('#json-string').text(data);
|
|
||||||
}});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$.ajax({ url: "/Schedule/get-show-data/format/json", dataType:"json", success:function(data){
|
|
||||||
var temp = data.data;
|
|
||||||
var rows = new Array();
|
|
||||||
for (var i=0; i<temp.length; i++){
|
|
||||||
rows[i] = [temp[i].name.toString(), temp[i].first_show.toString(), temp[i].start_time.toString(), temp[i].end_time.toString()];
|
|
||||||
var datagridData = {rows:rows};
|
|
||||||
createDataGrid(datagridData);
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//setTimeout(initShowListView, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
initShowListView();
|
|
||||||
});
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
set_include_path(__DIR__.'/../airtime_mvc/library' . PATH_SEPARATOR . get_include_path());
|
||||||
|
|
||||||
|
require_once(dirname(__FILE__).'/include/AirtimeIni.php');
|
||||||
|
require_once(dirname(__FILE__).'/include/AirtimeInstall.php');
|
||||||
|
|
||||||
|
require_once(AirtimeInstall::GetAirtimeSrcDir().'/application/configs/conf.php');
|
||||||
|
|
||||||
|
//echo PHP_EOL."*** Database Installation ***".PHP_EOL;
|
||||||
|
|
||||||
|
AirtimeInstall::CreateDatabaseUser();
|
||||||
|
|
||||||
|
AirtimeInstall::CreateDatabase();
|
||||||
|
|
||||||
|
AirtimeInstall::DbConnect(true);
|
||||||
|
|
||||||
|
AirtimeInstall::InstallPostgresScriptingLanguage();
|
||||||
|
|
||||||
|
AirtimeInstall::CreateDatabaseTables();
|
||||||
|
|
||||||
|
AirtimeInstall::SetAirtimeVersion(AIRTIME_VERSION);
|
||||||
|
|
|
@ -22,7 +22,8 @@ try {
|
||||||
array(
|
array(
|
||||||
'help|h' => 'Displays usage information.',
|
'help|h' => 'Displays usage information.',
|
||||||
'overwrite|o' => 'Overwrite any existing config files.',
|
'overwrite|o' => 'Overwrite any existing config files.',
|
||||||
'preserve|p' => 'Keep any existing config files.'
|
'preserve|p' => 'Keep any existing config files.',
|
||||||
|
'no-db|n' => 'Turn off database install.'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$opts->parse();
|
$opts->parse();
|
||||||
|
@ -34,6 +35,10 @@ if (isset($opts->h)) {
|
||||||
echo $opts->getUsageMessage();
|
echo $opts->getUsageMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
$db_install = true;
|
||||||
|
if (isset($opts->n)){
|
||||||
|
$db_install = false;
|
||||||
|
}
|
||||||
|
|
||||||
$overwrite = false;
|
$overwrite = false;
|
||||||
if (isset($opts->o)) {
|
if (isset($opts->o)) {
|
||||||
|
@ -72,9 +77,11 @@ require_once(AirtimeInstall::GetAirtimeSrcDir().'/application/configs/conf.php')
|
||||||
|
|
||||||
echo "* Airtime Version: ".AIRTIME_VERSION.PHP_EOL;
|
echo "* Airtime Version: ".AIRTIME_VERSION.PHP_EOL;
|
||||||
|
|
||||||
|
if ($db_install) {
|
||||||
|
|
||||||
//echo PHP_EOL."*** Database Installation ***".PHP_EOL;
|
//echo PHP_EOL."*** Database Installation ***".PHP_EOL;
|
||||||
|
|
||||||
AirtimeInstall::CreateDatabaseUser();
|
/* AirtimeInstall::CreateDatabaseUser();
|
||||||
|
|
||||||
AirtimeInstall::CreateDatabase();
|
AirtimeInstall::CreateDatabase();
|
||||||
|
|
||||||
|
@ -82,7 +89,9 @@ AirtimeInstall::DbConnect(true);
|
||||||
|
|
||||||
AirtimeInstall::InstallPostgresScriptingLanguage();
|
AirtimeInstall::InstallPostgresScriptingLanguage();
|
||||||
|
|
||||||
AirtimeInstall::CreateDatabaseTables();
|
AirtimeInstall::CreateDatabaseTables();*/
|
||||||
|
require( 'airtime-db-install.php' );
|
||||||
|
}
|
||||||
|
|
||||||
AirtimeInstall::InstallStorageDirectory();
|
AirtimeInstall::InstallStorageDirectory();
|
||||||
|
|
||||||
|
@ -98,6 +107,9 @@ system("python ".__DIR__."/../python_apps/pypo/install/pypo-install.py");
|
||||||
echo PHP_EOL."*** Recorder Installation ***".PHP_EOL;
|
echo PHP_EOL."*** Recorder Installation ***".PHP_EOL;
|
||||||
system("python ".__DIR__."/../python_apps/show-recorder/install/recorder-install.py");
|
system("python ".__DIR__."/../python_apps/show-recorder/install/recorder-install.py");
|
||||||
|
|
||||||
|
echo PHP_EOL."*** Media Monitor Installation ***".PHP_EOL;
|
||||||
|
system("python ".__DIR__."/../python_apps/pytag-fs/install/media-monitor-install.py");
|
||||||
|
|
||||||
AirtimeInstall::SetAirtimeVersion(AIRTIME_VERSION);
|
AirtimeInstall::SetAirtimeVersion(AIRTIME_VERSION);
|
||||||
|
|
||||||
echo "******************************* Install Complete *******************************".PHP_EOL;
|
echo "******************************* Install Complete *******************************".PHP_EOL;
|
||||||
|
|
|
@ -29,7 +29,10 @@ AirtimeInstall::UninstallPhpCode();
|
||||||
// still be a connection to the database and you wont be able to delete it.
|
// still be a connection to the database and you wont be able to delete it.
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
echo " * Dropping the database '".$CC_CONFIG['dsn']['database']."'...".PHP_EOL;
|
echo " * Dropping the database '".$CC_CONFIG['dsn']['database']."'...".PHP_EOL;
|
||||||
$command = "su postgres -c \"dropdb {$CC_CONFIG['dsn']['database']}\"";
|
|
||||||
|
// check if DB exists
|
||||||
|
$command = "echo \"DROP DATABASE IF EXISTS ".$CC_CONFIG['dsn']['database']."\" | sudo -u postgres psql";
|
||||||
|
|
||||||
@exec($command, $output, $dbDeleteFailed);
|
@exec($command, $output, $dbDeleteFailed);
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
|
@ -66,7 +69,7 @@ if ($dbDeleteFailed) {
|
||||||
// Delete the user
|
// Delete the user
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
echo " * Deleting database user '{$CC_CONFIG['dsn']['username']}'...".PHP_EOL;
|
echo " * Deleting database user '{$CC_CONFIG['dsn']['username']}'...".PHP_EOL;
|
||||||
$command = "echo \"DROP USER {$CC_CONFIG['dsn']['username']}\" | su postgres -c psql";
|
$command = "echo \"DROP USER IF EXISTS {$CC_CONFIG['dsn']['username']}\" | su postgres -c psql";
|
||||||
@exec($command, $output, $results);
|
@exec($command, $output, $results);
|
||||||
if ($results == 0) {
|
if ($results == 0) {
|
||||||
echo " * User '{$CC_CONFIG['dsn']['username']}' deleted.".PHP_EOL;
|
echo " * User '{$CC_CONFIG['dsn']['username']}' deleted.".PHP_EOL;
|
||||||
|
@ -85,6 +88,10 @@ echo PHP_EOL."*** Uninstalling Show Recorder ***".PHP_EOL;
|
||||||
$command = "python ".__DIR__."/../python_apps/show-recorder/install/recorder-uninstall.py";
|
$command = "python ".__DIR__."/../python_apps/show-recorder/install/recorder-uninstall.py";
|
||||||
system($command);
|
system($command);
|
||||||
|
|
||||||
|
echo PHP_EOL."*** Uninstalling Media Monitor ***".PHP_EOL;
|
||||||
|
$command = "python ".__DIR__."/../python_apps/pytag-fs/install/media-monitor-uninstall.py";
|
||||||
|
system($command);
|
||||||
|
|
||||||
#Disabled as this should be a manual process
|
#Disabled as this should be a manual process
|
||||||
#AirtimeIni::RemoveIniFiles();
|
#AirtimeIni::RemoveIniFiles();
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,15 @@ class AirtimeIni
|
||||||
const CONF_FILE_PYPO = "/etc/airtime/pypo.cfg";
|
const CONF_FILE_PYPO = "/etc/airtime/pypo.cfg";
|
||||||
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
||||||
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
||||||
|
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/MediaMonitor.cfg";
|
||||||
|
|
||||||
public static function IniFilesExist()
|
public static function IniFilesExist()
|
||||||
{
|
{
|
||||||
$configFiles = array(AirtimeIni::CONF_FILE_AIRTIME,
|
$configFiles = array(AirtimeIni::CONF_FILE_AIRTIME,
|
||||||
AirtimeIni::CONF_FILE_PYPO,
|
AirtimeIni::CONF_FILE_PYPO,
|
||||||
AirtimeIni::CONF_FILE_RECORDER,
|
AirtimeIni::CONF_FILE_RECORDER,
|
||||||
AirtimeIni::CONF_FILE_LIQUIDSOAP);
|
AirtimeIni::CONF_FILE_LIQUIDSOAP,
|
||||||
|
AirtimeIni::CONF_FILE_MEDIAMONITOR);
|
||||||
$exist = false;
|
$exist = false;
|
||||||
foreach ($configFiles as $conf) {
|
foreach ($configFiles as $conf) {
|
||||||
if (file_exists($conf)) {
|
if (file_exists($conf)) {
|
||||||
|
@ -72,6 +74,10 @@ class AirtimeIni
|
||||||
echo "Could not copy liquidsoap.cfg to /etc/airtime/. Exiting.";
|
echo "Could not copy liquidsoap.cfg to /etc/airtime/. Exiting.";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (!copy(__DIR__."/../../python_apps/pytag-fs/MediaMonitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){
|
||||||
|
echo "Could not copy MediaMonitor.cfg to /etc/airtime/. Exiting.";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,6 +102,10 @@ class AirtimeIni
|
||||||
unlink(AirtimeIni::CONF_FILE_LIQUIDSOAP);
|
unlink(AirtimeIni::CONF_FILE_LIQUIDSOAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file_exists(AirtimeIni::CONF_FILE_MEDIAMONITOR)){
|
||||||
|
unlink(AirtimeIni::CONF_FILE_MEDIAMONITOR);
|
||||||
|
}
|
||||||
|
|
||||||
if (file_exists("etc/airtime")){
|
if (file_exists("etc/airtime")){
|
||||||
rmdir("/etc/airtime/");
|
rmdir("/etc/airtime/");
|
||||||
}
|
}
|
||||||
|
@ -171,6 +181,7 @@ class AirtimeIni
|
||||||
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_AIRTIME, 'airtime_dir', AirtimeInstall::CONF_DIR_WWW);
|
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_AIRTIME, 'airtime_dir', AirtimeInstall::CONF_DIR_WWW);
|
||||||
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_PYPO, 'api_key', "'$api_key'");
|
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_PYPO, 'api_key', "'$api_key'");
|
||||||
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_RECORDER, 'api_key', "'$api_key'");
|
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_RECORDER, 'api_key', "'$api_key'");
|
||||||
|
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_MEDIAMONITOR, 'api_key', "'$api_key'");
|
||||||
AirtimeIni::UpdateIniValue(AirtimeInstall::CONF_DIR_WWW.'/build/build.properties', 'project.home', AirtimeInstall::CONF_DIR_WWW);
|
AirtimeIni::UpdateIniValue(AirtimeInstall::CONF_DIR_WWW.'/build/build.properties', 'project.home', AirtimeInstall::CONF_DIR_WWW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ class AirtimeInstall
|
||||||
|
|
||||||
$username = $CC_CONFIG['dsn']['username'];
|
$username = $CC_CONFIG['dsn']['username'];
|
||||||
$password = $CC_CONFIG['dsn']['password'];
|
$password = $CC_CONFIG['dsn']['password'];
|
||||||
$command = "echo \"CREATE USER $username ENCRYPTED PASSWORD '$password' LOGIN CREATEDB NOCREATEUSER;\" | su postgres -c psql";
|
$command = "echo \"CREATE USER $username ENCRYPTED PASSWORD '$password' LOGIN CREATEDB NOCREATEUSER;\" | su postgres -c psql 2>/dev/null";
|
||||||
|
|
||||||
@exec($command, $output, $results);
|
@exec($command, $output, $results);
|
||||||
if ($results == 0) {
|
if ($results == 0) {
|
||||||
|
@ -150,7 +150,7 @@ class AirtimeInstall
|
||||||
|
|
||||||
$database = $CC_CONFIG['dsn']['database'];
|
$database = $CC_CONFIG['dsn']['database'];
|
||||||
$username = $CC_CONFIG['dsn']['username'];
|
$username = $CC_CONFIG['dsn']['username'];
|
||||||
$command = "echo \"CREATE DATABASE $database OWNER $username\" | su postgres -c psql";
|
$command = "echo \"CREATE DATABASE $database OWNER $username\" | su postgres -c psql 2>/dev/null";
|
||||||
|
|
||||||
@exec($command, $output, $results);
|
@exec($command, $output, $results);
|
||||||
if ($results == 0) {
|
if ($results == 0) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import json
|
||||||
import os
|
import os
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
AIRTIME_VERSION = "1.9.0"
|
||||||
|
|
||||||
def api_client_factory(config):
|
def api_client_factory(config):
|
||||||
if config["api_client"] == "airtime":
|
if config["api_client"] == "airtime":
|
||||||
|
@ -30,6 +31,24 @@ def api_client_factory(config):
|
||||||
print
|
print
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
def recursive_urlencode(d):
|
||||||
|
def recursion(d, base=None):
|
||||||
|
pairs = []
|
||||||
|
|
||||||
|
for key, value in d.items():
|
||||||
|
if hasattr(value, 'values'):
|
||||||
|
pairs += recursion(value, key)
|
||||||
|
else:
|
||||||
|
new_pair = None
|
||||||
|
if base:
|
||||||
|
new_pair = "%s[%s]=%s" % (base, urllib.quote(unicode(key)), urllib.quote(unicode(value)))
|
||||||
|
else:
|
||||||
|
new_pair = "%s=%s" % (urllib.quote(unicode(key)), urllib.quote(unicode(value)))
|
||||||
|
pairs.append(new_pair)
|
||||||
|
return pairs
|
||||||
|
|
||||||
|
return '&'.join(recursion(d))
|
||||||
|
|
||||||
class ApiClientInterface:
|
class ApiClientInterface:
|
||||||
|
|
||||||
# Implementation: optional
|
# Implementation: optional
|
||||||
|
@ -98,6 +117,9 @@ class ApiClientInterface:
|
||||||
def upload_recorded_show(self):
|
def upload_recorded_show(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def update_media_metadata(self, md):
|
||||||
|
pass
|
||||||
|
|
||||||
# Put here whatever tests you want to run to make sure your API is working
|
# Put here whatever tests you want to run to make sure your API is working
|
||||||
def test(self):
|
def test(self):
|
||||||
pass
|
pass
|
||||||
|
@ -121,6 +143,8 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
logger.debug("Trying to contact %s", url)
|
logger.debug("Trying to contact %s", url)
|
||||||
url = url.replace("%%api_key%%", self.config["api_key"])
|
url = url.replace("%%api_key%%", self.config["api_key"])
|
||||||
|
|
||||||
|
version = -1
|
||||||
|
response = None
|
||||||
try:
|
try:
|
||||||
response = urllib.urlopen(url)
|
response = urllib.urlopen(url)
|
||||||
data = response.read()
|
data = response.read()
|
||||||
|
@ -129,34 +153,26 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
version = response_json['version']
|
version = response_json['version']
|
||||||
logger.debug("Airtime Version %s detected", version)
|
logger.debug("Airtime Version %s detected", version)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
try:
|
|
||||||
if e[1] == 401:
|
if e[1] == 401:
|
||||||
if (verbose):
|
if (verbose):
|
||||||
print '#####################################'
|
print '#####################################'
|
||||||
print '# YOUR API KEY SEEMS TO BE INVALID:'
|
print '# YOUR API KEY SEEMS TO BE INVALID:'
|
||||||
print '# ' + self.config["api_key"]
|
print '# ' + self.config["api_key"]
|
||||||
print '#####################################'
|
print '#####################################'
|
||||||
return False
|
return -1
|
||||||
except Exception, e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
if e[1] == 404:
|
if e[1] == 404:
|
||||||
if (verbose):
|
if (verbose):
|
||||||
print '#####################################'
|
print '#####################################'
|
||||||
print '# Unable to contact the Airtime-API'
|
print '# Unable to contact the Airtime-API'
|
||||||
print '# ' + url
|
print '# ' + url
|
||||||
print '#####################################'
|
print '#####################################'
|
||||||
return False
|
return -1
|
||||||
except Exception, e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
version = 0
|
|
||||||
logger.error("Unable to detect Airtime Version - %s, Response: %s", e, response)
|
logger.error("Unable to detect Airtime Version - %s, Response: %s", e, response)
|
||||||
|
|
||||||
return version
|
return version
|
||||||
|
|
||||||
|
|
||||||
def test(self):
|
def test(self):
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
status, items = self.get_schedule('2010-01-01-00-00-00', '2011-01-01-00-00-00')
|
status, items = self.get_schedule('2010-01-01-00-00-00', '2011-01-01-00-00-00')
|
||||||
|
@ -175,12 +191,12 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
|
|
||||||
def is_server_compatible(self, verbose = True):
|
def is_server_compatible(self, verbose = True):
|
||||||
version = self.__get_airtime_version(verbose)
|
version = self.__get_airtime_version(verbose)
|
||||||
if (version == 0 or version == False):
|
if (version == -1):
|
||||||
if (verbose):
|
if (verbose):
|
||||||
print 'Unable to get Airtime version number.'
|
print 'Unable to get Airtime version number.'
|
||||||
print
|
print
|
||||||
return False
|
return False
|
||||||
elif (version[0:4] != "1.9."):
|
elif (version[0:3] != AIRTIME_VERSION):
|
||||||
if (verbose):
|
if (verbose):
|
||||||
print 'Airtime version: ' + str(version)
|
print 'Airtime version: ' + str(version)
|
||||||
print 'pypo not compatible with this version of Airtime.'
|
print 'pypo not compatible with this version of Airtime.'
|
||||||
|
@ -198,7 +214,6 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
# Construct the URL
|
# Construct the URL
|
||||||
#export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"]
|
|
||||||
export_url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["export_url"])
|
export_url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["export_url"])
|
||||||
|
|
||||||
logger.info("Fetching schedule from %s", export_url)
|
logger.info("Fetching schedule from %s", export_url)
|
||||||
|
@ -220,8 +235,6 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
#src = "http://%s:%s/%s/%s" % \
|
|
||||||
#(self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["get_media_url"])
|
|
||||||
src = uri + "/api_key/%%api_key%%"
|
src = uri + "/api_key/%%api_key%%"
|
||||||
logger.info("try to download from %s to %s", src, dst)
|
logger.info("try to download from %s to %s", src, dst)
|
||||||
src = src.replace("%%api_key%%", self.config["api_key"])
|
src = src.replace("%%api_key%%", self.config["api_key"])
|
||||||
|
@ -239,7 +252,6 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
playlist = schedule[pkey]
|
playlist = schedule[pkey]
|
||||||
schedule_id = playlist["schedule_id"]
|
schedule_id = playlist["schedule_id"]
|
||||||
#url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"]
|
|
||||||
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 = "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))
|
url = url.replace("%%schedule_id%%", str(schedule_id))
|
||||||
|
@ -268,7 +280,6 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
response = ''
|
response = ''
|
||||||
try:
|
try:
|
||||||
schedule_id = data
|
schedule_id = data
|
||||||
#url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"]
|
|
||||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_start_playing_url"])
|
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_start_playing_url"])
|
||||||
url = url.replace("%%media_id%%", str(media_id))
|
url = url.replace("%%media_id%%", str(media_id))
|
||||||
url = url.replace("%%schedule_id%%", str(schedule_id))
|
url = url.replace("%%schedule_id%%", str(schedule_id))
|
||||||
|
@ -299,7 +310,6 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
response = None
|
response = None
|
||||||
try:
|
try:
|
||||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["show_schedule_url"])
|
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["show_schedule_url"])
|
||||||
#url = self.config["base_url"] + self.config["api_base"] + self.config["show_schedule_url"]
|
|
||||||
logger.debug(url)
|
logger.debug(url)
|
||||||
url = url.replace("%%api_key%%", self.config["api_key"])
|
url = url.replace("%%api_key%%", self.config["api_key"])
|
||||||
|
|
||||||
|
@ -319,7 +329,6 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
retries = int(self.config["upload_retries"])
|
retries = int(self.config["upload_retries"])
|
||||||
retries_wait = int(self.config["upload_wait"])
|
retries_wait = int(self.config["upload_wait"])
|
||||||
|
|
||||||
#url = self.config["base_url"] + self.config["api_base"] + self.config["upload_file_url"]
|
|
||||||
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["upload_file_url"])
|
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["upload_file_url"])
|
||||||
|
|
||||||
logger.debug(url)
|
logger.debug(url)
|
||||||
|
@ -347,6 +356,26 @@ class AirTimeApiClient(ApiClientInterface):
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def update_media_metadata(self, md):
|
||||||
|
logger = logging.getLogger()
|
||||||
|
response = None
|
||||||
|
try:
|
||||||
|
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_media_url"])
|
||||||
|
logger.debug(url)
|
||||||
|
url = url.replace("%%api_key%%", self.config["api_key"])
|
||||||
|
|
||||||
|
data = recursive_urlencode(md)
|
||||||
|
req = urllib2.Request(url, data)
|
||||||
|
|
||||||
|
response = urllib2.urlopen(req).read()
|
||||||
|
logger.info("update media %s", response)
|
||||||
|
response = json.loads(response)
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
logger.error("Exception: %s", e)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
|
@ -64,6 +64,21 @@ def get_current_script_dir():
|
||||||
#print current_script_dir[0:index]
|
#print current_script_dir[0:index]
|
||||||
return current_script_dir[0:index]
|
return current_script_dir[0:index]
|
||||||
|
|
||||||
|
def is_natty():
|
||||||
|
try:
|
||||||
|
f = open('/etc/lsb-release')
|
||||||
|
except IOError as e:
|
||||||
|
#File doesn't exist, so we're not even dealing with Ubuntu
|
||||||
|
return False
|
||||||
|
|
||||||
|
for line in f:
|
||||||
|
split = line.split("=")
|
||||||
|
split[0] = split[0].strip(" \r\n")
|
||||||
|
split[1] = split[1].strip(" \r\n")
|
||||||
|
if split[0] == "DISTRIB_CODENAME" and split[1] == "natty":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# load config file
|
# load config file
|
||||||
|
@ -94,10 +109,19 @@ try:
|
||||||
create_path(config["file_dir"])
|
create_path(config["file_dir"])
|
||||||
create_path(config["tmp_dir"])
|
create_path(config["tmp_dir"])
|
||||||
|
|
||||||
if platform.architecture()[0] == '64bit':
|
architecture = platform.architecture()[0]
|
||||||
|
natty = is_natty()
|
||||||
|
|
||||||
|
if architecture == '64bit' and natty:
|
||||||
|
print "Installing 64-bit liquidsoap binary (Natty)"
|
||||||
|
shutil.copy("%s/../liquidsoap/liquidsoap-amd64-natty"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)
|
||||||
|
elif architecture == '32bit' and natty:
|
||||||
|
print "Installing 32-bit liquidsoap binary (Natty)"
|
||||||
|
shutil.copy("%s/../liquidsoap/liquidsoap-i386-natty"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)
|
||||||
|
elif architecture == '64bit' and not natty:
|
||||||
print "Installing 64-bit liquidsoap binary"
|
print "Installing 64-bit liquidsoap binary"
|
||||||
shutil.copy("%s/../liquidsoap/liquidsoap-amd64"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)
|
shutil.copy("%s/../liquidsoap/liquidsoap-amd64"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)
|
||||||
elif platform.architecture()[0] == '32bit':
|
elif architecture == '32bit' and not natty:
|
||||||
print "Installing 32-bit liquidsoap binary"
|
print "Installing 32-bit liquidsoap binary"
|
||||||
shutil.copy("%s/../liquidsoap/liquidsoap-i386"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)
|
shutil.copy("%s/../liquidsoap/liquidsoap-i386"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)
|
||||||
else:
|
else:
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -3,29 +3,14 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Python part of radio playout (pypo)
|
Python part of radio playout (pypo)
|
||||||
|
|
||||||
The main functions are "fetch" (./pypo_cli.py -f) and "push" (./pypo_cli.py -p)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
#import calendar
|
|
||||||
#import traceback
|
|
||||||
from optparse import *
|
from optparse import *
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
#import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
import logging.config
|
||||||
#import shutil
|
|
||||||
#import urllib
|
|
||||||
#import urllib2
|
|
||||||
#import pickle
|
|
||||||
#import telnetlib
|
|
||||||
#import random
|
|
||||||
#import string
|
|
||||||
#import operator
|
|
||||||
#import inspect
|
|
||||||
from Queue import Queue
|
from Queue import Queue
|
||||||
|
|
||||||
from pypopush import PypoPush
|
from pypopush import PypoPush
|
||||||
|
@ -50,8 +35,6 @@ parser = OptionParser(usage=usage)
|
||||||
parser.add_option("-v", "--compat", help="Check compatibility with server API version", default=False, action="store_true", dest="check_compat")
|
parser.add_option("-v", "--compat", help="Check compatibility with server API version", default=False, action="store_true", dest="check_compat")
|
||||||
|
|
||||||
parser.add_option("-t", "--test", help="Do a test to make sure everything is working properly.", default=False, action="store_true", dest="test")
|
parser.add_option("-t", "--test", help="Do a test to make sure everything is working properly.", default=False, action="store_true", dest="test")
|
||||||
parser.add_option("-f", "--fetch-scheduler", help="Fetch the schedule from server. This is a polling process that runs forever.", default=False, action="store_true", dest="fetch_scheduler")
|
|
||||||
parser.add_option("-p", "--push-scheduler", help="Push the schedule to Liquidsoap. This is a polling process that runs forever.", default=False, action="store_true", dest="push_scheduler")
|
|
||||||
parser.add_option("-b", "--cleanup", help="Cleanup", default=False, action="store_true", dest="cleanup")
|
parser.add_option("-b", "--cleanup", help="Cleanup", default=False, action="store_true", dest="cleanup")
|
||||||
parser.add_option("-c", "--check", help="Check the cached schedule and exit", default=False, action="store_true", dest="check")
|
parser.add_option("-c", "--check", help="Check the cached schedule and exit", default=False, action="store_true", dest="check")
|
||||||
|
|
||||||
|
@ -76,8 +59,7 @@ class Global:
|
||||||
|
|
||||||
def selfcheck(self):
|
def selfcheck(self):
|
||||||
self.api_client = api_client.api_client_factory(config)
|
self.api_client = api_client.api_client_factory(config)
|
||||||
if (not self.api_client.is_server_compatible()):
|
return self.api_client.is_server_compatible()
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
def set_export_source(self, export_source):
|
def set_export_source(self, export_source):
|
||||||
self.export_source = export_source
|
self.export_source = export_source
|
||||||
|
@ -130,7 +112,8 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# initialize
|
# initialize
|
||||||
g = Global()
|
g = Global()
|
||||||
g.selfcheck()
|
|
||||||
|
while not g.selfcheck(): time.sleep(5000)
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,8 @@ base_port = 80
|
||||||
# where the binary files live
|
# where the binary files live
|
||||||
bin_dir = '/usr/lib/airtime/media-monitor'
|
bin_dir = '/usr/lib/airtime/media-monitor'
|
||||||
|
|
||||||
# base path to store recordered shows at
|
|
||||||
base_recorded_files = '/var/tmp/airtime/show-recorder/'
|
|
||||||
|
|
||||||
# where the logging files live
|
# where the logging files live
|
||||||
log_dir = '/var/log/airtime/show-recorder'
|
log_dir = '/var/log/airtime/media-monitor'
|
||||||
|
|
||||||
# Value needed to access the API
|
# Value needed to access the API
|
||||||
api_key = 'AAA'
|
api_key = 'AAA'
|
||||||
|
@ -22,8 +19,18 @@ api_base = 'api'
|
||||||
# URL to get the version number of the server API
|
# URL to get the version number of the server API
|
||||||
version_url = 'version/api_key/%%api_key%%'
|
version_url = 'version/api_key/%%api_key%%'
|
||||||
|
|
||||||
# URL to get the schedule of shows set to record
|
# URL to tell Airtime to update file's meta data
|
||||||
show_schedule_url = 'recorded-shows/format/json/api_key/%%api_key%%'
|
update_media_url = 'reload-metadata/format/json/api_key/%%api_key%%'
|
||||||
|
|
||||||
# URL to upload the recorded show's file to Airtime
|
############################################
|
||||||
upload_file_url = 'upload-recorded/format/json/api_key/%%api_key%%'
|
# RabbitMQ settings #
|
||||||
|
############################################
|
||||||
|
rabbitmq_host = 'localhost'
|
||||||
|
rabbitmq_user = 'guest'
|
||||||
|
rabbitmq_password = 'guest'
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# Media-Monitor preferences #
|
||||||
|
############################################
|
||||||
|
check_filesystem_events = 30 #how long to queue up events performed on the files themselves.
|
||||||
|
check_airtime_events = 30 #how long to queue metadata input from airtime.
|
||||||
|
|
|
@ -1,6 +1,26 @@
|
||||||
|
#!/usr/local/bin/python
|
||||||
|
import logging
|
||||||
|
import logging.config
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
|
||||||
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
import mutagen
|
||||||
import pyinotify
|
import pyinotify
|
||||||
from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
|
from pyinotify import WatchManager, Notifier, ProcessEvent
|
||||||
|
|
||||||
|
# For RabbitMQ
|
||||||
|
from kombu.connection import BrokerConnection
|
||||||
|
from kombu.messaging import Exchange, Queue, Consumer, Producer
|
||||||
|
from api_clients import api_client
|
||||||
|
|
||||||
# configure logging
|
# configure logging
|
||||||
try:
|
try:
|
||||||
|
@ -11,41 +31,183 @@ except Exception, e:
|
||||||
|
|
||||||
# loading config file
|
# loading config file
|
||||||
try:
|
try:
|
||||||
config = ConfigObj('/etc/airtime/recorder.cfg')
|
config = ConfigObj('/etc/airtime/MediaMonitor.cfg')
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print 'Error loading config file: ', e
|
print 'Error loading config file: ', e
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
# watched events
|
"""
|
||||||
mask = pyinotify.ALL_EVENTS
|
list of supported easy tags in mutagen version 1.20
|
||||||
|
['albumartistsort', 'musicbrainz_albumstatus', 'lyricist', 'releasecountry', 'date', 'performer', 'musicbrainz_albumartistid', 'composer', 'encodedby', 'tracknumber', 'musicbrainz_albumid', 'album', 'asin', 'musicbrainz_artistid', 'mood', 'copyright', 'author', 'media', 'length', 'version', 'artistsort', 'titlesort', 'discsubtitle', 'website', 'musicip_fingerprint', 'conductor', 'compilation', 'barcode', 'performer:*', 'composersort', 'musicbrainz_discid', 'musicbrainz_albumtype', 'genre', 'isrc', 'discnumber', 'musicbrainz_trmid', 'replaygain_*_gain', 'musicip_puid', 'artist', 'title', 'bpm', 'musicbrainz_trackid', 'arranger', 'albumsort', 'replaygain_*_peak', 'organization']
|
||||||
|
"""
|
||||||
|
|
||||||
wm = WatchManager()
|
def checkRabbitMQ(notifier):
|
||||||
wdd = wm.add_watch('/srv/airtime/stor', mask, rec=True)
|
try:
|
||||||
|
notifier.connection.drain_events(timeout=int(config["check_airtime_events"]))
|
||||||
|
except Exception, e:
|
||||||
|
logger = logging.getLogger('root')
|
||||||
|
logger.info("%s", e)
|
||||||
|
|
||||||
|
class AirtimeNotifier(Notifier):
|
||||||
|
|
||||||
|
def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, threshold=0, timeout=None):
|
||||||
|
Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, threshold, timeout)
|
||||||
|
|
||||||
|
self.airtime2mutagen = {\
|
||||||
|
"track_title": "title",\
|
||||||
|
"artist_name": "artist",\
|
||||||
|
"album_title": "album",\
|
||||||
|
"genre": "genre",\
|
||||||
|
"mood": "mood",\
|
||||||
|
"track_number": "tracknumber",\
|
||||||
|
"bpm": "bpm",\
|
||||||
|
"label": "organization",\
|
||||||
|
"composer": "composer",\
|
||||||
|
"encoded_by": "encodedby",\
|
||||||
|
"conductor": "conductor",\
|
||||||
|
"year": "date",\
|
||||||
|
"info_url": "website",\
|
||||||
|
"isrc_number": "isrc",\
|
||||||
|
"copyright": "copyright",\
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_exchange = Exchange("airtime-media-monitor", "direct", durable=True, auto_delete=True)
|
||||||
|
schedule_queue = Queue("media-monitor", exchange=schedule_exchange, key="filesystem")
|
||||||
|
self.connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], "/")
|
||||||
|
channel = self.connection.channel()
|
||||||
|
consumer = Consumer(channel, schedule_queue)
|
||||||
|
consumer.register_callback(self.handle_message)
|
||||||
|
consumer.consume()
|
||||||
|
|
||||||
|
def handle_message(self, body, message):
|
||||||
|
# ACK the message to take it off the queue
|
||||||
|
message.ack()
|
||||||
|
|
||||||
|
logger = logging.getLogger('root')
|
||||||
|
logger.info("Received md from RabbitMQ: " + body)
|
||||||
|
|
||||||
|
m = json.loads(message.body)
|
||||||
|
airtime_file = mutagen.File(m['filepath'], easy=True)
|
||||||
|
del m['filepath']
|
||||||
|
for key in m.keys() :
|
||||||
|
if m[key] != "" :
|
||||||
|
airtime_file[self.airtime2mutagen[key]] = m[key]
|
||||||
|
|
||||||
|
airtime_file.save()
|
||||||
|
|
||||||
|
class MediaMonitor(ProcessEvent):
|
||||||
|
|
||||||
|
def my_init(self):
|
||||||
|
"""
|
||||||
|
Method automatically called from ProcessEvent.__init__(). Additional
|
||||||
|
keyworded arguments passed to ProcessEvent.__init__() are then
|
||||||
|
delegated to my_init().
|
||||||
|
"""
|
||||||
|
self.api_client = api_client.api_client_factory(config)
|
||||||
|
|
||||||
|
self.mutagen2airtime = {\
|
||||||
|
"title": "track_title",\
|
||||||
|
"artist": "artist_name",\
|
||||||
|
"album": "album_title",\
|
||||||
|
"genre": "genre",\
|
||||||
|
"mood": "mood",\
|
||||||
|
"tracknumber": "track_number",\
|
||||||
|
"bpm": "bpm",\
|
||||||
|
"organization": "label",\
|
||||||
|
"composer": "composer",\
|
||||||
|
"encodedby": "encoded_by",\
|
||||||
|
"conductor": "conductor",\
|
||||||
|
"date": "year",\
|
||||||
|
"website": "info_url",\
|
||||||
|
"isrc": "isrc_number",\
|
||||||
|
"copyright": "copyright",\
|
||||||
|
}
|
||||||
|
|
||||||
|
self.logger = logging.getLogger('root')
|
||||||
|
|
||||||
|
self.temp_files = {}
|
||||||
|
|
||||||
|
def update_airtime(self, event):
|
||||||
|
self.logger.info("Updating Change to Airtime")
|
||||||
|
try:
|
||||||
|
f = open(event.pathname, 'rb')
|
||||||
|
m = hashlib.md5()
|
||||||
|
m.update(f.read())
|
||||||
|
|
||||||
|
md5 = m.hexdigest()
|
||||||
|
gunid = event.name.split('.')[0]
|
||||||
|
|
||||||
|
md = {'gunid':gunid, 'md5':md5}
|
||||||
|
|
||||||
|
file_info = mutagen.File(event.pathname, easy=True)
|
||||||
|
attrs = self.mutagen2airtime
|
||||||
|
for key in file_info.keys() :
|
||||||
|
if key in attrs :
|
||||||
|
md[attrs[key]] = file_info[key][0]
|
||||||
|
|
||||||
|
data = {'md': md}
|
||||||
|
|
||||||
|
response = self.api_client.update_media_metadata(data)
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.info("%s", e)
|
||||||
|
|
||||||
class PTmp(ProcessEvent):
|
|
||||||
def process_IN_CREATE(self, event):
|
def process_IN_CREATE(self, event):
|
||||||
if event.dir :
|
if not event.dir :
|
||||||
global wm
|
filename_info = event.name.split(".")
|
||||||
wdd = wm.add_watch(event.pathname, mask, rec=True)
|
|
||||||
#print wdd.keys()
|
|
||||||
|
|
||||||
print "%s: %s" % (event.maskname, os.path.join(event.path, event.name))
|
#file created is a tmp file which will be modified and then moved back to the original filename.
|
||||||
|
if len(filename_info) > 2 :
|
||||||
|
self.temp_files[event.pathname] = None
|
||||||
|
#This is a newly imported file.
|
||||||
|
else :
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.logger.info("%s: %s", event.maskname, event.pathname)
|
||||||
|
|
||||||
|
#event.path : /srv/airtime/stor/bd2
|
||||||
|
#event.name : bd2aa73b58d9c8abcced989621846e99.mp3
|
||||||
|
#event.pathname : /srv/airtime/stor/bd2/bd2aa73b58d9c8abcced989621846e99.mp3
|
||||||
def process_IN_MODIFY(self, event):
|
def process_IN_MODIFY(self, event):
|
||||||
if not event.dir :
|
if not event.dir :
|
||||||
print event.path
|
filename_info = event.name.split(".")
|
||||||
|
|
||||||
print "%s: %s" % (event.maskname, os.path.join(event.path, event.name))
|
#file modified is not a tmp file.
|
||||||
|
if len(filename_info) == 2 :
|
||||||
|
self.update_airtime(event)
|
||||||
|
|
||||||
|
self.logger.info("%s: path: %s name: %s", event.maskname, event.path, event.name)
|
||||||
|
|
||||||
|
def process_IN_MOVED_FROM(self, event):
|
||||||
|
if event.pathname in self.temp_files :
|
||||||
|
del self.temp_files[event.pathname]
|
||||||
|
self.temp_files[event.cookie] = event.pathname
|
||||||
|
|
||||||
|
self.logger.info("%s: %s", event.maskname, event.pathname)
|
||||||
|
|
||||||
|
def process_IN_MOVED_TO(self, event):
|
||||||
|
if event.cookie in self.temp_files :
|
||||||
|
del self.temp_files[event.cookie]
|
||||||
|
self.update_airtime(event)
|
||||||
|
|
||||||
|
self.logger.info("%s: %s", event.maskname, event.pathname)
|
||||||
|
|
||||||
def process_default(self, event):
|
def process_default(self, event):
|
||||||
print "%s: %s" % (event.maskname, os.path.join(event.path, event.name))
|
self.logger.info("%s: %s", event.maskname, event.pathname)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
try:
|
try:
|
||||||
notifier = Notifier(wm, PTmp(), read_freq=2, timeout=1)
|
# watched events
|
||||||
|
mask = pyinotify.IN_CREATE | pyinotify.IN_MODIFY | pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
|
||||||
|
#mask = pyinotify.ALL_EVENTS
|
||||||
|
|
||||||
|
wm = WatchManager()
|
||||||
|
wdd = wm.add_watch('/srv/airtime/stor', mask, rec=True, auto_add=True)
|
||||||
|
|
||||||
|
notifier = AirtimeNotifier(wm, MediaMonitor(), read_freq=int(config["check_filesystem_events"]), timeout=1)
|
||||||
notifier.coalesce_events()
|
notifier.coalesce_events()
|
||||||
notifier.loop()
|
notifier.loop(callback=checkRabbitMQ)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
notifier.stop()
|
notifier.stop()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
print "Please run this as root."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
print "Starting daemontool script recorder"
|
||||||
|
os.system("svc -u /etc/service/recorder")
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
print "exception:" + str(e)
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
print "Please run this as root."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
print "Stopping daemontool script recorder"
|
||||||
|
|
||||||
|
p1 = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
|
||||||
|
p2 = subprocess.Popen(["awk", "/recorder.py/ && !/awk/ {print $2}"], stdin=p1.stdout, stdout=subprocess.PIPE)
|
||||||
|
recorder_pid = p2.communicate()[0].strip(" \n\r\t")
|
||||||
|
if (len(recorder_pid) > 0):
|
||||||
|
os.system("svc -d /etc/service/recorder 1>/dev/null 2>&1")
|
||||||
|
os.system("svc -d /etc/service/recorder/log 1>/dev/null 2>&1")
|
||||||
|
os.system("kill -2 %s" % recorder_pid)
|
||||||
|
print "Success."
|
||||||
|
else:
|
||||||
|
print "Not Running."
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
print "exception:" + str(e)
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
exec setuidgid pypo multilog t /var/log/airtime/media-monitor/main
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/sh
|
||||||
|
media_monitor_user="pypo"
|
||||||
|
|
||||||
|
# Location of pypo_cli.py Python script
|
||||||
|
media_monitor_path="/usr/lib/airtime/media-monitor/"
|
||||||
|
media_monitor_script="MediaMonitor.py"
|
||||||
|
|
||||||
|
api_client_path="/usr/lib/airtime/pypo/"
|
||||||
|
cd ${media_monitor_path}
|
||||||
|
|
||||||
|
echo "*** Daemontools: starting daemon"
|
||||||
|
exec 2>&1
|
||||||
|
# Note the -u when calling python! we need it to get unbuffered binary stdout and stderr
|
||||||
|
|
||||||
|
export PYTHONPATH=${api_client_path}
|
||||||
|
setuidgid ${media_monitor_user} python -u ${media_monitor_path}${media_monitor_script}
|
||||||
|
# EOF
|
|
@ -0,0 +1,127 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
from optparse import *
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import logging.config
|
||||||
|
import shutil
|
||||||
|
import string
|
||||||
|
import platform
|
||||||
|
from configobj import ConfigObj
|
||||||
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
print "Please run this as root."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
PATH_INI_FILE = '/etc/airtime/MediaMonitor.cfg'
|
||||||
|
|
||||||
|
def create_path(path):
|
||||||
|
if not (os.path.exists(path)):
|
||||||
|
print "Creating directory " + path
|
||||||
|
os.makedirs(path)
|
||||||
|
|
||||||
|
def create_user(username):
|
||||||
|
print "Checking for user "+username
|
||||||
|
p = Popen('id '+username, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
|
||||||
|
output = p.stdout.read()
|
||||||
|
if (output[0:3] != "uid"):
|
||||||
|
# Make the pypo user
|
||||||
|
print "Creating user "+username
|
||||||
|
os.system("adduser --system --quiet --group --shell /bin/bash "+username)
|
||||||
|
|
||||||
|
#set pypo password
|
||||||
|
p = os.popen('/usr/bin/passwd pypo 1>/dev/null 2>&1', 'w')
|
||||||
|
p.write('pypo\n')
|
||||||
|
p.write('pypo\n')
|
||||||
|
p.close()
|
||||||
|
else:
|
||||||
|
print "User already exists."
|
||||||
|
#add pypo to audio group
|
||||||
|
os.system("adduser " + username + " audio 1>/dev/null 2>&1")
|
||||||
|
#add pypo to www-data group
|
||||||
|
os.system("adduser " + username + " www-data 1>/dev/null 2>&1")
|
||||||
|
|
||||||
|
def copy_dir(src_dir, dest_dir):
|
||||||
|
if (os.path.exists(dest_dir)) and (dest_dir != "/"):
|
||||||
|
print "Removing old directory "+dest_dir
|
||||||
|
shutil.rmtree(dest_dir)
|
||||||
|
if not (os.path.exists(dest_dir)):
|
||||||
|
print "Copying directory "+os.path.realpath(src_dir)+" to "+os.path.realpath(dest_dir)
|
||||||
|
shutil.copytree(src_dir, dest_dir)
|
||||||
|
|
||||||
|
def get_current_script_dir():
|
||||||
|
current_script_dir = os.path.realpath(__file__)
|
||||||
|
index = current_script_dir.rindex('/')
|
||||||
|
#print current_script_dir[0:index]
|
||||||
|
return current_script_dir[0:index]
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
# load config file
|
||||||
|
try:
|
||||||
|
config = ConfigObj(PATH_INI_FILE)
|
||||||
|
except Exception, e:
|
||||||
|
print 'Error loading config file: ', e
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
current_script_dir = get_current_script_dir()
|
||||||
|
print "Checking and removing any existing media monitor processes"
|
||||||
|
os.system("python %s/media-monitor-uninstall.py 1>/dev/null 2>&1"% current_script_dir)
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
# Create users
|
||||||
|
create_user("pypo")
|
||||||
|
|
||||||
|
print "Creating log directories"
|
||||||
|
create_path(config["log_dir"])
|
||||||
|
os.system("chmod -R 755 " + config["log_dir"])
|
||||||
|
os.system("chown -R pypo:pypo "+config["log_dir"])
|
||||||
|
|
||||||
|
copy_dir("%s/.."%current_script_dir, config["bin_dir"])
|
||||||
|
|
||||||
|
print "Setting permissions"
|
||||||
|
os.system("chmod -R 755 "+config["bin_dir"])
|
||||||
|
os.system("chown -R pypo:pypo "+config["bin_dir"])
|
||||||
|
|
||||||
|
print "Creating symbolic links"
|
||||||
|
os.system("rm -f /usr/bin/airtime-media-monitor-start")
|
||||||
|
os.system("ln -s "+config["bin_dir"]+"/airtime-media-monitor-start /usr/bin/")
|
||||||
|
os.system("rm -f /usr/bin/airtime-media-monitor-stop")
|
||||||
|
os.system("ln -s "+config["bin_dir"]+"/airtime-media-monitor-stop /usr/bin/")
|
||||||
|
|
||||||
|
print "Installing recorder daemon"
|
||||||
|
create_path("/etc/service/media-monitor")
|
||||||
|
create_path("/etc/service/media-monitor/log")
|
||||||
|
shutil.copy("%s/media-monitor-daemontools.sh"%current_script_dir, "/etc/service/media-monitor/run")
|
||||||
|
shutil.copy("%s/media-monitor-daemontools-logger.sh"%current_script_dir, "/etc/service/media-monitor/log/run")
|
||||||
|
os.system("chmod -R 755 /etc/service/media-monitor")
|
||||||
|
os.system("chown -R pypo:pypo /etc/service/media-monitor")
|
||||||
|
|
||||||
|
print "Waiting for processes to start..."
|
||||||
|
time.sleep(5)
|
||||||
|
os.system("python /usr/bin/airtime-media-monitor-start")
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
found = True
|
||||||
|
|
||||||
|
p = Popen('svstat /etc/service/media-monitor', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
|
||||||
|
output = p.stdout.read()
|
||||||
|
if (output.find("unable to open supervise/ok: file does not exist") >= 0):
|
||||||
|
found = False
|
||||||
|
print output
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
print "Media monitor install has completed, but daemontools is not running, please make sure you have it installed and then reboot."
|
||||||
|
except Exception, e:
|
||||||
|
print "exception:" + str(e)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
print "Please run this as root."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
PATH_INI_FILE = '/etc/airtime/MediaMonitor.cfg'
|
||||||
|
|
||||||
|
def remove_path(path):
|
||||||
|
os.system("rm -rf " + path)
|
||||||
|
|
||||||
|
def remove_user(username):
|
||||||
|
os.system("killall -u %s 1>/dev/null 2>&1" % username)
|
||||||
|
|
||||||
|
#allow all process to be completely closed before we attempt to delete user
|
||||||
|
print "Waiting for processes to close..."
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
os.system("deluser --remove-home " + username + " 1>/dev/null 2>&1")
|
||||||
|
|
||||||
|
def get_current_script_dir():
|
||||||
|
current_script_dir = os.path.realpath(__file__)
|
||||||
|
index = current_script_dir.rindex('/')
|
||||||
|
return current_script_dir[0:index]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# load config file
|
||||||
|
try:
|
||||||
|
config = ConfigObj(PATH_INI_FILE)
|
||||||
|
except Exception, e:
|
||||||
|
print 'Error loading config file: ', e
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
os.system("python /usr/bin/airtime-media-monitor-stop")
|
||||||
|
|
||||||
|
print "Removing log directories"
|
||||||
|
remove_path(config["log_dir"])
|
||||||
|
|
||||||
|
print "Removing symlinks"
|
||||||
|
os.system("rm -f /usr/bin/airtime-media-monitor-start")
|
||||||
|
os.system("rm -f /usr/bin/airtime-media-monitor-stop")
|
||||||
|
|
||||||
|
print "Removing application files"
|
||||||
|
remove_path(config["bin_dir"])
|
||||||
|
|
||||||
|
print "Removing daemontool script media-monitor"
|
||||||
|
remove_path("rm -rf /etc/service/media-monitor")
|
||||||
|
|
||||||
|
remove_user("pypo")
|
||||||
|
print "Uninstall complete."
|
||||||
|
except Exception, e:
|
||||||
|
print "exception:" + str(e)
|
|
@ -17,7 +17,7 @@ exec 2>&1
|
||||||
export PYTHONPATH=${api_client_path}
|
export PYTHONPATH=${api_client_path}
|
||||||
#su ${recorder_user} -c "python -u ${recorder_path}${recorder_script}"
|
#su ${recorder_user} -c "python -u ${recorder_path}${recorder_script}"
|
||||||
|
|
||||||
setuidgid ${recorder_user} ${recorder_path}${recorder_script}
|
setuidgid ${recorder_user} python -u ${recorder_path}${recorder_script}
|
||||||
|
|
||||||
|
|
||||||
# EOF
|
# EOF
|
||||||
|
|
|
@ -55,6 +55,7 @@ class ShowRecorder(Thread):
|
||||||
self.start_time = start_time
|
self.start_time = start_time
|
||||||
self.filetype = filetype
|
self.filetype = filetype
|
||||||
self.show_instance = show_instance
|
self.show_instance = show_instance
|
||||||
|
self.logger = logging.getLogger('root')
|
||||||
|
|
||||||
def record_show(self):
|
def record_show(self):
|
||||||
|
|
||||||
|
@ -67,11 +68,9 @@ class ShowRecorder(Thread):
|
||||||
#-ge:0.1,0.1,0,-1
|
#-ge:0.1,0.1,0,-1
|
||||||
args = command.split(" ")
|
args = command.split(" ")
|
||||||
|
|
||||||
print "starting record"
|
self.logger.info("starting record")
|
||||||
|
|
||||||
code = call(args)
|
code = call(args)
|
||||||
|
self.logger.info("finishing record, return code %s", code)
|
||||||
print "finishing record, return code %s" % (code)
|
|
||||||
|
|
||||||
return code, filepath
|
return code, filepath
|
||||||
|
|
||||||
|
@ -94,7 +93,7 @@ class ShowRecorder(Thread):
|
||||||
if code == 0:
|
if code == 0:
|
||||||
self.upload_file(filepath)
|
self.upload_file(filepath)
|
||||||
else:
|
else:
|
||||||
print "problem recording show"
|
self.logger.info("problem recording show")
|
||||||
|
|
||||||
|
|
||||||
class Record():
|
class Record():
|
||||||
|
@ -102,6 +101,7 @@ class Record():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.api_client = api_client.api_client_factory(config)
|
self.api_client = api_client.api_client_factory(config)
|
||||||
self.shows_to_record = {}
|
self.shows_to_record = {}
|
||||||
|
self.logger = logging.getLogger('root')
|
||||||
|
|
||||||
def process_shows(self, shows):
|
def process_shows(self, shows):
|
||||||
|
|
||||||
|
@ -123,21 +123,21 @@ class Record():
|
||||||
start_time = sorted_show_keys[0]
|
start_time = sorted_show_keys[0]
|
||||||
next_show = getDateTimeObj(start_time)
|
next_show = getDateTimeObj(start_time)
|
||||||
|
|
||||||
print next_show
|
self.logger.debug("Next show %s", next_show)
|
||||||
print tnow
|
self.logger.debug("Now %s", tnow)
|
||||||
|
|
||||||
delta = next_show - tnow
|
delta = next_show - tnow
|
||||||
min_delta = datetime.timedelta(seconds=60)
|
min_delta = datetime.timedelta(seconds=60)
|
||||||
|
|
||||||
if delta <= min_delta:
|
if delta <= min_delta:
|
||||||
print "sleeping %s seconds until show" % (delta.seconds)
|
self.logger.debug("sleeping %s seconds until show", delta.seconds)
|
||||||
time.sleep(delta.seconds)
|
time.sleep(delta.seconds)
|
||||||
|
|
||||||
show_length = self.shows_to_record[start_time][0]
|
show_length = self.shows_to_record[start_time][0]
|
||||||
show_instance = self.shows_to_record[start_time][1]
|
show_instance = self.shows_to_record[start_time][1]
|
||||||
show_name = self.shows_to_record[start_time][2]
|
show_name = self.shows_to_record[start_time][2]
|
||||||
|
|
||||||
show = ShowRecorder(show_instance, show_length.seconds, show_name, start_time, filetype="mp3", )
|
show = ShowRecorder(show_instance, show_length.seconds, show_name, start_time, filetype="mp3")
|
||||||
show.start()
|
show.start()
|
||||||
|
|
||||||
#remove show from shows to record.
|
#remove show from shows to record.
|
||||||
|
@ -165,7 +165,3 @@ if __name__ == '__main__':
|
||||||
recorder.get_shows()
|
recorder.get_shows()
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue