Merge branch '2.1.x' into devel
Conflicts: python_apps/pypo/liquidsoap_scripts/ls_script.liq
This commit is contained in:
commit
335dd1516b
|
@ -23,8 +23,9 @@ date_default_timezone_set('UTC');
|
||||||
date_default_timezone_set(Application_Model_Preference::GetTimezone());
|
date_default_timezone_set(Application_Model_Preference::GetTimezone());
|
||||||
|
|
||||||
global $CC_CONFIG;
|
global $CC_CONFIG;
|
||||||
$CC_CONFIG['airtime_version'] = Application_Model_Preference::GetAirtimeVersion();
|
$airtime_version = Application_Model_Preference::GetAirtimeVersion();
|
||||||
|
$uniqueid = Application_Model_Preference::GetUniqueId();
|
||||||
|
$CC_CONFIG['airtime_version'] = md5($airtime_version.$uniqueid);
|
||||||
require_once __DIR__."/configs/navigation.php";
|
require_once __DIR__."/configs/navigation.php";
|
||||||
|
|
||||||
Zend_Validate::setDefaultNamespaces("Zend");
|
Zend_Validate::setDefaultNamespaces("Zend");
|
||||||
|
|
|
@ -41,7 +41,7 @@ class LibraryController extends Zend_Controller_Action
|
||||||
$user = new Application_Model_User($userInfo->id);
|
$user = new Application_Model_User($userInfo->id);
|
||||||
|
|
||||||
//Open a jPlayer window and play the audio clip.
|
//Open a jPlayer window and play the audio clip.
|
||||||
$menu["play"] = array("name"=> "Play", "icon" => "play");
|
$menu["play"] = array("name"=> "Preview", "icon" => "play");
|
||||||
|
|
||||||
$isAdminOrPM = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER));
|
$isAdminOrPM = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER));
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,12 @@ class LoginController extends Zend_Controller_Action
|
||||||
|
|
||||||
public function passwordRestoreAction()
|
public function passwordRestoreAction()
|
||||||
{
|
{
|
||||||
|
global $CC_CONFIG;
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$baseUrl = $request->getBaseUrl();
|
||||||
|
$this->view->headScript()->appendFile($baseUrl.'/js/airtime/login/password-restore.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
|
||||||
|
|
||||||
if (!Application_Model_Preference::GetEnableSystemEmail()) {
|
if (!Application_Model_Preference::GetEnableSystemEmail()) {
|
||||||
$this->_redirect('login');
|
$this->_redirect('login');
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,5 +29,13 @@ class Application_Form_PasswordRestore extends Zend_Form
|
||||||
'ViewHelper'
|
'ViewHelper'
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$cancel = new Zend_Form_Element_Button("cancel");
|
||||||
|
$cancel->class = 'ui-button ui-widget ui-state-default ui-button-text-only center';
|
||||||
|
$cancel->setLabel("Cancel")
|
||||||
|
->setIgnore(True)
|
||||||
|
->setAttrib('onclick', 'redirectToLogin();')
|
||||||
|
->setDecorators(array('ViewHelper'));
|
||||||
|
$this->addElement($cancel);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -82,17 +82,9 @@ class Application_Model_MusicDir {
|
||||||
$show_instances = $con->query($sql)->fetchAll();
|
$show_instances = $con->query($sql)->fetchAll();
|
||||||
|
|
||||||
// get all the files on this dir
|
// get all the files on this dir
|
||||||
$sql = "SELECT f.id FROM cc_music_dirs as md "
|
$sql = "UPDATE cc_files SET file_exists = 'f' WHERE id IN (SELECT f.id FROM cc_music_dirs as md "
|
||||||
." LEFT JOIN cc_files as f on f.directory = md.id WHERE md.id = $music_dir_id";
|
." LEFT JOIN cc_files as f on f.directory = md.id WHERE md.id = $music_dir_id)";
|
||||||
$files = $con->query($sql)->fetchAll();
|
$affected = $con->exec($sql);
|
||||||
|
|
||||||
// set file_exist flag to false
|
|
||||||
foreach ($files as $file_row) {
|
|
||||||
$temp_file = Application_Model_StoredFile::Recall($file_row['id']);
|
|
||||||
if($temp_file != null){
|
|
||||||
$temp_file->setFileExistsFlag(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set RemovedFlag to true
|
// set RemovedFlag to true
|
||||||
if ($userAddedWatchedDir) {
|
if ($userAddedWatchedDir) {
|
||||||
|
@ -399,14 +391,16 @@ class Application_Model_MusicDir {
|
||||||
* otherwise, it will set "Exists" flag to true
|
* otherwise, it will set "Exists" flag to true
|
||||||
**/
|
**/
|
||||||
public static function removeWatchedDir($p_dir, $userAddedWatchedDir=true){
|
public static function removeWatchedDir($p_dir, $userAddedWatchedDir=true){
|
||||||
|
|
||||||
|
//make sure that $p_dir has a trailing "/"
|
||||||
$real_path = Application_Common_OsPath::normpath($p_dir)."/";
|
$real_path = Application_Common_OsPath::normpath($p_dir)."/";
|
||||||
if($real_path != "/"){
|
if($real_path != "/"){
|
||||||
$p_dir = $real_path;
|
$p_dir = $real_path;
|
||||||
}
|
}
|
||||||
$dir = Application_Model_MusicDir::getDirByPath($p_dir);
|
$dir = Application_Model_MusicDir::getDirByPath($p_dir);
|
||||||
if($dir == NULL){
|
if (is_null($dir)) {
|
||||||
return array("code"=>1,"error"=>"'$p_dir' doesn't exist in the watched list.");
|
return array("code"=>1, "error"=>"'$p_dir' doesn't exist in the watched list.");
|
||||||
}else{
|
} else {
|
||||||
$dir->remove($userAddedWatchedDir);
|
$dir->remove($userAddedWatchedDir);
|
||||||
$data = array();
|
$data = array();
|
||||||
$data["directory"] = $p_dir;
|
$data["directory"] = $p_dir;
|
||||||
|
|
|
@ -618,7 +618,7 @@ class Application_Model_Preference
|
||||||
if (defined('APPLICATION_ENV') && APPLICATION_ENV == "development" && function_exists('exec')){
|
if (defined('APPLICATION_ENV') && APPLICATION_ENV == "development" && function_exists('exec')){
|
||||||
$version = exec("git rev-parse --short HEAD 2>/dev/null", $out, $return_code);
|
$version = exec("git rev-parse --short HEAD 2>/dev/null", $out, $return_code);
|
||||||
if ($return_code == 0){
|
if ($return_code == 0){
|
||||||
return self::GetValue("system_version")."+".$version;
|
return self::GetValue("system_version")."+".$version.":".time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self::GetValue("system_version");
|
return self::GetValue("system_version");
|
||||||
|
|
|
@ -548,10 +548,11 @@ class Application_Model_Schedule {
|
||||||
$data["media"][$kick_start]['end'] = $kick_start;
|
$data["media"][$kick_start]['end'] = $kick_start;
|
||||||
$data["media"][$kick_start]['event_type'] = "kick_out";
|
$data["media"][$kick_start]['event_type'] = "kick_out";
|
||||||
$data["media"][$kick_start]['type'] = "event";
|
$data["media"][$kick_start]['type'] = "event";
|
||||||
|
|
||||||
if($kick_time !== $switch_off_time){
|
if($kick_time !== $switch_off_time){
|
||||||
$data["media"][$switch_start]['start'] = Application_Model_Schedule::AirtimeTimeToPypoTime($switch_off_time);
|
$switch_start = Application_Model_Schedule::AirtimeTimeToPypoTime($switch_off_time);
|
||||||
$data["media"][$switch_start]['end'] = Application_Model_Schedule::AirtimeTimeToPypoTime($switch_off_time);
|
$data["media"][$switch_start]['start'] = $switch_start;
|
||||||
|
$data["media"][$switch_start]['end'] = $switch_start;
|
||||||
$data["media"][$switch_start]['event_type'] = "switch_off";
|
$data["media"][$switch_start]['event_type'] = "switch_off";
|
||||||
$data["media"][$switch_start]['type'] = "event";
|
$data["media"][$switch_start]['type'] = "event";
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,7 @@ class Application_Model_ShowBuilder {
|
||||||
$row["title"] = $p_item["show_name"];
|
$row["title"] = $p_item["show_name"];
|
||||||
$row["instance"] = intval($p_item["si_id"]);
|
$row["instance"] = intval($p_item["si_id"]);
|
||||||
$row["image"] = '';
|
$row["image"] = '';
|
||||||
|
$row["id"] = -1;
|
||||||
|
|
||||||
$this->getScheduledStatus($startsEpoch, $endsEpoch, $row);
|
$this->getScheduledStatus($startsEpoch, $endsEpoch, $row);
|
||||||
|
|
||||||
|
@ -310,6 +311,7 @@ class Application_Model_ShowBuilder {
|
||||||
$row = $this->defaultRowArray;
|
$row = $this->defaultRowArray;
|
||||||
$row["footer"] = true;
|
$row["footer"] = true;
|
||||||
$row["instance"] = intval($p_item["si_id"]);
|
$row["instance"] = intval($p_item["si_id"]);
|
||||||
|
$row["id"] = -1;
|
||||||
$this->getRowTimestamp($p_item, $row);
|
$this->getRowTimestamp($p_item, $row);
|
||||||
|
|
||||||
$showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
|
$showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
|
||||||
|
|
|
@ -327,7 +327,10 @@ class Application_Model_StoredFile {
|
||||||
throw new DeleteScheduledFileException();
|
throw new DeleteScheduledFileException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists($filepath)) {
|
$music_dir = Application_Model_MusicDir::getDirByPK($this->_file->getDbDirectory());
|
||||||
|
$type = $music_dir->getType();
|
||||||
|
|
||||||
|
if (file_exists($filepath) && $type == "stor") {
|
||||||
$data = array("filepath" => $filepath, "delete" => 1);
|
$data = array("filepath" => $filepath, "delete" => 1);
|
||||||
Application_Model_RabbitMq::SendMessageToMediaMonitor("file_delete", $data);
|
Application_Model_RabbitMq::SendMessageToMediaMonitor("file_delete", $data);
|
||||||
}
|
}
|
||||||
|
@ -889,10 +892,9 @@ Logging::log("getting media! - 2");
|
||||||
$command = sprintf("/usr/bin/airtime-liquidsoap -c 'output.dummy(audio_to_stereo(single(\"%s\")))' 2>&1", $audio_file);
|
$command = sprintf("/usr/bin/airtime-liquidsoap -c 'output.dummy(audio_to_stereo(single(\"%s\")))' 2>&1", $audio_file);
|
||||||
|
|
||||||
exec($command, $output, $rv);
|
exec($command, $output, $rv);
|
||||||
if ($rv != 0 || $output[0] == 'TagLib: MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.') {
|
if ($rv != 0 || (!empty($output) && $output[0] == 'TagLib: MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.')) {
|
||||||
$result = array("code" => 110, "message" => "This file appears to be corrupted and will not be added to media library.");
|
$result = array("code" => 110, "message" => "This file appears to be corrupted and will not be added to media library.");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
//Martin K.: changed to rename: Much less load + quicker since this is an atomic operation
|
//Martin K.: changed to rename: Much less load + quicker since this is an atomic operation
|
||||||
$r = @rename($audio_file, $audio_stor);
|
$r = @rename($audio_file, $audio_stor);
|
||||||
|
|
||||||
|
|
|
@ -21,5 +21,8 @@
|
||||||
<dd id="submit-element">
|
<dd id="submit-element">
|
||||||
<?php echo $this->element->getElement('submit') ?>
|
<?php echo $this->element->getElement('submit') ?>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dd id="cancel-element">
|
||||||
|
<?php echo $this->element->getElement('cancel') ?>
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</form>
|
</form>
|
|
@ -69,16 +69,19 @@
|
||||||
<?php echo $this->form->getSubform('live_stream_subform'); ?>
|
<?php echo $this->form->getSubform('live_stream_subform'); ?>
|
||||||
</div>
|
</div>
|
||||||
<div style="float: left; width: 600px;">
|
<div style="float: left; width: 600px;">
|
||||||
|
<fieldset class="padded">
|
||||||
|
<legend>Output Stream Settings</legend>
|
||||||
<?php
|
<?php
|
||||||
for($i=1;$i<=$this->num_stream;$i++){
|
for($i=1;$i<=$this->num_stream;$i++){
|
||||||
echo $this->form->getSubform("s".$i."_subform");
|
echo $this->form->getSubform("s".$i."_subform");
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
</fieldset>
|
||||||
<?php if($this->enable_stream_conf == "true"){?>
|
<?php if($this->enable_stream_conf == "true"){?>
|
||||||
<div class="button-bar bottom" id="submit-element">
|
<div class="button-bar bottom" id="submit-element">
|
||||||
<input type="submit" class="ui-button ui-state-default right-floated" value="Save" id="Save" name="Save" />
|
<input type="submit" class="ui-button ui-state-default right-floated" value="Save" id="Save" name="Save" />
|
||||||
</div>
|
</div>
|
||||||
<?php }?>
|
<?php }?>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<?php echo $this->when; ?>
|
<?php echo $this->when; ?>
|
||||||
<?php echo $this->repeats; ?>
|
<?php echo $this->repeats; ?>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="collapsible-header"><span class="arrow-icon"></span>Live Stream</h3>
|
<h3 class="collapsible-header"><span class="arrow-icon"></span>Live Stream Input</h3>
|
||||||
<div id="live-stream-override" class="collapsible-content">
|
<div id="live-stream-override" class="collapsible-content">
|
||||||
<?php echo $this->live; ?>
|
<?php echo $this->live; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -808,6 +808,12 @@ dt.block-display, dd.block-display {
|
||||||
font-size:14px;
|
font-size:14px;
|
||||||
padding: 6px 10px 6px;
|
padding: 6px 10px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-content dd button.ui-button, .login-content dd button.btn {
|
||||||
|
width:100%;
|
||||||
|
font-size:14px;
|
||||||
|
padding: 6px 10px 6px;
|
||||||
|
}
|
||||||
.login-content .hidden, .hidden {
|
.login-content .hidden, .hidden {
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,13 +202,19 @@ var AIRTIME = (function(AIRTIME) {
|
||||||
*/
|
*/
|
||||||
mod.selectCurrentPage = function() {
|
mod.selectCurrentPage = function() {
|
||||||
$.fn.reverse = [].reverse;
|
$.fn.reverse = [].reverse;
|
||||||
var $trs = $libTable.find("tbody input:checkbox").parents("tr").reverse();
|
var $inputs = $libTable.find("tbody input:checkbox"),
|
||||||
|
$trs = $inputs.parents("tr").reverse();
|
||||||
|
|
||||||
|
$inputs.attr("checked", true);
|
||||||
|
$trs.addClass(LIB_SELECTED_CLASS);
|
||||||
|
|
||||||
$trs.each(function(i, el){
|
$trs.each(function(i, el){
|
||||||
$el = $(this);
|
$el = $(this);
|
||||||
|
mod.addToChosen($el);
|
||||||
mod.selectItem($el);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mod.checkToolBarIcons();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -216,14 +222,20 @@ var AIRTIME = (function(AIRTIME) {
|
||||||
* (behaviour taken from gmail)
|
* (behaviour taken from gmail)
|
||||||
*/
|
*/
|
||||||
mod.deselectCurrentPage = function() {
|
mod.deselectCurrentPage = function() {
|
||||||
|
var $inputs = $libTable.find("tbody input:checkbox"),
|
||||||
|
$trs = $inputs.parents("tr"),
|
||||||
|
id;
|
||||||
|
|
||||||
var $trs = $libTable.find("tbody input:checkbox").filter(":checked").parents("tr");
|
$inputs.attr("checked", false);
|
||||||
|
$trs.removeClass(LIB_SELECTED_CLASS);
|
||||||
|
|
||||||
$trs.each(function(i, el){
|
$trs.each(function(i, el){
|
||||||
$el = $(this);
|
$el = $(this);
|
||||||
|
id = $el.attr("id");
|
||||||
mod.deselectItem($el);
|
delete chosenItems[id];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mod.checkToolBarIcons();
|
||||||
};
|
};
|
||||||
|
|
||||||
mod.selectNone = function() {
|
mod.selectNone = function() {
|
||||||
|
@ -252,20 +264,22 @@ var AIRTIME = (function(AIRTIME) {
|
||||||
};
|
};
|
||||||
|
|
||||||
mod.fnDeleteSelectedItems = function() {
|
mod.fnDeleteSelectedItems = function() {
|
||||||
var aData = AIRTIME.library.getSelectedData(),
|
if (confirm('Are you sure you want to delete the selected item(s)?')) {
|
||||||
item,
|
var aData = AIRTIME.library.getSelectedData(),
|
||||||
temp,
|
item,
|
||||||
aMedia = [];
|
temp,
|
||||||
|
aMedia = [];
|
||||||
//process selected files/playlists.
|
|
||||||
for (item in aData) {
|
//process selected files/playlists.
|
||||||
temp = aData[item];
|
for (item in aData) {
|
||||||
if (temp !== null && temp.hasOwnProperty('id') ) {
|
temp = aData[item];
|
||||||
aMedia.push({"id": temp.id, "type": temp.ftype});
|
if (temp !== null && temp.hasOwnProperty('id') ) {
|
||||||
}
|
aMedia.push({"id": temp.id, "type": temp.ftype});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
AIRTIME.library.fnDeleteItems(aMedia);
|
|
||||||
|
AIRTIME.library.fnDeleteItems(aMedia);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
libraryInit = function() {
|
libraryInit = function() {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
function redirectToLogin(){
|
||||||
|
window.location = "/Login"
|
||||||
|
}
|
|
@ -1,3 +1,32 @@
|
||||||
|
function getFileName(ext){
|
||||||
|
var filename = $("#his_date_start").val()+"_"+$("#his_time_start").val()+"m--"+$("#his_date_end").val()+"_"+$("#his_time_end").val()+"m"
|
||||||
|
filename = filename.replace(/:/g,"h")
|
||||||
|
if(ext == "pdf"){
|
||||||
|
filename = filename+".pdf"
|
||||||
|
}else{
|
||||||
|
filename = filename+".csv"
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFlashFileName( nButton, oConfig, oFlash ) {
|
||||||
|
var filename = getFileName(oConfig.sExtends)
|
||||||
|
oFlash.setFileName( filename );
|
||||||
|
if(oConfig.sExtends == "pdf"){
|
||||||
|
this.fnSetText( oFlash,
|
||||||
|
"title:"+ this.fnGetTitle(oConfig) +"\n"+
|
||||||
|
"message:"+ oConfig.sPdfMessage +"\n"+
|
||||||
|
"colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+
|
||||||
|
"orientation:"+ oConfig.sPdfOrientation +"\n"+
|
||||||
|
"size:"+ oConfig.sPdfSize +"\n"+
|
||||||
|
"--/TableToolsOpts--\n" +
|
||||||
|
this.fnGetTableData(oConfig));
|
||||||
|
}else{
|
||||||
|
this.fnSetText( oFlash,
|
||||||
|
this.fnGetTableData(oConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var AIRTIME = (function(AIRTIME) {
|
var AIRTIME = (function(AIRTIME) {
|
||||||
var mod;
|
var mod;
|
||||||
|
|
||||||
|
@ -37,7 +66,7 @@ var AIRTIME = (function(AIRTIME) {
|
||||||
|
|
||||||
"aoColumns": [
|
"aoColumns": [
|
||||||
{"sTitle": "Title", "mDataProp": "title", "sClass": "his_title"}, /* Title */
|
{"sTitle": "Title", "mDataProp": "title", "sClass": "his_title"}, /* Title */
|
||||||
{"sTitle": "Artist", "mDataProp": "artist", "sClass": "his_artist"}, /* Creator */
|
{"sTitle": "Creator", "mDataProp": "artist", "sClass": "his_artist"}, /* Creator */
|
||||||
{"sTitle": "Played", "mDataProp": "played", "sClass": "his_artist"}, /* times played */
|
{"sTitle": "Played", "mDataProp": "played", "sClass": "his_artist"}, /* times played */
|
||||||
{"sTitle": "Length", "mDataProp": "length", "sClass": "his_length library_length"}, /* Length */
|
{"sTitle": "Length", "mDataProp": "length", "sClass": "his_length library_length"}, /* Length */
|
||||||
{"sTitle": "Composer", "mDataProp": "composer", "sClass": "his_composer"}, /* Composer */
|
{"sTitle": "Composer", "mDataProp": "composer", "sClass": "his_composer"}, /* Composer */
|
||||||
|
@ -65,7 +94,19 @@ var AIRTIME = (function(AIRTIME) {
|
||||||
"sDom": 'lf<"dt-process-rel"r><"H"T><"dataTables_scrolling"t><"F"ip>',
|
"sDom": 'lf<"dt-process-rel"r><"H"T><"dataTables_scrolling"t><"F"ip>',
|
||||||
|
|
||||||
"oTableTools": {
|
"oTableTools": {
|
||||||
"sSwfPath": "/js/datatables/plugin/TableTools/swf/copy_cvs_xls_pdf.swf"
|
"sSwfPath": "/js/datatables/plugin/TableTools/swf/copy_cvs_xls_pdf.swf",
|
||||||
|
"aButtons": [
|
||||||
|
"copy",
|
||||||
|
{
|
||||||
|
"sExtends": "csv",
|
||||||
|
"fnClick": setFlashFileName
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sExtends": "pdf",
|
||||||
|
"fnClick": setFlashFileName
|
||||||
|
},
|
||||||
|
"print"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
oTable.fnSetFilteringDelay(350);
|
oTable.fnSetFilteringDelay(350);
|
||||||
|
|
|
@ -91,6 +91,34 @@ function onEndTimeSelect(){
|
||||||
$("#add_show_end_time").trigger('input');
|
$("#add_show_end_time").trigger('input');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function padZeroes(number, length)
|
||||||
|
{
|
||||||
|
var str = '' + number;
|
||||||
|
while (str.length < length) {str = '0' + str;}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hashCode(str) { // java String#hashCode
|
||||||
|
var hash = 0;
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
function intToRGB(i){
|
||||||
|
return (padZeroes(((i>>16)&0xFF).toString(16), 2) +
|
||||||
|
padZeroes(((i>>8)&0xFF).toString(16), 2)+
|
||||||
|
padZeroes((i&0xFF).toString(16), 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToColor(s)
|
||||||
|
{
|
||||||
|
return intToRGB(hashCode(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function setAddShowEvents() {
|
function setAddShowEvents() {
|
||||||
|
|
||||||
var form = $("#add-show-form");
|
var form = $("#add-show-form");
|
||||||
|
@ -548,6 +576,12 @@ function setAddShowEvents() {
|
||||||
loadingIcon.hide();
|
loadingIcon.hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bgColorEle = $("#add_show_background_color");
|
||||||
|
$('#add_show_name').bind('input', 'change', function(){
|
||||||
|
var colorCode = stringToColor($(this).val());
|
||||||
|
bgColorEle.val(colorCode);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showErrorSections() {
|
function showErrorSections() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Full Calendar callback methods.
|
* Full Calendar callback methods.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -9,12 +9,12 @@ function scheduleRefetchEvents(json) {
|
||||||
alert("The show instance doesn't exist anymore!");
|
alert("The show instance doesn't exist anymore!");
|
||||||
}
|
}
|
||||||
if(json.show_id) {
|
if(json.show_id) {
|
||||||
var dialog_id = parseInt($("#add_show_id").val(), 10);
|
var dialog_id = parseInt($("#add_show_id").val(), 10);
|
||||||
|
|
||||||
//if you've deleted the show you are currently editing, close the add show dialog.
|
//if you've deleted the show you are currently editing, close the add show dialog.
|
||||||
if (dialog_id === json.show_id) {
|
if (dialog_id === json.show_id) {
|
||||||
$("#add-show-close").click();
|
$("#add-show-close").click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$("#schedule_calendar").fullCalendar( 'refetchEvents' );
|
$("#schedule_calendar").fullCalendar( 'refetchEvents' );
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,9 @@ function openAddShowForm() {
|
||||||
$("#schedule_calendar").fullCalendar('render');
|
$("#schedule_calendar").fullCalendar('render');
|
||||||
}
|
}
|
||||||
$("#schedule-show-what").show(0, function(){
|
$("#schedule-show-what").show(0, function(){
|
||||||
$add_show_name = $("#add_show_name");
|
$add_show_name = $("#add_show_name");
|
||||||
$add_show_name.focus();
|
$add_show_name.focus();
|
||||||
$add_show_name.select();
|
$add_show_name.select();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,16 +59,16 @@ function removeAddShowButton(){
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTimeStamp(date){
|
function makeTimeStamp(date){
|
||||||
var sy, sm, sd, h, m, s, timestamp;
|
var sy, sm, sd, h, m, s, timestamp;
|
||||||
sy = date.getFullYear();
|
sy = date.getFullYear();
|
||||||
sm = date.getMonth() + 1;
|
sm = date.getMonth() + 1;
|
||||||
sd = date.getDate();
|
sd = date.getDate();
|
||||||
h = date.getHours();
|
h = date.getHours();
|
||||||
m = date.getMinutes();
|
m = date.getMinutes();
|
||||||
s = date.getSeconds();
|
s = date.getSeconds();
|
||||||
|
|
||||||
timestamp = sy+"-"+ pad(sm, 2) +"-"+ pad(sd, 2) +" "+ pad(h, 2) +":"+ pad(m, 2) +":"+ pad(s, 2);
|
timestamp = sy+"-"+ pad(sm, 2) +"-"+ pad(sd, 2) +" "+ pad(h, 2) +":"+ pad(m, 2) +":"+ pad(s, 2);
|
||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dayClick(date, allDay, jsEvent, view){
|
function dayClick(date, allDay, jsEvent, view){
|
||||||
|
@ -76,9 +76,9 @@ function dayClick(date, allDay, jsEvent, view){
|
||||||
// Hence, if the user if DJ then it won't open anything.
|
// Hence, if the user if DJ then it won't open anything.
|
||||||
if(userType == "A" || userType == "P"){
|
if(userType == "A" || userType == "P"){
|
||||||
var now, today, selected, chosenDate, chosenTime;
|
var now, today, selected, chosenDate, chosenTime;
|
||||||
|
|
||||||
now = adjustDateToServerDate(new Date(), serverTimezoneOffset);
|
now = adjustDateToServerDate(new Date(), serverTimezoneOffset);
|
||||||
|
|
||||||
if(view.name === "month") {
|
if(view.name === "month") {
|
||||||
today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||||
selected = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
selected = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||||
|
@ -87,17 +87,17 @@ function dayClick(date, allDay, jsEvent, view){
|
||||||
today = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes());
|
today = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes());
|
||||||
selected = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes());
|
selected = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(selected >= today) {
|
if(selected >= today) {
|
||||||
var addShow = $('.add-button');
|
var addShow = $('.add-button');
|
||||||
|
|
||||||
//remove the +show button if it exists.
|
//remove the +show button if it exists.
|
||||||
if(addShow.length == 1){
|
if(addShow.length == 1){
|
||||||
var span = $(addShow).parent();
|
var span = $(addShow).parent();
|
||||||
$(span).prev().remove();
|
$(span).prev().remove();
|
||||||
$(span).remove();
|
$(span).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// get current duration value on the form
|
// get current duration value on the form
|
||||||
var duration_string = $.trim($("#add_show_duration").val());
|
var duration_string = $.trim($("#add_show_duration").val());
|
||||||
var duration_info = duration_string.split(" ");
|
var duration_info = duration_string.split(" ");
|
||||||
|
@ -111,7 +111,7 @@ function dayClick(date, allDay, jsEvent, view){
|
||||||
}
|
}
|
||||||
// duration in milisec
|
// duration in milisec
|
||||||
var duration = (duration_h * 60 * 60 * 1000) + (duration_m * 60 * 1000);
|
var duration = (duration_h * 60 * 60 * 1000) + (duration_m * 60 * 1000);
|
||||||
|
|
||||||
var startTime_string, startTime
|
var startTime_string, startTime
|
||||||
// get start time value on the form
|
// get start time value on the form
|
||||||
if(view.name === "month") {
|
if(view.name === "month") {
|
||||||
|
@ -124,13 +124,13 @@ function dayClick(date, allDay, jsEvent, view){
|
||||||
startTime_string = pad(selected.getHours(),2)+":"+pad(selected.getMinutes(),2)
|
startTime_string = pad(selected.getHours(),2)+":"+pad(selected.getMinutes(),2)
|
||||||
startTime = 0
|
startTime = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate endDateTime
|
// calculate endDateTime
|
||||||
var endDateTime = new Date(selected.getTime() + startTime + duration);
|
var endDateTime = new Date(selected.getTime() + startTime + duration);
|
||||||
|
|
||||||
chosenDate = selected.getFullYear() + '-' + pad(selected.getMonth()+1,2) + '-' + pad(selected.getDate(),2);
|
chosenDate = selected.getFullYear() + '-' + pad(selected.getMonth()+1,2) + '-' + pad(selected.getDate(),2);
|
||||||
var endDateFormat = endDateTime.getFullYear() + '-' + pad(endDateTime.getMonth()+1,2) + '-' + pad(endDateTime.getDate(),2);
|
var endDateFormat = endDateTime.getFullYear() + '-' + pad(endDateTime.getMonth()+1,2) + '-' + pad(endDateTime.getDate(),2);
|
||||||
|
|
||||||
$("#add_show_start_date").val(chosenDate);
|
$("#add_show_start_date").val(chosenDate);
|
||||||
$("#add_show_end_date_no_repeat").val(endDateFormat);
|
$("#add_show_end_date_no_repeat").val(endDateFormat);
|
||||||
$("#add_show_end_date").val(endDateFormat);
|
$("#add_show_end_date").val(endDateFormat);
|
||||||
|
@ -140,7 +140,7 @@ function dayClick(date, allDay, jsEvent, view){
|
||||||
$("#add_show_end_time").val(endTimeString)
|
$("#add_show_end_time").val(endTimeString)
|
||||||
}
|
}
|
||||||
$("#schedule-show-when").show();
|
$("#schedule-show-when").show();
|
||||||
|
|
||||||
openAddShowForm();
|
openAddShowForm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,10 +173,10 @@ function viewDisplay( view ) {
|
||||||
.fullCalendar('destroy')
|
.fullCalendar('destroy')
|
||||||
.fullCalendar(opt)
|
.fullCalendar(opt)
|
||||||
.fullCalendar( 'gotoDate', date );
|
.fullCalendar( 'gotoDate', date );
|
||||||
|
|
||||||
//save slotMin value to db
|
//save slotMin value to db
|
||||||
var url = '/Schedule/set-time-interval/format/json';
|
var url = '/Schedule/set-time-interval/format/json';
|
||||||
$.post(url, {timeInterval: slotMin});
|
$.post(url, {timeInterval: slotMin});
|
||||||
});
|
});
|
||||||
|
|
||||||
var topLeft = $(view.element).find("table.fc-agenda-days > thead th:first");
|
var topLeft = $(view.element).find("table.fc-agenda-days > thead th:first");
|
||||||
|
@ -192,146 +192,128 @@ function viewDisplay( view ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(($("#add-show-form").length == 1) && ($("#add-show-form").css('display')=='none') && ($('.fc-header-left > span').length == 5)) {
|
if(($("#add-show-form").length == 1) && ($("#add-show-form").css('display')=='none') && ($('.fc-header-left > span').length == 5)) {
|
||||||
|
|
||||||
//userType is defined in bootstrap.php, and is derived from the currently logged in user.
|
//userType is defined in bootstrap.php, and is derived from the currently logged in user.
|
||||||
if(userType == "A" || userType == "P"){
|
if(userType == "A" || userType == "P"){
|
||||||
makeAddShowButton();
|
makeAddShowButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//save view name to db
|
//save view name to db
|
||||||
var url = '/Schedule/set-time-scale/format/json';
|
var url = '/Schedule/set-time-scale/format/json';
|
||||||
$.post(url, {timeScale: view.name});
|
$.post(url, {timeScale: view.name});
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventRender(event, element, view) {
|
function eventRender(event, element, view) {
|
||||||
|
|
||||||
$(element).data("event", event);
|
$(element).data("event", event);
|
||||||
|
|
||||||
//only put progress bar on shows that aren't being recorded.
|
//only put progress bar on shows that aren't being recorded.
|
||||||
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 0) {
|
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 0) {
|
||||||
var div = $('<div/>');
|
var div = $('<div/>');
|
||||||
div
|
div
|
||||||
.height('5px')
|
.height('5px')
|
||||||
.width('95%')
|
.width('95%')
|
||||||
.css('margin-top', '1px')
|
.css('margin-top', '1px')
|
||||||
.css('margin-left', 'auto')
|
.css('margin-left', 'auto')
|
||||||
.css('margin-right', 'auto')
|
.css('margin-right', 'auto')
|
||||||
.progressbar({
|
.progressbar({
|
||||||
value: event.percent
|
value: event.percent
|
||||||
});
|
});
|
||||||
|
|
||||||
$(element).find(".fc-event-content").append(div);
|
$(element).find(".fc-event-content").append(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
//add the record/rebroadcast icons if needed.
|
//add the record/rebroadcast/soundcloud icons if needed
|
||||||
//record icon (only if not on soundcloud, will always be true for future events)
|
|
||||||
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 1 && event.soundcloud_id === -1) {
|
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 1 && event.soundcloud_id === -1) {
|
||||||
|
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon recording"></span>');
|
||||||
|
} else if ((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 1 && event.soundcloud_id > 0) {
|
||||||
|
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon recording"></span><span id="'+event.id+'" class="small-icon soundcloud"></span>');
|
||||||
|
} else if ((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 1 && event.soundcloud_id === -2) {
|
||||||
|
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon recording"></span><span id="'+event.id+'" class="small-icon progress"></span>');
|
||||||
|
} else if ((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 1 && event.soundcloud_id === -3) {
|
||||||
|
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon recording"></span><span id="'+event.id+'" class="small-icon sc-error"></span>');
|
||||||
|
}
|
||||||
|
|
||||||
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon recording"></span>');
|
|
||||||
}
|
|
||||||
if(view.name === 'month' && event.record === 1 && event.soundcloud_id === -1) {
|
if(view.name === 'month' && event.record === 1 && event.soundcloud_id === -1) {
|
||||||
|
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon recording"></span>');
|
||||||
|
} else if (view.name === 'month' && event.record === 1 && event.soundcloud_id > 0) {
|
||||||
|
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon recording"></span><span id="'+event.id+'" class="small-icon soundcloud"></span>');
|
||||||
|
} else if (view.name === 'month' && event.record === 1 && event.soundcloud_id === -2) {
|
||||||
|
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon recording"></span><span id="'+event.id+'" class="small-icon progress"></span>');
|
||||||
|
} else if (view.name === 'month' && event.record === 1 && event.soundcloud_id === -3) {
|
||||||
|
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon recording"></span><span id="'+event.id+'" class="small-icon sc-error"></span>');
|
||||||
|
}
|
||||||
|
|
||||||
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon recording"></span>');
|
|
||||||
}
|
|
||||||
//rebroadcast icon
|
//rebroadcast icon
|
||||||
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.rebroadcast === 1) {
|
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.rebroadcast === 1) {
|
||||||
|
|
||||||
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon rebroadcast"></span>');
|
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon rebroadcast"></span>');
|
||||||
}
|
}
|
||||||
if(view.name === 'month' && event.rebroadcast === 1) {
|
if(view.name === 'month' && event.rebroadcast === 1) {
|
||||||
|
|
||||||
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon rebroadcast"></span>');
|
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon rebroadcast"></span>');
|
||||||
}
|
|
||||||
//soundcloud icon
|
|
||||||
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.soundcloud_id > 0 && event.record === 1) {
|
|
||||||
|
|
||||||
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon soundcloud"></span>');
|
|
||||||
}
|
|
||||||
if(view.name === 'month' && event.soundcloud_id > 0 && event.record === 1) {
|
|
||||||
|
|
||||||
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon soundcloud"></span>');
|
|
||||||
}
|
|
||||||
|
|
||||||
//progress icon
|
|
||||||
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.soundcloud_id === -2 && event.record === 1) {
|
|
||||||
|
|
||||||
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon progress"></span>');
|
|
||||||
}
|
|
||||||
if(view.name === 'month' && event.soundcloud_id === -2 && event.record === 1) {
|
|
||||||
|
|
||||||
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon progress"></span>');
|
|
||||||
}
|
|
||||||
|
|
||||||
//error icon
|
|
||||||
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.soundcloud_id === -3 && event.record === 1) {
|
|
||||||
|
|
||||||
$(element).find(".fc-event-time").before('<span id="'+event.id+'" class="small-icon sc-error"></span>');
|
|
||||||
}
|
|
||||||
if(view.name === 'month' && event.soundcloud_id === -3 && event.record === 1) {
|
|
||||||
|
|
||||||
$(element).find(".fc-event-title").after('<span id="'+event.id+'" class="small-icon sc-error"></span>');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventAfterRender( event, element, view ) {
|
function eventAfterRender( event, element, view ) {
|
||||||
|
|
||||||
$(element).find(".small-icon").live('mouseover',function(){
|
$(element).find(".small-icon").live('mouseover',function(){
|
||||||
addQtipToSCIcons($(this));
|
addQtipToSCIcons($(this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventDrop(event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
|
function eventDrop(event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
|
||||||
var url;
|
var url;
|
||||||
|
|
||||||
url = '/Schedule/move-show/format/json';
|
url = '/Schedule/move-show/format/json';
|
||||||
|
|
||||||
$.post(url,
|
$.post(url,
|
||||||
{day: dayDelta, min: minuteDelta, showInstanceId: event.id},
|
{day: dayDelta, min: minuteDelta, showInstanceId: event.id},
|
||||||
function(json){
|
function(json){
|
||||||
if(json.show_error == true){
|
if(json.show_error == true){
|
||||||
alertShowErrorAndReload();
|
alertShowErrorAndReload();
|
||||||
}
|
}
|
||||||
if(json.error) {
|
if(json.error) {
|
||||||
alert(json.error);
|
alert(json.error);
|
||||||
revertFunc();
|
revertFunc();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventResize( event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view ) {
|
function eventResize( event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view ) {
|
||||||
var url;
|
var url;
|
||||||
|
|
||||||
url = '/Schedule/resize-show/format/json';
|
url = '/Schedule/resize-show/format/json';
|
||||||
|
|
||||||
$.post(url,
|
$.post(url,
|
||||||
{day: dayDelta, min: minuteDelta, showId: event.showId},
|
{day: dayDelta, min: minuteDelta, showId: event.showId},
|
||||||
function(json){
|
function(json){
|
||||||
if(json.show_error == true){
|
if(json.show_error == true){
|
||||||
alertShowErrorAndReload();
|
alertShowErrorAndReload();
|
||||||
}
|
}
|
||||||
if(json.error) {
|
if(json.error) {
|
||||||
alert(json.error);
|
alert(json.error);
|
||||||
revertFunc();
|
revertFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleRefetchEvents(json);
|
scheduleRefetchEvents(json);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFullCalendarEvents(start, end, callback) {
|
function getFullCalendarEvents(start, end, callback) {
|
||||||
var url, start_date, end_date;
|
var url, start_date, end_date;
|
||||||
|
|
||||||
start_date = makeTimeStamp(start);
|
start_date = makeTimeStamp(start);
|
||||||
end_date = makeTimeStamp(end);
|
end_date = makeTimeStamp(end);
|
||||||
|
|
||||||
url = '/Schedule/event-feed';
|
url = '/Schedule/event-feed';
|
||||||
|
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
|
|
||||||
$.post(url, {format: "json", start: start_date, end: end_date, cachep: d.getTime()}, function(json){
|
$.post(url, {format: "json", start: start_date, end: end_date, cachep: d.getTime()}, function(json){
|
||||||
callback(json.events);
|
callback(json.events);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSCUploadStatus(){
|
function checkSCUploadStatus(){
|
||||||
|
@ -340,9 +322,9 @@ function checkSCUploadStatus(){
|
||||||
var id = $(this).attr("id");
|
var id = $(this).attr("id");
|
||||||
$.post(url, {format: "json", id: id, type:"show"}, function(json){
|
$.post(url, {format: "json", id: id, type:"show"}, function(json){
|
||||||
if(json.sc_id > 0){
|
if(json.sc_id > 0){
|
||||||
$("span[id="+id+"]").removeClass("progress").addClass("soundcloud");
|
$("span[id="+id+"]:not(.recording)").removeClass("progress").addClass("soundcloud");
|
||||||
}else if(json.sc_id == "-3"){
|
}else if(json.sc_id == "-3"){
|
||||||
$("span[id="+id+"]").removeClass("progress").addClass("sc-error");
|
$("span[id="+id+"]:not(.recording)").removeClass("progress").addClass("sc-error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
var AIRTIME = (function(AIRTIME){
|
var AIRTIME = (function(AIRTIME){
|
||||||
var mod;
|
var mod;
|
||||||
|
|
||||||
if (AIRTIME.schedule === undefined) {
|
if (AIRTIME.schedule === undefined) {
|
||||||
AIRTIME.schedule = {};
|
AIRTIME.schedule = {};
|
||||||
}
|
}
|
||||||
mod = AIRTIME.schedule;
|
mod = AIRTIME.schedule;
|
||||||
|
|
||||||
return AIRTIME;
|
return AIRTIME;
|
||||||
|
|
||||||
}(AIRTIME || {}));
|
}(AIRTIME || {}));
|
||||||
|
|
||||||
var serverTimezoneOffset = 0;
|
var serverTimezoneOffset = 0;
|
||||||
|
|
||||||
function closeDialog(event, ui) {
|
function closeDialog(event, ui) {
|
||||||
$("#schedule_calendar").fullCalendar( 'refetchEvents' );
|
$("#schedule_calendar").fullCalendar( 'refetchEvents' );
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkShowLength(json) {
|
function checkShowLength(json) {
|
||||||
|
@ -36,11 +36,11 @@ function confirmCancelShow(show_instance_id){
|
||||||
if (confirm('Cancel Current Show?')) {
|
if (confirm('Cancel Current Show?')) {
|
||||||
var url = "/Schedule/cancel-current-show";
|
var url = "/Schedule/cancel-current-show";
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
data: {format: "json", id: show_instance_id},
|
data: {format: "json", id: show_instance_id},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
scheduleRefetchEvents(data);
|
scheduleRefetchEvents(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,11 +49,11 @@ function confirmCancelRecordedShow(show_instance_id){
|
||||||
if (confirm('Stop recording current show?')) {
|
if (confirm('Stop recording current show?')) {
|
||||||
var url = "/Schedule/cancel-current-show";
|
var url = "/Schedule/cancel-current-show";
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
data: {format: "json", id: show_instance_id},
|
data: {format: "json", id: show_instance_id},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
scheduleRefetchEvents(data);
|
scheduleRefetchEvents(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,21 +72,21 @@ function uploadToSoundCloud(show_instance_id){
|
||||||
if(span.length == 0){
|
if(span.length == 0){
|
||||||
span = $(window.triggerElement).find(".soundcloud");
|
span = $(window.triggerElement).find(".soundcloud");
|
||||||
span.removeClass("soundcloud")
|
span.removeClass("soundcloud")
|
||||||
.addClass("progress");
|
.addClass("progress");
|
||||||
}else{
|
}else{
|
||||||
span.removeClass("recording")
|
span.removeClass("recording")
|
||||||
.addClass("progress");
|
.addClass("progress");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkCalendarSCUploadStatus(){
|
function checkCalendarSCUploadStatus(){
|
||||||
|
|
||||||
var url = '/Library/get-upload-to-soundcloud-status',
|
var url = '/Library/get-upload-to-soundcloud-status',
|
||||||
span,
|
span,
|
||||||
id;
|
id;
|
||||||
|
|
||||||
function checkSCUploadStatusCallback(json) {
|
function checkSCUploadStatusCallback(json) {
|
||||||
|
|
||||||
if (json.sc_id > 0) {
|
if (json.sc_id > 0) {
|
||||||
span.removeClass("progress").addClass("soundcloud");
|
span.removeClass("progress").addClass("soundcloud");
|
||||||
|
|
||||||
|
@ -108,113 +108,111 @@ function checkCalendarSCUploadStatus(){
|
||||||
}
|
}
|
||||||
|
|
||||||
function findViewportDimensions() {
|
function findViewportDimensions() {
|
||||||
var viewportwidth,
|
var viewportwidth,
|
||||||
viewportheight;
|
viewportheight;
|
||||||
|
|
||||||
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use
|
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use
|
||||||
// window.innerWidth and window.innerHeight
|
// window.innerWidth and window.innerHeight
|
||||||
if (typeof window.innerWidth != 'undefined') {
|
if (typeof window.innerWidth != 'undefined') {
|
||||||
viewportwidth = window.innerWidth, viewportheight = window.innerHeight;
|
viewportwidth = window.innerWidth, viewportheight = window.innerHeight;
|
||||||
}
|
}
|
||||||
// IE6 in standards compliant mode (i.e. with a valid doctype as the first
|
// IE6 in standards compliant mode (i.e. with a valid doctype as the first
|
||||||
// line in the document)
|
// line in the document)
|
||||||
else if (typeof document.documentElement != 'undefined'
|
else if (typeof document.documentElement != 'undefined'
|
||||||
&& typeof document.documentElement.clientWidth != 'undefined'
|
&& typeof document.documentElement.clientWidth != 'undefined'
|
||||||
&& document.documentElement.clientWidth != 0) {
|
&& document.documentElement.clientWidth != 0) {
|
||||||
viewportwidth = document.documentElement.clientWidth;
|
viewportwidth = document.documentElement.clientWidth;
|
||||||
viewportheight = document.documentElement.clientHeight;
|
viewportheight = document.documentElement.clientHeight;
|
||||||
}
|
}
|
||||||
// older versions of IE
|
// older versions of IE
|
||||||
else {
|
else {
|
||||||
viewportwidth = document.getElementsByTagName('body')[0].clientWidth;
|
viewportwidth = document.getElementsByTagName('body')[0].clientWidth;
|
||||||
viewportheight = document.getElementsByTagName('body')[0].clientHeight;
|
viewportheight = document.getElementsByTagName('body')[0].clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width: viewportwidth,
|
width: viewportwidth,
|
||||||
height: viewportheight
|
height: viewportheight
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildScheduleDialog (json) {
|
function buildScheduleDialog (json) {
|
||||||
|
var dialog = $(json.dialog),
|
||||||
var dialog = $(json.dialog),
|
viewport = findViewportDimensions(),
|
||||||
viewport = findViewportDimensions(),
|
height = Math.floor(viewport.height * 0.96),
|
||||||
height = Math.floor(viewport.height * 0.96),
|
width = Math.floor(viewport.width * 0.96),
|
||||||
width = Math.floor(viewport.width * 0.96),
|
fnServer = AIRTIME.showbuilder.fnServerData,
|
||||||
fnServer = AIRTIME.showbuilder.fnServerData,
|
//subtract padding in pixels
|
||||||
//subtract padding in pixels
|
widgetWidth = width - 60,
|
||||||
widgetWidth = width - 60,
|
libWidth = Math.floor(widgetWidth * 0.5),
|
||||||
libWidth = Math.floor(widgetWidth * 0.5),
|
builderWidth = Math.floor(widgetWidth * 0.5);
|
||||||
builderWidth = Math.floor(widgetWidth * 0.5);
|
|
||||||
|
dialog.find("#library_content")
|
||||||
dialog.find("#library_content")
|
.height(height - 115)
|
||||||
.height(height - 115)
|
.width(libWidth);
|
||||||
.width(libWidth);
|
|
||||||
|
dialog.find("#show_builder")
|
||||||
dialog.find("#show_builder")
|
.height(height - 115)
|
||||||
.height(height - 115)
|
.width(builderWidth);
|
||||||
.width(builderWidth);
|
|
||||||
|
dialog.dialog({
|
||||||
dialog.dialog({
|
autoOpen: false,
|
||||||
autoOpen: false,
|
title: json.title,
|
||||||
title: json.title,
|
width: width,
|
||||||
width: width,
|
height: height,
|
||||||
height: height,
|
resizable: false,
|
||||||
resizable: false,
|
draggable: true,
|
||||||
draggable: true,
|
modal: true,
|
||||||
modal: true,
|
close: closeDialog,
|
||||||
close: closeDialog,
|
buttons: {"Ok": function() {
|
||||||
buttons: {"Ok": function() {
|
$(this).dialog("close");
|
||||||
dialog.remove();
|
}}
|
||||||
$("#schedule_calendar").fullCalendar( 'refetchEvents' );
|
});
|
||||||
}}
|
|
||||||
});
|
//set the start end times so the builder datatables knows its time range.
|
||||||
|
fnServer.start = json.start;
|
||||||
//set the start end times so the builder datatables knows its time range.
|
fnServer.end = json.end;
|
||||||
fnServer.start = json.start;
|
|
||||||
fnServer.end = json.end;
|
AIRTIME.library.libraryInit();
|
||||||
|
AIRTIME.showbuilder.builderDataTable();
|
||||||
AIRTIME.library.libraryInit();
|
|
||||||
AIRTIME.showbuilder.builderDataTable();
|
//set max heights of datatables.
|
||||||
|
dialog.find(".lib-content .dataTables_scrolling")
|
||||||
//set max heights of datatables.
|
.css("max-height", height - 90 - 155);
|
||||||
dialog.find(".lib-content .dataTables_scrolling")
|
|
||||||
.css("max-height", height - 90 - 155);
|
dialog.find(".sb-content .dataTables_scrolling")
|
||||||
|
.css("max-height", height - 90 - 60);
|
||||||
dialog.find(".sb-content .dataTables_scrolling")
|
|
||||||
.css("max-height", height - 90 - 60);
|
dialog.dialog('open');
|
||||||
|
|
||||||
dialog.dialog('open');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildContentDialog (json){
|
function buildContentDialog (json){
|
||||||
var dialog = $(json.dialog),
|
var dialog = $(json.dialog),
|
||||||
viewport = findViewportDimensions(),
|
viewport = findViewportDimensions(),
|
||||||
height = viewport.height * 2/3,
|
height = viewport.height * 2/3,
|
||||||
width = viewport.width * 4/5;
|
width = viewport.width * 4/5;
|
||||||
|
|
||||||
if (json.show_error == true){
|
if (json.show_error == true){
|
||||||
alertShowErrorAndReload();
|
alertShowErrorAndReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.find("#show_progressbar").progressbar({
|
dialog.find("#show_progressbar").progressbar({
|
||||||
value: json.percentFilled
|
value: json.percentFilled
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.dialog({
|
dialog.dialog({
|
||||||
autoOpen: false,
|
autoOpen: false,
|
||||||
title: "Contents of Show \"" + json.showTitle + "\"",
|
title: "Contents of Show \"" + json.showTitle + "\"",
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
modal: true,
|
modal: true,
|
||||||
close: closeDialog,
|
close: closeDialog,
|
||||||
buttons: {"Ok": function() {
|
buttons: {"Ok": function() {
|
||||||
dialog.remove();
|
dialog.remove();
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.dialog('open');
|
dialog.dialog('open');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -279,55 +277,55 @@ function alertShowErrorAndReload(){
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$.ajax({ url: "/Api/calendar-init/format/json", dataType:"json", success:createFullCalendar
|
$.ajax({ url: "/Api/calendar-init/format/json", dataType:"json", success:createFullCalendar
|
||||||
, error:function(jqXHR, textStatus, errorThrown){}});
|
, error:function(jqXHR, textStatus, errorThrown){}});
|
||||||
|
|
||||||
setInterval(checkCalendarSCUploadStatus, 5000);
|
setInterval(checkCalendarSCUploadStatus, 5000);
|
||||||
|
|
||||||
$.contextMenu({
|
$.contextMenu({
|
||||||
selector: 'div.fc-event',
|
selector: 'div.fc-event',
|
||||||
trigger: "left",
|
trigger: "left",
|
||||||
ignoreRightClick: true,
|
ignoreRightClick: true,
|
||||||
|
|
||||||
build: function($el, e) {
|
build: function($el, e) {
|
||||||
var data,
|
var data,
|
||||||
items,
|
items,
|
||||||
callback;
|
callback;
|
||||||
|
|
||||||
data = $el.data("event");
|
data = $el.data("event");
|
||||||
|
|
||||||
function processMenuItems(oItems) {
|
function processMenuItems(oItems) {
|
||||||
|
|
||||||
//define a schedule callback.
|
//define a schedule callback.
|
||||||
if (oItems.schedule !== undefined) {
|
if (oItems.schedule !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
|
|
||||||
$.post(oItems.schedule.url, {format: "json", id: data.id}, function(json){
|
$.post(oItems.schedule.url, {format: "json", id: data.id}, function(json){
|
||||||
buildScheduleDialog(json);
|
buildScheduleDialog(json);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
oItems.schedule.callback = callback;
|
oItems.schedule.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a clear callback.
|
//define a clear callback.
|
||||||
if (oItems.clear !== undefined) {
|
if (oItems.clear !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
if (confirm("Remove all content?")) {
|
if (confirm("Remove all content?")) {
|
||||||
$.post(oItems.clear.url, {format: "json", id: data.id}, function(json){
|
$.post(oItems.clear.url, {format: "json", id: data.id}, function(json){
|
||||||
scheduleRefetchEvents(json);
|
scheduleRefetchEvents(json);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
oItems.clear.callback = callback;
|
oItems.clear.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define an edit callback.
|
//define an edit callback.
|
||||||
if (oItems.edit !== undefined) {
|
if (oItems.edit !== undefined) {
|
||||||
if(oItems.edit.items !== undefined){
|
if(oItems.edit.items !== undefined){
|
||||||
var edit = oItems.edit.items;
|
var edit = oItems.edit.items;
|
||||||
|
|
||||||
//edit a single instance
|
//edit a single instance
|
||||||
callback = function() {
|
callback = function() {
|
||||||
|
@ -344,55 +342,55 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
edit.all.callback = callback;
|
edit.all.callback = callback;
|
||||||
}else{
|
}else{
|
||||||
callback = function() {
|
callback = function() {
|
||||||
$.get(oItems.edit.url, {format: "json", id: data.id, type: oItems.edit._type}, function(json){
|
$.get(oItems.edit.url, {format: "json", id: data.id, type: oItems.edit._type}, function(json){
|
||||||
beginEditShow(json);
|
beginEditShow(json);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
oItems.edit.callback = callback;
|
oItems.edit.callback = callback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a content callback.
|
//define a content callback.
|
||||||
if (oItems.content !== undefined) {
|
if (oItems.content !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
$.get(oItems.content.url, {format: "json", id: data.id}, function(json){
|
$.get(oItems.content.url, {format: "json", id: data.id}, function(json){
|
||||||
buildContentDialog(json);
|
buildContentDialog(json);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
oItems.content.callback = callback;
|
oItems.content.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a soundcloud upload callback.
|
//define a soundcloud upload callback.
|
||||||
if (oItems.soundcloud_upload !== undefined) {
|
if (oItems.soundcloud_upload !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
uploadToSoundCloud(data.id);
|
uploadToSoundCloud(data.id);
|
||||||
};
|
};
|
||||||
oItems.soundcloud_upload.callback = callback;
|
oItems.soundcloud_upload.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a view on soundcloud callback.
|
//define a view on soundcloud callback.
|
||||||
if (oItems.soundcloud_view !== undefined) {
|
if (oItems.soundcloud_view !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
window.open(oItems.soundcloud_view.url);
|
window.open(oItems.soundcloud_view.url);
|
||||||
};
|
};
|
||||||
oItems.soundcloud_view.callback = callback;
|
oItems.soundcloud_view.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a cancel recorded show callback.
|
//define a cancel recorded show callback.
|
||||||
if (oItems.cancel_recorded !== undefined) {
|
if (oItems.cancel_recorded !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
confirmCancelRecordedShow(data.id);
|
confirmCancelRecordedShow(data.id);
|
||||||
};
|
};
|
||||||
oItems.cancel_recorded.callback = callback;
|
oItems.cancel_recorded.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a view recorded callback.
|
//define a view recorded callback.
|
||||||
if (oItems.view_recorded !== undefined) {
|
if (oItems.view_recorded !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
|
@ -400,71 +398,71 @@ $(document).ready(function() {
|
||||||
};
|
};
|
||||||
oItems.view_recorded.callback = callback;
|
oItems.view_recorded.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a cancel callback.
|
//define a cancel callback.
|
||||||
if (oItems.cancel !== undefined) {
|
if (oItems.cancel !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
confirmCancelShow(data.id);
|
confirmCancelShow(data.id);
|
||||||
};
|
};
|
||||||
oItems.cancel.callback = callback;
|
oItems.cancel.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
//define a delete callback.
|
//define a delete callback.
|
||||||
if (oItems.del !== undefined) {
|
if (oItems.del !== undefined) {
|
||||||
|
|
||||||
//repeating show multiple delete options
|
//repeating show multiple delete options
|
||||||
if (oItems.del.items !== undefined) {
|
if (oItems.del.items !== undefined) {
|
||||||
var del = oItems.del.items;
|
var del = oItems.del.items;
|
||||||
|
|
||||||
//delete a single instance
|
//delete a single instance
|
||||||
callback = function() {
|
callback = function() {
|
||||||
$.post(del.single.url, {format: "json", id: data.id}, function(json){
|
$.post(del.single.url, {format: "json", id: data.id}, function(json){
|
||||||
scheduleRefetchEvents(json);
|
scheduleRefetchEvents(json);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
del.single.callback = callback;
|
del.single.callback = callback;
|
||||||
|
|
||||||
//delete this instance and all following instances.
|
//delete this instance and all following instances.
|
||||||
callback = function() {
|
callback = function() {
|
||||||
$.post(del.following.url, {format: "json", id: data.id}, function(json){
|
$.post(del.following.url, {format: "json", id: data.id}, function(json){
|
||||||
scheduleRefetchEvents(json);
|
scheduleRefetchEvents(json);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
del.following.callback = callback;
|
del.following.callback = callback;
|
||||||
|
|
||||||
}
|
}
|
||||||
//single show
|
//single show
|
||||||
else {
|
else {
|
||||||
callback = function() {
|
callback = function() {
|
||||||
$.post(oItems.del.url, {format: "json", id: data.id}, function(json){
|
$.post(oItems.del.url, {format: "json", id: data.id}, function(json){
|
||||||
scheduleRefetchEvents(json);
|
scheduleRefetchEvents(json);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
oItems.del.callback = callback;
|
oItems.del.callback = callback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items = oItems;
|
items = oItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/schedule/make-context-menu",
|
url: "/schedule/make-context-menu",
|
||||||
type: "GET",
|
type: "GET",
|
||||||
data: {id : data.id, format: "json"},
|
data: {id : data.id, format: "json"},
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
async: false,
|
async: false,
|
||||||
success: function(json){
|
success: function(json){
|
||||||
processMenuItems(json.items);
|
processMenuItems(json.items);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: items,
|
items: items,
|
||||||
determinePosition : function($menu, x, y) {
|
determinePosition : function($menu, x, y) {
|
||||||
$menu.css('display', 'block')
|
$menu.css('display', 'block')
|
||||||
.position({ my: "left top", at: "right top", of: this, offset: "-20 10", collision: "fit"})
|
.position({ my: "left top", at: "right top", of: this, offset: "-20 10", collision: "fit"})
|
||||||
.css('display', 'none');
|
.css('display', 'none');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,10 @@ var AIRTIME = (function(AIRTIME){
|
||||||
$sbTable,
|
$sbTable,
|
||||||
$toolbar,
|
$toolbar,
|
||||||
$ul,
|
$ul,
|
||||||
$lib;
|
$lib,
|
||||||
|
cursors = [],
|
||||||
|
cursorIds = [];
|
||||||
|
showInstanceIds = [];
|
||||||
|
|
||||||
if (AIRTIME.showbuilder === undefined) {
|
if (AIRTIME.showbuilder === undefined) {
|
||||||
AIRTIME.showbuilder = {};
|
AIRTIME.showbuilder = {};
|
||||||
|
@ -127,7 +130,7 @@ var AIRTIME = (function(AIRTIME){
|
||||||
};
|
};
|
||||||
|
|
||||||
mod.selectCursor = function($el) {
|
mod.selectCursor = function($el) {
|
||||||
|
|
||||||
$el.addClass(CURSOR_SELECTED_CLASS);
|
$el.addClass(CURSOR_SELECTED_CLASS);
|
||||||
mod.checkToolBarIcons();
|
mod.checkToolBarIcons();
|
||||||
};
|
};
|
||||||
|
@ -208,11 +211,23 @@ var AIRTIME = (function(AIRTIME){
|
||||||
|
|
||||||
mod.fnItemCallback = function(json) {
|
mod.fnItemCallback = function(json) {
|
||||||
checkError(json);
|
checkError(json);
|
||||||
|
|
||||||
|
cursorIds = [];
|
||||||
|
/* We need to keep record of which show the cursor belongs to
|
||||||
|
* in the case where more than one show is displayed in the show builder
|
||||||
|
* because header and footer rows have the same id
|
||||||
|
*/
|
||||||
|
showInstanceIds = [];
|
||||||
|
cursors = $(".cursor-selected-row");
|
||||||
|
for (i = 0; i < cursors.length; i++) {
|
||||||
|
cursorIds.push(($(cursors.get(i)).attr("id")));
|
||||||
|
showInstanceIds.push(($(cursors.get(i)).attr("si_id")));
|
||||||
|
}
|
||||||
oSchedTable.fnDraw();
|
oSchedTable.fnDraw();
|
||||||
|
|
||||||
mod.enableUI();
|
mod.enableUI();
|
||||||
};
|
};
|
||||||
|
|
||||||
mod.fnAdd = function(aMediaIds, aSchedIds) {
|
mod.fnAdd = function(aMediaIds, aSchedIds) {
|
||||||
|
|
||||||
mod.disableUI();
|
mod.disableUI();
|
||||||
|
@ -236,11 +251,14 @@ var AIRTIME = (function(AIRTIME){
|
||||||
mod.fnRemove = function(aItems) {
|
mod.fnRemove = function(aItems) {
|
||||||
|
|
||||||
mod.disableUI();
|
mod.disableUI();
|
||||||
|
if (confirm("Delete selected item(s)?")) {
|
||||||
$.post( "/showbuilder/schedule-remove",
|
$.post( "/showbuilder/schedule-remove",
|
||||||
{"items": aItems, "format": "json"},
|
{"items": aItems, "format": "json"},
|
||||||
mod.fnItemCallback
|
mod.fnItemCallback
|
||||||
);
|
);
|
||||||
|
}else{
|
||||||
|
mod.enableUI();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mod.fnRemoveSelectedItems = function() {
|
mod.fnRemoveSelectedItems = function() {
|
||||||
|
@ -401,7 +419,6 @@ var AIRTIME = (function(AIRTIME){
|
||||||
headerIcon;
|
headerIcon;
|
||||||
|
|
||||||
fnPrepareSeparatorRow = function fnPrepareSeparatorRow(sRowContent, sClass, iNodeIndex) {
|
fnPrepareSeparatorRow = function fnPrepareSeparatorRow(sRowContent, sClass, iNodeIndex) {
|
||||||
|
|
||||||
$node = $(nRow.children[iNodeIndex]);
|
$node = $(nRow.children[iNodeIndex]);
|
||||||
$node.html(sRowContent);
|
$node.html(sRowContent);
|
||||||
$node.attr('colspan',100);
|
$node.attr('colspan',100);
|
||||||
|
@ -413,7 +430,7 @@ var AIRTIME = (function(AIRTIME){
|
||||||
|
|
||||||
$nRow.addClass(sClass);
|
$nRow.addClass(sClass);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (aData.header === true) {
|
if (aData.header === true) {
|
||||||
//remove the column classes from all tds.
|
//remove the column classes from all tds.
|
||||||
$nRow.find('td').removeClass();
|
$nRow.find('td').removeClass();
|
||||||
|
@ -572,11 +589,13 @@ var AIRTIME = (function(AIRTIME){
|
||||||
$nRow.addClass("sb-future");
|
$nRow.addClass("sb-future");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aData.allowed !== true) {
|
if (aData.allowed !== true || aData.header === true) {
|
||||||
$nRow.addClass("sb-not-allowed");
|
$nRow.addClass("sb-not-allowed");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$nRow.addClass("sb-allowed");
|
$nRow.addClass("sb-allowed");
|
||||||
|
$nRow.attr("id", aData.id);
|
||||||
|
$nRow.attr("si_id", aData.instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
//status used to colour tracks.
|
//status used to colour tracks.
|
||||||
|
@ -590,7 +609,7 @@ var AIRTIME = (function(AIRTIME){
|
||||||
if (aData.currentShow === true) {
|
if (aData.currentShow === true) {
|
||||||
$nRow.addClass("sb-current-show");
|
$nRow.addClass("sb-current-show");
|
||||||
}
|
}
|
||||||
|
|
||||||
//call the context menu so we can prevent the event from propagating.
|
//call the context menu so we can prevent the event from propagating.
|
||||||
$nRow.find('td:gt(1)').click(function(e){
|
$nRow.find('td:gt(1)').click(function(e){
|
||||||
|
|
||||||
|
@ -615,6 +634,7 @@ var AIRTIME = (function(AIRTIME){
|
||||||
$cursorRows,
|
$cursorRows,
|
||||||
$table = $(this),
|
$table = $(this),
|
||||||
$parent = $table.parent(),
|
$parent = $table.parent(),
|
||||||
|
$tr,
|
||||||
//use this array to cache DOM heights then we can detach the table to manipulate it to increase speed.
|
//use this array to cache DOM heights then we can detach the table to manipulate it to increase speed.
|
||||||
heights = [];
|
heights = [];
|
||||||
|
|
||||||
|
@ -653,6 +673,13 @@ var AIRTIME = (function(AIRTIME){
|
||||||
});
|
});
|
||||||
|
|
||||||
$td.append(markerDiv).wrapInner(wrapperDiv);
|
$td.append(markerDiv).wrapInner(wrapperDiv);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//re-highlight selected cursors before draw took place
|
||||||
|
for (i = 0; i < cursorIds.length; i++) {
|
||||||
|
$tr = $table.find("tr[id="+cursorIds[i]+"][si_id="+showInstanceIds[i]+"]");
|
||||||
|
mod.selectCursor($tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if there is only 1 cursor on the page highlight it by default.
|
//if there is only 1 cursor on the page highlight it by default.
|
||||||
|
@ -678,8 +705,14 @@ var AIRTIME = (function(AIRTIME){
|
||||||
|
|
||||||
if (temp.length > 0) {
|
if (temp.length > 0) {
|
||||||
aData = temp.data("aData");
|
aData = temp.data("aData");
|
||||||
|
// max time interval
|
||||||
mod.timeout = setTimeout(mod.refresh, aData.refresh * 1000); //need refresh in milliseconds
|
// setTimeout allows only up to (2^31)-1 millisecs timeout value
|
||||||
|
maxRefreshInterval = Math.pow(2, 31) - 1;
|
||||||
|
refreshInterval = aData.refresh * 1000;
|
||||||
|
if(refreshInterval > maxRefreshInterval){
|
||||||
|
refreshInterval = maxRefreshInterval;
|
||||||
|
}
|
||||||
|
mod.timeout = setTimeout(mod.refresh, refreshInterval); //need refresh in milliseconds
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -890,8 +923,8 @@ var AIRTIME = (function(AIRTIME){
|
||||||
|
|
||||||
$ul = $("<ul/>");
|
$ul = $("<ul/>");
|
||||||
$ul.append('<li class="ui-state-default sb-button-select" title="Select"><span class="ui-icon ui-icon-document-b"></span></li>')
|
$ul.append('<li class="ui-state-default sb-button-select" title="Select"><span class="ui-icon ui-icon-document-b"></span></li>')
|
||||||
.append('<li class="ui-state-default ui-state-disabled sb-button-trim" title="Delete all overbooked tracks"><span class="ui-icon ui-icon-scissors"></span></li>')
|
.append('<li class="ui-state-default ui-state-disabled sb-button-trim" title="Remove all overbooked tracks"><span class="ui-icon ui-icon-scissors"></span></li>')
|
||||||
.append('<li class="ui-state-default ui-state-disabled sb-button-delete" title="Delete selected scheduled items"><span class="ui-icon ui-icon-trash"></span></li>');
|
.append('<li class="ui-state-default ui-state-disabled sb-button-delete" title="Remove selected scheduled items"><span class="ui-icon ui-icon-trash"></span></li>');
|
||||||
$toolbar.append($ul);
|
$toolbar.append($ul);
|
||||||
|
|
||||||
$ul = $("<ul/>");
|
$ul = $("<ul/>");
|
||||||
|
@ -1065,7 +1098,7 @@ var AIRTIME = (function(AIRTIME){
|
||||||
if (oItems.del !== undefined) {
|
if (oItems.del !== undefined) {
|
||||||
|
|
||||||
callback = function() {
|
callback = function() {
|
||||||
if (confirm("Delete selected Items?")) {
|
if (confirm("Delete selected item?")) {
|
||||||
AIRTIME.showbuilder.fnRemove([{
|
AIRTIME.showbuilder.fnRemove([{
|
||||||
id: data.id,
|
id: data.id,
|
||||||
timestamp: data.timestamp,
|
timestamp: data.timestamp,
|
||||||
|
|
|
@ -30,7 +30,7 @@ AIRTIME = (function(AIRTIME) {
|
||||||
|
|
||||||
oBaseDatePickerSettings = {
|
oBaseDatePickerSettings = {
|
||||||
dateFormat: 'yy-mm-dd',
|
dateFormat: 'yy-mm-dd',
|
||||||
onSelect: function(sDate, oDatePicker) {
|
onClick: function(sDate, oDatePicker) {
|
||||||
$(this).datepicker( "setDate", sDate );
|
$(this).datepicker( "setDate", sDate );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -83,6 +83,34 @@ AIRTIME = (function(AIRTIME) {
|
||||||
oTable.fnDraw();
|
oTable.fnDraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showSearchSubmit() {
|
||||||
|
var fn,
|
||||||
|
oRange,
|
||||||
|
op,
|
||||||
|
oTable = $('#show_builder_table').dataTable();
|
||||||
|
|
||||||
|
//reset timestamp value since input values could have changed.
|
||||||
|
AIRTIME.showbuilder.resetTimestamp();
|
||||||
|
|
||||||
|
oRange = AIRTIME.utilities.fnGetScheduleRange(dateStartId, timeStartId, dateEndId, timeEndId);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
mod.onReady = function() {
|
mod.onReady = function() {
|
||||||
//define module vars.
|
//define module vars.
|
||||||
|
@ -118,33 +146,7 @@ AIRTIME = (function(AIRTIME) {
|
||||||
|
|
||||||
$builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 95);
|
$builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 95);
|
||||||
|
|
||||||
$builder.on("click", "#sb_submit", function(ev){
|
$builder.on("click", "#sb_submit", showSearchSubmit);
|
||||||
var fn,
|
|
||||||
oRange,
|
|
||||||
op,
|
|
||||||
oTable = $('#show_builder_table').dataTable();
|
|
||||||
|
|
||||||
//reset timestamp value since input values could have changed.
|
|
||||||
AIRTIME.showbuilder.resetTimestamp();
|
|
||||||
|
|
||||||
oRange = AIRTIME.utilities.fnGetScheduleRange(dateStartId, timeStartId, dateEndId, timeEndId);
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
|
|
||||||
$builder.on("click","#sb_edit", function (ev){
|
$builder.on("click","#sb_edit", function (ev){
|
||||||
var schedTable = $("#show_builder_table").dataTable();
|
var schedTable = $("#show_builder_table").dataTable();
|
||||||
|
@ -220,7 +222,9 @@ AIRTIME = (function(AIRTIME) {
|
||||||
|
|
||||||
if ($(this).is(':checked')) {
|
if ($(this).is(':checked')) {
|
||||||
$(ev.delegateTarget).find('#sb_show_filter').val(0);
|
$(ev.delegateTarget).find('#sb_show_filter').val(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSearchSubmit();
|
||||||
});
|
});
|
||||||
|
|
||||||
//set select event for choosing a show.
|
//set select event for choosing a show.
|
||||||
|
@ -229,6 +233,9 @@ AIRTIME = (function(AIRTIME) {
|
||||||
if ($(this).val() !== 0) {
|
if ($(this).val() !== 0) {
|
||||||
$(ev.delegateTarget).find('#sb_my_shows').attr("checked", false);
|
$(ev.delegateTarget).find('#sb_my_shows').attr("checked", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSearchSubmit();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkScheduleUpdates(){
|
function checkScheduleUpdates(){
|
||||||
|
@ -277,4 +284,4 @@ AIRTIME = (function(AIRTIME) {
|
||||||
} (AIRTIME || {}));
|
} (AIRTIME || {}));
|
||||||
|
|
||||||
$(document).ready(AIRTIME.builderMain.onReady);
|
$(document).ready(AIRTIME.builderMain.onReady);
|
||||||
$(window).resize(AIRTIME.builderMain.onResize);
|
$(window).resize(AIRTIME.builderMain.onResize);
|
||||||
|
|
|
@ -15,8 +15,11 @@ class Version20110711161043 extends AbstractMigration
|
||||||
{
|
{
|
||||||
public function up(Schema $schema)
|
public function up(Schema $schema)
|
||||||
{
|
{
|
||||||
|
$ini = parse_ini_file(__DIR__."/../include/airtime-install.ini");
|
||||||
|
$stor_dir = $ini["storage_dir"];
|
||||||
|
|
||||||
/* 1) update cc_files table to include to "directory" column */
|
/* 1) update cc_files table to include to "directory" column */
|
||||||
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('stor', '/srv/airtime/stor/');");
|
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('stor', $stor_dir);");
|
||||||
|
|
||||||
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('link', '');");
|
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('link', '');");
|
||||||
|
|
||||||
|
|
|
@ -318,8 +318,13 @@ class AirtimeInstall
|
||||||
|
|
||||||
public static function SetDefaultTimezone()
|
public static function SetDefaultTimezone()
|
||||||
{
|
{
|
||||||
$con = Propel::getConnection();
|
$con = Propel::getConnection();
|
||||||
$defaultTimezone = exec("cat /etc/timezone");
|
// we need to run php as commandline because we want to get the timezone in cli php.ini file
|
||||||
|
$defaultTimezone = exec("php -r 'echo date_default_timezone_get().PHP_EOL;'");
|
||||||
|
$defaultTimezone = trim($defaultTimezone);
|
||||||
|
if((!in_array($defaultTimezone, DateTimeZone::listIdentifiers()))){
|
||||||
|
$defaultTimezone = "UTC";
|
||||||
|
}
|
||||||
$sql = "INSERT INTO cc_pref (keystr, valstr) VALUES ('timezone', '$defaultTimezone')";
|
$sql = "INSERT INTO cc_pref (keystr, valstr) VALUES ('timezone', '$defaultTimezone')";
|
||||||
$result = $con->exec($sql);
|
$result = $con->exec($sql);
|
||||||
if ($result < 1) {
|
if ($result < 1) {
|
||||||
|
|
|
@ -61,6 +61,16 @@ AirtimeInstall::InstallStorageDirectory();
|
||||||
|
|
||||||
$db_install = getenv("nodb")!="t";
|
$db_install = getenv("nodb")!="t";
|
||||||
if ($db_install) {
|
if ($db_install) {
|
||||||
|
|
||||||
|
echo "* Checking database for correct encoding".PHP_EOL;
|
||||||
|
exec('su -c \'psql -t -c "SHOW SERVER_ENCODING"\' postgres | grep -i "UTF.*8"', $out, $return_code);
|
||||||
|
if ($return_code != 0){
|
||||||
|
echo " * Unfortunately your postgresql database has not been created using a UTF-8 encoding.".PHP_EOL;
|
||||||
|
echo " * As of Airtime 2.1, installs will fail unless the encoding has been set to UTF-8. Please verify this is the case".PHP_EOL;
|
||||||
|
echo " * and try the install again".PHP_EOL;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
if($newInstall) {
|
if($newInstall) {
|
||||||
//call external script. "y" argument means force creation of database tables.
|
//call external script. "y" argument means force creation of database tables.
|
||||||
passthru('php '.__DIR__.'/airtime-db-install.php y');
|
passthru('php '.__DIR__.'/airtime-db-install.php y');
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* All functions other than start() should be marked as
|
||||||
|
* private.
|
||||||
|
*/
|
||||||
|
class AirtimeDatabaseUpgrade{
|
||||||
|
|
||||||
|
public static function start($p_dbValues){
|
||||||
|
echo "* Updating Database".PHP_EOL;
|
||||||
|
self::task0($p_dbValues);
|
||||||
|
echo " * Complete".PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function task0($p_dbValues){
|
||||||
|
|
||||||
|
$username = $p_dbValues['database']['dbuser'];
|
||||||
|
$password = $p_dbValues['database']['dbpass'];
|
||||||
|
$host = $p_dbValues['database']['host'];
|
||||||
|
$database = $p_dbValues['database']['dbname'];
|
||||||
|
$dir = __DIR__;
|
||||||
|
|
||||||
|
passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/data/upgrade.sql $database 2>&1 | grep -v \"will create implicit index\"");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once 'DbUpgrade.php';
|
||||||
|
|
||||||
|
$filename = "/etc/airtime/airtime.conf";
|
||||||
|
$values = parse_ini_file($filename, true);
|
||||||
|
|
||||||
|
AirtimeDatabaseUpgrade::start($values);
|
|
@ -0,0 +1,4 @@
|
||||||
|
DELETE FROM cc_pref WHERE keystr = 'system_version';
|
||||||
|
INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '2.1.3');
|
||||||
|
|
||||||
|
UPDATE cc_show_instances SET time_filled='00:00:00' WHERE time_filled IS NULL;
|
|
@ -10,6 +10,7 @@
|
||||||
### END INIT INFO
|
### END INIT INFO
|
||||||
|
|
||||||
USERID=pypo
|
USERID=pypo
|
||||||
|
ROOTUSERID=root
|
||||||
GROUPID=pypo
|
GROUPID=pypo
|
||||||
NAME=Airtime\ Playout
|
NAME=Airtime\ Playout
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ start () {
|
||||||
chown pypo:pypo /etc/airtime
|
chown pypo:pypo /etc/airtime
|
||||||
chown pypo:pypo /etc/airtime/liquidsoap.cfg
|
chown pypo:pypo /etc/airtime/liquidsoap.cfg
|
||||||
|
|
||||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0
|
start-stop-daemon --start --background --quiet --chuid $ROOTUSERID:$ROOTUSERID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0
|
||||||
monit monitor airtime-playout >/dev/null 2>&1
|
monit monitor airtime-playout >/dev/null 2>&1
|
||||||
|
|
||||||
liquidsoap_start
|
liquidsoap_start
|
||||||
|
@ -80,7 +81,7 @@ monit_restart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start_no_monit() {
|
start_no_monit() {
|
||||||
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0
|
start-stop-daemon --start --background --quiet --chuid $ROOTUSERID:$ROOTUSERID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0
|
||||||
liquidsoap_start
|
liquidsoap_start
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,20 +109,6 @@ try:
|
||||||
print e
|
print e
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
"""
|
|
||||||
logging.basicConfig(format='%(message)s')
|
|
||||||
|
|
||||||
#generate liquidsoap config file
|
|
||||||
#access the DB and generate liquidsoap.cfg under /etc/airtime/
|
|
||||||
ac = api_client.api_client_factory(config, logging.getLogger())
|
|
||||||
ss = ac.get_stream_setting()
|
|
||||||
|
|
||||||
if ss is not None:
|
|
||||||
generate_liquidsoap_config(ss)
|
|
||||||
else:
|
|
||||||
print "Unable to connect to the Airtime server."
|
|
||||||
"""
|
|
||||||
|
|
||||||
#initialize init.d scripts
|
#initialize init.d scripts
|
||||||
subprocess.call("update-rc.d airtime-playout defaults >/dev/null 2>&1", shell=True)
|
subprocess.call("update-rc.d airtime-playout defaults >/dev/null 2>&1", shell=True)
|
||||||
|
|
||||||
|
|
|
@ -130,27 +130,29 @@ end
|
||||||
|
|
||||||
def append_dj_inputs(master_harbor_input_port, master_harbor_input_mount_point, dj_harbor_input_port, dj_harbor_input_mount_point, s) =
|
def append_dj_inputs(master_harbor_input_port, master_harbor_input_mount_point, dj_harbor_input_port, dj_harbor_input_mount_point, s) =
|
||||||
if master_harbor_input_port != 0 and master_harbor_input_mount_point != "" and dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then
|
if master_harbor_input_port != 0 and master_harbor_input_mount_point != "" and dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then
|
||||||
master_dj = mksafe(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client,
|
master_dj = mksafe(audio_to_stereo(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client,
|
||||||
max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect))
|
max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect)))
|
||||||
dj_live = mksafe(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client,
|
dj_live = mksafe(audio_to_stereo(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client,
|
||||||
max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect))
|
max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect)))
|
||||||
|
|
||||||
master_dj = rewrite_metadata([("artist","Airtime"), ("title", "Master Dj")],master_dj)
|
master_dj = rewrite_metadata([("artist","Airtime"), ("title", "Master Dj")],master_dj)
|
||||||
dj_live = rewrite_metadata([("artist","Airtime"), ("title", "Live Dj")],dj_live)
|
dj_live = rewrite_metadata([("artist","Airtime"), ("title", "Live Dj")],dj_live)
|
||||||
|
|
||||||
ignore(output.dummy(master_dj, fallible=true))
|
ignore(output.dummy(master_dj, fallible=true))
|
||||||
ignore(output.dummy(dj_live, fallible=true))
|
ignore(output.dummy(dj_live, fallible=true))
|
||||||
switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition, transition], [({!master_dj_enabled},master_dj), ({!live_dj_enabled},dj_live), ({true}, s)])
|
switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition, transition], [({!master_dj_enabled},master_dj), ({!live_dj_enabled},dj_live), ({true}, s)])
|
||||||
elsif master_harbor_input_port != 0 and master_harbor_input_mount_point != "" then
|
elsif master_harbor_input_port != 0 and master_harbor_input_mount_point != "" then
|
||||||
master_dj = mksafe(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client,
|
master_dj = mksafe(audio_to_stereo(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client,
|
||||||
max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect))
|
max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect)))
|
||||||
ignore(output.dummy(master_dj, fallible=true))
|
ignore(output.dummy(master_dj, fallible=true))
|
||||||
master_dj = rewrite_metadata([("artist","Airtime"), ("title", "Master Dj")],master_dj)
|
master_dj = rewrite_metadata([("artist","Airtime"), ("title", "Master Dj")],master_dj)
|
||||||
switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!master_dj_enabled},master_dj), ({true}, s)])
|
switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!master_dj_enabled},master_dj), ({true}, s)])
|
||||||
elsif dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then
|
elsif dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then
|
||||||
dj_live = mksafe(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client,
|
dj_live = mksafe(audio_to_stereo(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client,
|
||||||
max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect))
|
max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect)))
|
||||||
|
|
||||||
dj_live = rewrite_metadata([("artist","Airtime"), ("title", "Live Dj")],dj_live)
|
dj_live = rewrite_metadata([("artist","Airtime"), ("title", "Live Dj")],dj_live)
|
||||||
|
|
||||||
ignore(output.dummy(dj_live, fallible=true))
|
ignore(output.dummy(dj_live, fallible=true))
|
||||||
switch(id="live_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!live_dj_enabled},dj_live), ({true}, s)])
|
switch(id="live_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!live_dj_enabled},dj_live), ({true}, s)])
|
||||||
else
|
else
|
||||||
|
|
|
@ -10,4 +10,4 @@ SCRIPT=`readlink -f $0`
|
||||||
# Absolute path this script is in
|
# Absolute path this script is in
|
||||||
SCRIPTPATH=`dirname $SCRIPT`
|
SCRIPTPATH=`dirname $SCRIPT`
|
||||||
|
|
||||||
cd ${SCRIPTPATH}/../ && python pypo-notify.py "$@"
|
cd ${SCRIPTPATH}/../ && python pyponotify.py "$@"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
[loggers]
|
[loggers]
|
||||||
keys=root,fetch,push,recorder,message_h,notify
|
keys=root,fetch,push,recorder,message_h
|
||||||
|
|
||||||
[handlers]
|
[handlers]
|
||||||
keys=pypo,recorder,message_h,notify
|
keys=pypo,recorder,message_h
|
||||||
|
|
||||||
[formatters]
|
[formatters]
|
||||||
keys=simpleFormatter
|
keys=simpleFormatter
|
||||||
|
@ -35,18 +35,6 @@ handlers=message_h
|
||||||
qualname=message_h
|
qualname=message_h
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
[logger_notify]
|
|
||||||
level=DEBUG
|
|
||||||
handlers=notify
|
|
||||||
qualname=notify
|
|
||||||
propagate=0
|
|
||||||
|
|
||||||
[handler_notify]
|
|
||||||
class=logging.handlers.RotatingFileHandler
|
|
||||||
level=DEBUG
|
|
||||||
formatter=simpleFormatter
|
|
||||||
args=("/var/log/airtime/pypo/notify.log", 'a', 1000000, 5,)
|
|
||||||
|
|
||||||
[handler_pypo]
|
[handler_pypo]
|
||||||
class=logging.handlers.RotatingFileHandler
|
class=logging.handlers.RotatingFileHandler
|
||||||
level=DEBUG
|
level=DEBUG
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
[loggers]
|
||||||
|
keys=root,notify
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys=notify
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys=simpleFormatter
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=notify
|
||||||
|
|
||||||
|
[logger_notify]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=notify
|
||||||
|
qualname=notify
|
||||||
|
propagate=0
|
||||||
|
|
||||||
|
[handler_notify]
|
||||||
|
class=logging.handlers.RotatingFileHandler
|
||||||
|
level=DEBUG
|
||||||
|
formatter=simpleFormatter
|
||||||
|
args=("/var/log/airtime/pypo/notify.log", 'a', 1000000, 5,)
|
||||||
|
|
||||||
|
[formatter_simpleFormatter]
|
||||||
|
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
|
||||||
|
datefmt=
|
|
@ -2,13 +2,15 @@
|
||||||
Python part of radio playout (pypo)
|
Python part of radio playout (pypo)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from optparse import OptionParser
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import telnetlib
|
||||||
|
|
||||||
import time
|
import time
|
||||||
from optparse import *
|
|
||||||
import sys
|
import sys
|
||||||
import signal
|
import signal
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
|
||||||
import logging.handlers
|
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
from Queue import Queue
|
from Queue import Queue
|
||||||
|
@ -50,40 +52,6 @@ parser.add_option("-c", "--check", help="Check the cached schedule and exit", de
|
||||||
#need to wait for Python 2.7 for this..
|
#need to wait for Python 2.7 for this..
|
||||||
#logging.captureWarnings(True)
|
#logging.captureWarnings(True)
|
||||||
|
|
||||||
def configure_locale():
|
|
||||||
logger.debug("Before %s", locale.nl_langinfo(locale.CODESET))
|
|
||||||
current_locale = locale.getlocale()
|
|
||||||
|
|
||||||
if current_locale[1] is None:
|
|
||||||
logger.debug("No locale currently set. Attempting to get default locale.")
|
|
||||||
default_locale = locale.getdefaultlocale()
|
|
||||||
|
|
||||||
if default_locale[1] is None:
|
|
||||||
logger.debug("No default locale exists. Let's try loading from /etc/default/locale")
|
|
||||||
if os.path.exists("/etc/default/locale"):
|
|
||||||
config = ConfigObj('/etc/default/locale')
|
|
||||||
lang = config.get('LANG')
|
|
||||||
new_locale = lang
|
|
||||||
else:
|
|
||||||
logger.error("/etc/default/locale could not be found! Please run 'sudo update-locale' from command-line.")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
new_locale = default_locale
|
|
||||||
|
|
||||||
logger.info("New locale set to: %s", locale.setlocale(locale.LC_ALL, new_locale))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
reload(sys)
|
|
||||||
sys.setdefaultencoding("UTF-8")
|
|
||||||
current_locale_encoding = locale.getlocale()[1].lower()
|
|
||||||
logger.debug("sys default encoding %s", sys.getdefaultencoding())
|
|
||||||
logger.debug("After %s", locale.nl_langinfo(locale.CODESET))
|
|
||||||
|
|
||||||
if current_locale_encoding not in ['utf-8', 'utf8']:
|
|
||||||
logger.error("Need a UTF-8 locale. Currently '%s'. Exiting..." % current_locale_encoding)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# configure logging
|
# configure logging
|
||||||
try:
|
try:
|
||||||
logging.config.fileConfig("logging.cfg")
|
logging.config.fileConfig("logging.cfg")
|
||||||
|
@ -92,7 +60,42 @@ try:
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print "Couldn't configure logging"
|
print "Couldn't configure logging"
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
def configure_locale():
|
||||||
|
logger.debug("Before %s", locale.nl_langinfo(locale.CODESET))
|
||||||
|
current_locale = locale.getlocale()
|
||||||
|
|
||||||
|
if current_locale[1] is None:
|
||||||
|
logger.debug("No locale currently set. Attempting to get default locale.")
|
||||||
|
default_locale = locale.getdefaultlocale()
|
||||||
|
|
||||||
|
if default_locale[1] is None:
|
||||||
|
logger.debug("No default locale exists. Let's try loading from /etc/default/locale")
|
||||||
|
if os.path.exists("/etc/default/locale"):
|
||||||
|
locale_config = ConfigObj('/etc/default/locale')
|
||||||
|
lang = locale_config.get('LANG')
|
||||||
|
new_locale = lang
|
||||||
|
else:
|
||||||
|
logger.error("/etc/default/locale could not be found! Please run 'sudo update-locale' from command-line.")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
new_locale = default_locale
|
||||||
|
|
||||||
|
logger.info("New locale set to: %s", locale.setlocale(locale.LC_ALL, new_locale))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
reload(sys)
|
||||||
|
sys.setdefaultencoding("UTF-8")
|
||||||
|
current_locale_encoding = locale.getlocale()[1].lower()
|
||||||
|
logger.debug("sys default encoding %s", sys.getdefaultencoding())
|
||||||
|
logger.debug("After %s", locale.nl_langinfo(locale.CODESET))
|
||||||
|
|
||||||
|
if current_locale_encoding not in ['utf-8', 'utf8']:
|
||||||
|
logger.error("Need a UTF-8 locale. Currently '%s'. Exiting..." % current_locale_encoding)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
configure_locale()
|
configure_locale()
|
||||||
|
|
||||||
# loading config file
|
# loading config file
|
||||||
|
@ -105,63 +108,65 @@ except Exception, e:
|
||||||
class Global:
|
class Global:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.api_client = api_client.api_client_factory(config)
|
self.api_client = api_client.api_client_factory(config)
|
||||||
|
|
||||||
def selfcheck(self):
|
def selfcheck(self):
|
||||||
self.api_client = api_client.api_client_factory(config)
|
self.api_client = api_client.api_client_factory(config)
|
||||||
return self.api_client.is_server_compatible()
|
return self.api_client.is_server_compatible()
|
||||||
|
|
||||||
def test_api(self):
|
def test_api(self):
|
||||||
self.api_client.test()
|
self.api_client.test()
|
||||||
|
|
||||||
"""
|
|
||||||
def check_schedule(self):
|
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
try:
|
|
||||||
schedule_file = open(self.schedule_file, "r")
|
|
||||||
schedule = pickle.load(schedule_file)
|
|
||||||
schedule_file.close()
|
|
||||||
|
|
||||||
except Exception, e:
|
|
||||||
logger.error("%s", e)
|
|
||||||
schedule = None
|
|
||||||
|
|
||||||
for pkey in sorted(schedule.iterkeys()):
|
|
||||||
playlist = schedule[pkey]
|
|
||||||
print '*****************************************'
|
|
||||||
print '\033[0;32m%s %s\033[m' % ('scheduled at:', str(pkey))
|
|
||||||
print 'cached at : ' + self.cache_dir + str(pkey)
|
|
||||||
print 'played: ' + str(playlist['played'])
|
|
||||||
print 'schedule id: ' + str(playlist['schedule_id'])
|
|
||||||
print 'duration: ' + str(playlist['duration'])
|
|
||||||
print 'source id: ' + str(playlist['x_ident'])
|
|
||||||
print '-----------------------------------------'
|
|
||||||
|
|
||||||
for media in playlist['medias']:
|
|
||||||
print media
|
|
||||||
"""
|
|
||||||
|
|
||||||
def keyboardInterruptHandler(signum, frame):
|
def keyboardInterruptHandler(signum, frame):
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
logger.info('\nKeyboard Interrupt\n')
|
logger.info('\nKeyboard Interrupt\n')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
def liquidsoap_running_test(telnet_lock, host, port, logger):
|
||||||
|
logger.debug("Checking to see if Liquidsoap is running")
|
||||||
|
success = True
|
||||||
|
try:
|
||||||
|
telnet_lock.acquire()
|
||||||
|
tn = telnetlib.Telnet(host, port)
|
||||||
|
msg = "version\n"
|
||||||
|
tn.write(msg)
|
||||||
|
tn.write("exit\n")
|
||||||
|
logger.info("Liquidsoap version %s", tn.read_all())
|
||||||
|
except Exception, e:
|
||||||
|
logger.error(str(e))
|
||||||
|
success = False
|
||||||
|
finally:
|
||||||
|
telnet_lock.release()
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
logger = logging.getLogger()
|
|
||||||
logger.info('###########################################')
|
logger.info('###########################################')
|
||||||
logger.info('# *** pypo *** #')
|
logger.info('# *** pypo *** #')
|
||||||
logger.info('# Liquidsoap Scheduled Playout System #')
|
logger.info('# Liquidsoap Scheduled Playout System #')
|
||||||
logger.info('###########################################')
|
logger.info('###########################################')
|
||||||
|
|
||||||
|
#Although all of our calculations are in UTC, it is useful to know what timezone
|
||||||
|
#the local machine is, so that we have a reference for what time the actual
|
||||||
|
#log entries were made
|
||||||
|
logger.info("Timezone: %s" % str(time.tzname))
|
||||||
|
logger.info("UTC time: %s" % str(datetime.utcnow()))
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, keyboardInterruptHandler)
|
signal.signal(signal.SIGINT, keyboardInterruptHandler)
|
||||||
|
|
||||||
# initialize
|
# initialize
|
||||||
g = Global()
|
g = Global()
|
||||||
|
|
||||||
while not g.selfcheck(): time.sleep(5)
|
while not g.selfcheck():
|
||||||
|
time.sleep(5)
|
||||||
logger = logging.getLogger()
|
|
||||||
|
telnet_lock = Lock()
|
||||||
|
|
||||||
|
ls_host = config['ls_host']
|
||||||
|
ls_port = config['ls_port']
|
||||||
|
while not liquidsoap_running_test(telnet_lock, ls_host, ls_port, logger):
|
||||||
|
logger.warning("Liquidsoap not started yet. Sleeping one second and trying again")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
if options.test:
|
if options.test:
|
||||||
g.test_api()
|
g.test_api()
|
||||||
|
@ -173,9 +178,9 @@ if __name__ == '__main__':
|
||||||
pypoFetch_q = Queue()
|
pypoFetch_q = Queue()
|
||||||
recorder_q = Queue()
|
recorder_q = Queue()
|
||||||
pypoPush_q = Queue()
|
pypoPush_q = Queue()
|
||||||
|
|
||||||
telnet_lock = Lock()
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This queue is shared between pypo-fetch and pypo-file, where pypo-file
|
This queue is shared between pypo-fetch and pypo-file, where pypo-file
|
||||||
is the receiver. Pypo-fetch will send every schedule it gets to pypo-file
|
is the receiver. Pypo-fetch will send every schedule it gets to pypo-file
|
||||||
|
@ -183,19 +188,19 @@ if __name__ == '__main__':
|
||||||
priority, and will retrieve it.
|
priority, and will retrieve it.
|
||||||
"""
|
"""
|
||||||
media_q = Queue()
|
media_q = Queue()
|
||||||
|
|
||||||
pmh = PypoMessageHandler(pypoFetch_q, recorder_q)
|
pmh = PypoMessageHandler(pypoFetch_q, recorder_q)
|
||||||
pmh.daemon = True
|
pmh.daemon = True
|
||||||
pmh.start()
|
pmh.start()
|
||||||
|
|
||||||
pfile = PypoFile(media_q)
|
pfile = PypoFile(media_q)
|
||||||
pfile.daemon = True
|
pfile.daemon = True
|
||||||
pfile.start()
|
pfile.start()
|
||||||
|
|
||||||
pf = PypoFetch(pypoFetch_q, pypoPush_q, media_q, telnet_lock)
|
pf = PypoFetch(pypoFetch_q, pypoPush_q, media_q, telnet_lock)
|
||||||
pf.daemon = True
|
pf.daemon = True
|
||||||
pf.start()
|
pf.start()
|
||||||
|
|
||||||
pp = PypoPush(pypoPush_q, telnet_lock)
|
pp = PypoPush(pypoPush_q, telnet_lock)
|
||||||
pp.daemon = True
|
pp.daemon = True
|
||||||
pp.start()
|
pp.start()
|
||||||
|
@ -204,23 +209,12 @@ if __name__ == '__main__':
|
||||||
recorder.daemon = True
|
recorder.daemon = True
|
||||||
recorder.start()
|
recorder.start()
|
||||||
|
|
||||||
# all join() are commented out becase we want to exit entire pypo
|
# all join() are commented out because we want to exit entire pypo
|
||||||
# if pypofetch is exiting
|
# if pypofetch is exiting
|
||||||
#pmh.join()
|
#pmh.join()
|
||||||
#recorder.join()
|
#recorder.join()
|
||||||
#pp.join()
|
#pp.join()
|
||||||
pf.join()
|
pf.join()
|
||||||
|
|
||||||
logger.info("pypo fetch exit")
|
logger.info("pypo fetch exit")
|
||||||
sys.exit()
|
sys.exit()
|
||||||
"""
|
|
||||||
if options.check:
|
|
||||||
try: g.check_schedule()
|
|
||||||
except Exception, e:
|
|
||||||
print e
|
|
||||||
|
|
||||||
if options.cleanup:
|
|
||||||
try: pf.cleanup('scheduler')
|
|
||||||
except Exception, e:
|
|
||||||
print e
|
|
||||||
"""
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
|
||||||
import logging.config
|
import logging.config
|
||||||
import shutil
|
|
||||||
import json
|
import json
|
||||||
import telnetlib
|
import telnetlib
|
||||||
import copy
|
import copy
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
from Queue import Empty
|
||||||
|
|
||||||
from api_clients import api_client
|
from api_clients import api_client
|
||||||
from std_err_override import LogWriter
|
from std_err_override import LogWriter
|
||||||
|
|
||||||
|
@ -29,7 +29,9 @@ try:
|
||||||
config = ConfigObj('/etc/airtime/pypo.cfg')
|
config = ConfigObj('/etc/airtime/pypo.cfg')
|
||||||
LS_HOST = config['ls_host']
|
LS_HOST = config['ls_host']
|
||||||
LS_PORT = config['ls_port']
|
LS_PORT = config['ls_port']
|
||||||
POLL_INTERVAL = int(config['poll_interval'])
|
#POLL_INTERVAL = int(config['poll_interval'])
|
||||||
|
POLL_INTERVAL = 1800
|
||||||
|
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.error('Error loading config file: %s', e)
|
logger.error('Error loading config file: %s', e)
|
||||||
|
@ -43,12 +45,12 @@ class PypoFetch(Thread):
|
||||||
self.push_queue = pypoPush_q
|
self.push_queue = pypoPush_q
|
||||||
self.media_prepare_queue = media_q
|
self.media_prepare_queue = media_q
|
||||||
self.last_update_schedule_timestamp = time.time()
|
self.last_update_schedule_timestamp = time.time()
|
||||||
self.listener_timeout = 3600
|
self.listener_timeout = POLL_INTERVAL
|
||||||
|
|
||||||
self.telnet_lock = telnet_lock
|
self.telnet_lock = telnet_lock
|
||||||
|
|
||||||
self.logger = logging.getLogger();
|
self.logger = logging.getLogger();
|
||||||
|
|
||||||
self.cache_dir = os.path.join(config["cache_dir"], "scheduler")
|
self.cache_dir = os.path.join(config["cache_dir"], "scheduler")
|
||||||
self.logger.debug("Cache dir %s", self.cache_dir)
|
self.logger.debug("Cache dir %s", self.cache_dir)
|
||||||
|
|
||||||
|
@ -63,24 +65,24 @@ class PypoFetch(Thread):
|
||||||
os.makedirs(dir)
|
os.makedirs(dir)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.schedule_data = []
|
self.schedule_data = []
|
||||||
self.logger.info("PypoFetch: init complete")
|
self.logger.info("PypoFetch: init complete")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Handle a message from RabbitMQ, put it into our yucky global var.
|
Handle a message from RabbitMQ, put it into our yucky global var.
|
||||||
Hopefully there is a better way to do this.
|
Hopefully there is a better way to do this.
|
||||||
"""
|
"""
|
||||||
def handle_message(self, message):
|
def handle_message(self, message):
|
||||||
try:
|
try:
|
||||||
self.logger.info("Received event from Pypo Message Handler: %s" % message)
|
self.logger.info("Received event from Pypo Message Handler: %s" % message)
|
||||||
|
|
||||||
m = json.loads(message)
|
m = json.loads(message)
|
||||||
command = m['event_type']
|
command = m['event_type']
|
||||||
self.logger.info("Handling command: " + command)
|
self.logger.info("Handling command: " + command)
|
||||||
|
|
||||||
if command == 'update_schedule':
|
if command == 'update_schedule':
|
||||||
self.schedule_data = m['schedule']
|
self.schedule_data = m['schedule']
|
||||||
self.process_schedule(self.schedule_data)
|
self.process_schedule(self.schedule_data)
|
||||||
elif command == 'update_stream_setting':
|
elif command == 'update_stream_setting':
|
||||||
self.logger.info("Updating stream setting...")
|
self.logger.info("Updating stream setting...")
|
||||||
|
@ -100,12 +102,12 @@ class PypoFetch(Thread):
|
||||||
elif command == 'disconnect_source':
|
elif command == 'disconnect_source':
|
||||||
self.logger.info("disconnect_on_source show command received...")
|
self.logger.info("disconnect_on_source show command received...")
|
||||||
self.disconnect_source(self.logger, self.telnet_lock, m['sourcename'])
|
self.disconnect_source(self.logger, self.telnet_lock, m['sourcename'])
|
||||||
|
|
||||||
# update timeout value
|
# update timeout value
|
||||||
if command == 'update_schedule':
|
if command == 'update_schedule':
|
||||||
self.listener_timeout = 3600
|
self.listener_timeout = POLL_INTERVAL
|
||||||
else:
|
else:
|
||||||
self.listener_timeout = self.last_update_schedule_timestamp - time.time() + 3600
|
self.listener_timeout = self.last_update_schedule_timestamp - time.time() + POLL_INTERVAL
|
||||||
if self.listener_timeout < 0:
|
if self.listener_timeout < 0:
|
||||||
self.listener_timeout = 0
|
self.listener_timeout = 0
|
||||||
self.logger.info("New timeout: %s" % self.listener_timeout)
|
self.logger.info("New timeout: %s" % self.listener_timeout)
|
||||||
|
@ -115,7 +117,7 @@ class PypoFetch(Thread):
|
||||||
self.logger.error('Exception: %s', e)
|
self.logger.error('Exception: %s', e)
|
||||||
self.logger.error("traceback: %s", top)
|
self.logger.error("traceback: %s", top)
|
||||||
self.logger.error("Exception in handling Message Handler message: %s", e)
|
self.logger.error("Exception in handling Message Handler message: %s", e)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def disconnect_source(logger, lock, sourcename):
|
def disconnect_source(logger, lock, sourcename):
|
||||||
logger.debug('Disconnecting source: %s', sourcename)
|
logger.debug('Disconnecting source: %s', sourcename)
|
||||||
|
@ -124,7 +126,7 @@ class PypoFetch(Thread):
|
||||||
command += "master_harbor.kick\n"
|
command += "master_harbor.kick\n"
|
||||||
elif(sourcename == "live_dj"):
|
elif(sourcename == "live_dj"):
|
||||||
command += "live_dj_harbor.kick\n"
|
command += "live_dj_harbor.kick\n"
|
||||||
|
|
||||||
lock.acquire()
|
lock.acquire()
|
||||||
try:
|
try:
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
@ -135,7 +137,7 @@ class PypoFetch(Thread):
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
finally:
|
finally:
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def switch_source(logger, lock, sourcename, status):
|
def switch_source(logger, lock, sourcename, status):
|
||||||
logger.debug('Switching source: %s to "%s" status', sourcename, status)
|
logger.debug('Switching source: %s to "%s" status', sourcename, status)
|
||||||
|
@ -146,12 +148,12 @@ class PypoFetch(Thread):
|
||||||
command += "live_dj_"
|
command += "live_dj_"
|
||||||
elif(sourcename == "scheduled_play"):
|
elif(sourcename == "scheduled_play"):
|
||||||
command += "scheduled_play_"
|
command += "scheduled_play_"
|
||||||
|
|
||||||
if(status == "on"):
|
if(status == "on"):
|
||||||
command += "start\n"
|
command += "start\n"
|
||||||
else:
|
else:
|
||||||
command += "stop\n"
|
command += "stop\n"
|
||||||
|
|
||||||
lock.acquire()
|
lock.acquire()
|
||||||
try:
|
try:
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
@ -162,7 +164,7 @@ class PypoFetch(Thread):
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
finally:
|
finally:
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
grabs some information that are needed to be set on bootstrap time
|
grabs some information that are needed to be set on bootstrap time
|
||||||
and configures them
|
and configures them
|
||||||
|
@ -171,16 +173,16 @@ class PypoFetch(Thread):
|
||||||
self.logger.debug('Getting information needed on bootstrap from Airtime')
|
self.logger.debug('Getting information needed on bootstrap from Airtime')
|
||||||
info = self.api_client.get_bootstrap_info()
|
info = self.api_client.get_bootstrap_info()
|
||||||
if info == None:
|
if info == None:
|
||||||
self.logger.error('Unable to get bootstrap info.. Existing pypo...')
|
self.logger.error('Unable to get bootstrap info.. Exiting pypo...')
|
||||||
sys.exit(0)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
self.logger.debug('info:%s',info)
|
self.logger.debug('info:%s', info)
|
||||||
for k, v in info['switch_status'].iteritems():
|
for k, v in info['switch_status'].iteritems():
|
||||||
self.switch_source(self.logger, self.telnet_lock, k, v)
|
self.switch_source(self.logger, self.telnet_lock, k, v)
|
||||||
self.update_liquidsoap_stream_format(info['stream_label'])
|
self.update_liquidsoap_stream_format(info['stream_label'])
|
||||||
self.update_liquidsoap_station_name(info['station_name'])
|
self.update_liquidsoap_station_name(info['station_name'])
|
||||||
self.update_liquidsoap_transition_fade(info['transition_fade'])
|
self.update_liquidsoap_transition_fade(info['transition_fade'])
|
||||||
|
|
||||||
def write_liquidsoap_config(self, setting):
|
def write_liquidsoap_config(self, setting):
|
||||||
fh = open('/etc/airtime/liquidsoap.cfg', 'w')
|
fh = open('/etc/airtime/liquidsoap.cfg', 'w')
|
||||||
self.logger.info("Rewriting liquidsoap.cfg...")
|
self.logger.info("Rewriting liquidsoap.cfg...")
|
||||||
|
@ -197,7 +199,7 @@ class PypoFetch(Thread):
|
||||||
if temp == "":
|
if temp == "":
|
||||||
temp = "0"
|
temp = "0"
|
||||||
buffer_str += temp
|
buffer_str += temp
|
||||||
|
|
||||||
buffer_str += "\n"
|
buffer_str += "\n"
|
||||||
fh.write(api_client.encode_to(buffer_str))
|
fh.write(api_client.encode_to(buffer_str))
|
||||||
fh.write("log_file = \"/var/log/airtime/pypo-liquidsoap/<script>.log\"\n");
|
fh.write("log_file = \"/var/log/airtime/pypo-liquidsoap/<script>.log\"\n");
|
||||||
|
@ -206,18 +208,18 @@ class PypoFetch(Thread):
|
||||||
# we could just restart liquidsoap but it take more time somehow.
|
# we could just restart liquidsoap but it take more time somehow.
|
||||||
self.logger.info("Restarting pypo...")
|
self.logger.info("Restarting pypo...")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def regenerateLiquidsoapConf(self, setting):
|
def regenerateLiquidsoapConf(self, setting):
|
||||||
existing = {}
|
existing = {}
|
||||||
# create a temp file
|
# create a temp file
|
||||||
|
|
||||||
setting = sorted(setting.items())
|
setting = sorted(setting.items())
|
||||||
try:
|
try:
|
||||||
fh = open('/etc/airtime/liquidsoap.cfg', 'r')
|
fh = open('/etc/airtime/liquidsoap.cfg', 'r')
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
#file does not exist
|
#file does not exist
|
||||||
self.write_liquidsoap_config(setting)
|
self.write_liquidsoap_config(setting)
|
||||||
|
|
||||||
self.logger.info("Reading existing config...")
|
self.logger.info("Reading existing config...")
|
||||||
# read existing conf file and build dict
|
# read existing conf file and build dict
|
||||||
while True:
|
while True:
|
||||||
|
@ -226,9 +228,9 @@ class PypoFetch(Thread):
|
||||||
# empty line means EOF
|
# empty line means EOF
|
||||||
if not line:
|
if not line:
|
||||||
break
|
break
|
||||||
|
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
||||||
if line[0] == "#":
|
if line[0] == "#":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -243,7 +245,7 @@ class PypoFetch(Thread):
|
||||||
value = ''
|
value = ''
|
||||||
existing[key] = value
|
existing[key] = value
|
||||||
fh.close()
|
fh.close()
|
||||||
|
|
||||||
# dict flag for any change in cofig
|
# dict flag for any change in cofig
|
||||||
change = {}
|
change = {}
|
||||||
# this flag is to detect disable -> disable change
|
# this flag is to detect disable -> disable change
|
||||||
|
@ -251,7 +253,7 @@ class PypoFetch(Thread):
|
||||||
state_change_restart = {}
|
state_change_restart = {}
|
||||||
#restart flag
|
#restart flag
|
||||||
restart = False
|
restart = False
|
||||||
|
|
||||||
self.logger.info("Looking for changes...")
|
self.logger.info("Looking for changes...")
|
||||||
# look for changes
|
# look for changes
|
||||||
for k, s in setting:
|
for k, s in setting:
|
||||||
|
@ -267,13 +269,13 @@ class PypoFetch(Thread):
|
||||||
self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
|
self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
|
||||||
restart = True;
|
restart = True;
|
||||||
else:
|
else:
|
||||||
stream, dump = s[u'keyname'].split('_',1)
|
stream, dump = s[u'keyname'].split('_', 1)
|
||||||
if "_output" in s[u'keyname']:
|
if "_output" in s[u'keyname']:
|
||||||
if (existing[s[u'keyname']] != s[u'value']):
|
if (existing[s[u'keyname']] != s[u'value']):
|
||||||
self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
|
self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
|
||||||
restart = True;
|
restart = True;
|
||||||
state_change_restart[stream] = True
|
state_change_restart[stream] = True
|
||||||
elif ( s[u'value'] != 'disabled'):
|
elif (s[u'value'] != 'disabled'):
|
||||||
state_change_restart[stream] = True
|
state_change_restart[stream] = True
|
||||||
else:
|
else:
|
||||||
state_change_restart[stream] = False
|
state_change_restart[stream] = False
|
||||||
|
@ -284,10 +286,10 @@ class PypoFetch(Thread):
|
||||||
if not (s[u'value'] == existing[s[u'keyname']]):
|
if not (s[u'value'] == existing[s[u'keyname']]):
|
||||||
self.logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value'])
|
self.logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value'])
|
||||||
change[stream] = True
|
change[stream] = True
|
||||||
|
|
||||||
# set flag change for sound_device alway True
|
# set flag change for sound_device alway True
|
||||||
self.logger.info("Change:%s, State_Change:%s...", change, state_change_restart)
|
self.logger.info("Change:%s, State_Change:%s...", change, state_change_restart)
|
||||||
|
|
||||||
for k, v in state_change_restart.items():
|
for k, v in state_change_restart.items():
|
||||||
if k == "sound_device" and v:
|
if k == "sound_device" and v:
|
||||||
restart = True
|
restart = True
|
||||||
|
@ -306,7 +308,7 @@ class PypoFetch(Thread):
|
||||||
updates the status of liquidsoap connection to the streaming server
|
updates the status of liquidsoap connection to the streaming server
|
||||||
This fucntion updates the bootup time variable in liquidsoap script
|
This fucntion updates the bootup time variable in liquidsoap script
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
try:
|
try:
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
@ -314,25 +316,25 @@ class PypoFetch(Thread):
|
||||||
# we are manually adjusting the bootup time variable so the status msg will get
|
# we are manually adjusting the bootup time variable so the status msg will get
|
||||||
# updated.
|
# updated.
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
boot_up_time_command = "vars.bootup_time "+str(current_time)+"\n"
|
boot_up_time_command = "vars.bootup_time " + str(current_time) + "\n"
|
||||||
tn.write(boot_up_time_command)
|
tn.write(boot_up_time_command)
|
||||||
tn.write("streams.connection_status\n")
|
tn.write("streams.connection_status\n")
|
||||||
tn.write('exit\n')
|
tn.write('exit\n')
|
||||||
|
|
||||||
output = tn.read_all()
|
output = tn.read_all()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
finally:
|
finally:
|
||||||
self.telnet_lock.release()
|
self.telnet_lock.release()
|
||||||
|
|
||||||
output_list = output.split("\r\n")
|
output_list = output.split("\r\n")
|
||||||
stream_info = output_list[2]
|
stream_info = output_list[2]
|
||||||
|
|
||||||
# streamin info is in the form of:
|
# streamin info is in the form of:
|
||||||
# eg. s1:true,2:true,3:false
|
# eg. s1:true,2:true,3:false
|
||||||
streams = stream_info.split(",")
|
streams = stream_info.split(",")
|
||||||
self.logger.info(streams)
|
self.logger.info(streams)
|
||||||
|
|
||||||
fake_time = current_time + 1
|
fake_time = current_time + 1
|
||||||
for s in streams:
|
for s in streams:
|
||||||
info = s.split(':')
|
info = s.split(':')
|
||||||
|
@ -340,7 +342,7 @@ class PypoFetch(Thread):
|
||||||
status = info[1]
|
status = info[1]
|
||||||
if(status == "true"):
|
if(status == "true"):
|
||||||
self.api_client.notify_liquidsoap_status("OK", stream_id, str(fake_time))
|
self.api_client.notify_liquidsoap_status("OK", stream_id, str(fake_time))
|
||||||
|
|
||||||
def update_liquidsoap_stream_format(self, stream_format):
|
def update_liquidsoap_stream_format(self, stream_format):
|
||||||
# Push stream metadata to liquidsoap
|
# Push stream metadata to liquidsoap
|
||||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||||
|
@ -356,7 +358,7 @@ class PypoFetch(Thread):
|
||||||
self.logger.error("Exception %s", e)
|
self.logger.error("Exception %s", e)
|
||||||
finally:
|
finally:
|
||||||
self.telnet_lock.release()
|
self.telnet_lock.release()
|
||||||
|
|
||||||
def update_liquidsoap_transition_fade(self, fade):
|
def update_liquidsoap_transition_fade(self, fade):
|
||||||
# Push stream metadata to liquidsoap
|
# Push stream metadata to liquidsoap
|
||||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||||
|
@ -372,14 +374,14 @@ class PypoFetch(Thread):
|
||||||
self.logger.error("Exception %s", e)
|
self.logger.error("Exception %s", e)
|
||||||
finally:
|
finally:
|
||||||
self.telnet_lock.release()
|
self.telnet_lock.release()
|
||||||
|
|
||||||
def update_liquidsoap_station_name(self, station_name):
|
def update_liquidsoap_station_name(self, station_name):
|
||||||
# Push stream metadata to liquidsoap
|
# Push stream metadata to liquidsoap
|
||||||
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
|
||||||
try:
|
try:
|
||||||
self.logger.info(LS_HOST)
|
self.logger.info(LS_HOST)
|
||||||
self.logger.info(LS_PORT)
|
self.logger.info(LS_PORT)
|
||||||
|
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
try:
|
try:
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
@ -387,7 +389,7 @@ class PypoFetch(Thread):
|
||||||
self.logger.info(command)
|
self.logger.info(command)
|
||||||
tn.write(command)
|
tn.write(command)
|
||||||
tn.write('exit\n')
|
tn.write('exit\n')
|
||||||
tn.read_all()
|
tn.read_all()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
finally:
|
finally:
|
||||||
|
@ -403,7 +405,7 @@ class PypoFetch(Thread):
|
||||||
to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
|
to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
|
||||||
- runs the cleanup routine, to get rid of unused cached files
|
- runs the cleanup routine, to get rid of unused cached files
|
||||||
"""
|
"""
|
||||||
def process_schedule(self, schedule_data):
|
def process_schedule(self, schedule_data):
|
||||||
self.last_update_schedule_timestamp = time.time()
|
self.last_update_schedule_timestamp = time.time()
|
||||||
self.logger.debug(schedule_data)
|
self.logger.debug(schedule_data)
|
||||||
media = schedule_data["media"]
|
media = schedule_data["media"]
|
||||||
|
@ -411,7 +413,7 @@ class PypoFetch(Thread):
|
||||||
|
|
||||||
# Download all the media and put playlists in liquidsoap "annotate" format
|
# Download all the media and put playlists in liquidsoap "annotate" format
|
||||||
try:
|
try:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Make sure cache_dir exists
|
Make sure cache_dir exists
|
||||||
"""
|
"""
|
||||||
|
@ -420,15 +422,16 @@ class PypoFetch(Thread):
|
||||||
os.makedirs(download_dir)
|
os.makedirs(download_dir)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for key in media:
|
for key in media:
|
||||||
media_item = media[key]
|
media_item = media[key]
|
||||||
if(media_item['type'] == 'file'):
|
if(media_item['type'] == 'file'):
|
||||||
fileExt = os.path.splitext(media_item['uri'])[1]
|
fileExt = os.path.splitext(media_item['uri'])[1]
|
||||||
dst = os.path.join(download_dir, media_item['id']+fileExt)
|
dst = os.path.join(download_dir, media_item['id'] + fileExt)
|
||||||
media_item['dst'] = dst
|
media_item['dst'] = dst
|
||||||
|
media_item['started_copying'] = False
|
||||||
media_filtered[key] = media_item
|
media_filtered[key] = media_item
|
||||||
|
|
||||||
self.media_prepare_queue.put(copy.copy(media_filtered))
|
self.media_prepare_queue.put(copy.copy(media_filtered))
|
||||||
except Exception, e: self.logger.error("%s", e)
|
except Exception, e: self.logger.error("%s", e)
|
||||||
|
|
||||||
|
@ -440,7 +443,7 @@ class PypoFetch(Thread):
|
||||||
# cleanup
|
# cleanup
|
||||||
try: self.cache_cleanup(media)
|
try: self.cache_cleanup(media)
|
||||||
except Exception, e: self.logger.error("%s", e)
|
except Exception, e: self.logger.error("%s", e)
|
||||||
|
|
||||||
def cache_cleanup(self, media):
|
def cache_cleanup(self, media):
|
||||||
"""
|
"""
|
||||||
Get list of all files in the cache dir and remove them if they aren't being used anymore.
|
Get list of all files in the cache dir and remove them if they aren't being used anymore.
|
||||||
|
@ -449,18 +452,18 @@ class PypoFetch(Thread):
|
||||||
"""
|
"""
|
||||||
cached_file_set = set(os.listdir(self.cache_dir))
|
cached_file_set = set(os.listdir(self.cache_dir))
|
||||||
scheduled_file_set = set()
|
scheduled_file_set = set()
|
||||||
|
|
||||||
for mkey in media:
|
for mkey in media:
|
||||||
media_item = media[mkey]
|
media_item = media[mkey]
|
||||||
fileExt = os.path.splitext(media_item['uri'])[1]
|
fileExt = os.path.splitext(media_item['uri'])[1]
|
||||||
scheduled_file_set.add(media_item["id"] + fileExt)
|
scheduled_file_set.add(media_item["id"] + fileExt)
|
||||||
|
|
||||||
unneeded_files = cached_file_set - scheduled_file_set
|
unneeded_files = cached_file_set - scheduled_file_set
|
||||||
|
|
||||||
self.logger.debug("Files to remove " + str(unneeded_files))
|
self.logger.debug("Files to remove " + str(unneeded_files))
|
||||||
for file in unneeded_files:
|
for f in unneeded_files:
|
||||||
self.logger.debug("Removing %s" % os.path.join(self.cache_dir, file))
|
self.logger.debug("Removing %s" % os.path.join(self.cache_dir, f))
|
||||||
os.remove(os.path.join(self.cache_dir, file))
|
os.remove(os.path.join(self.cache_dir, f))
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
# Bootstrap: since we are just starting up, we need to grab the
|
# Bootstrap: since we are just starting up, we need to grab the
|
||||||
|
@ -471,10 +474,10 @@ class PypoFetch(Thread):
|
||||||
self.process_schedule(self.schedule_data)
|
self.process_schedule(self.schedule_data)
|
||||||
self.set_bootstrap_variables()
|
self.set_bootstrap_variables()
|
||||||
|
|
||||||
loops = 1
|
loops = 1
|
||||||
while True:
|
while True:
|
||||||
self.logger.info("Loop #%s", loops)
|
self.logger.info("Loop #%s", loops)
|
||||||
try:
|
try:
|
||||||
"""
|
"""
|
||||||
our simple_queue.get() requires a timeout, in which case we
|
our simple_queue.get() requires a timeout, in which case we
|
||||||
fetch the Airtime schedule manually. It is important to fetch
|
fetch the Airtime schedule manually. It is important to fetch
|
||||||
|
@ -486,18 +489,20 @@ class PypoFetch(Thread):
|
||||||
sent, and we will have very stale (or non-existent!) data about the
|
sent, and we will have very stale (or non-existent!) data about the
|
||||||
schedule.
|
schedule.
|
||||||
|
|
||||||
Currently we are checking every 3600 seconds (1 hour)
|
Currently we are checking every POLL_INTERVAL seconds
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
message = self.fetch_queue.get(block=True, timeout=self.listener_timeout)
|
message = self.fetch_queue.get(block=True, timeout=self.listener_timeout)
|
||||||
self.handle_message(message)
|
self.handle_message(message)
|
||||||
|
except Empty, e:
|
||||||
|
self.logger.info("Queue timeout. Fetching schedule manually")
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
import traceback
|
import traceback
|
||||||
top = traceback.format_exc()
|
top = traceback.format_exc()
|
||||||
self.logger.error('Exception: %s', e)
|
self.logger.error('Exception: %s', e)
|
||||||
self.logger.error("traceback: %s", top)
|
self.logger.error("traceback: %s", top)
|
||||||
|
|
||||||
success, self.schedule_data = self.api_client.get_schedule()
|
success, self.schedule_data = self.api_client.get_schedule()
|
||||||
if success:
|
if success:
|
||||||
self.process_schedule(self.schedule_data)
|
self.process_schedule(self.schedule_data)
|
||||||
|
|
|
@ -5,10 +5,10 @@ from Queue import Empty
|
||||||
from configobj import ConfigObj
|
from configobj import ConfigObj
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
|
||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import stat
|
||||||
|
|
||||||
from std_err_override import LogWriter
|
from std_err_override import LogWriter
|
||||||
|
|
||||||
|
@ -33,21 +33,21 @@ except Exception, e:
|
||||||
|
|
||||||
|
|
||||||
class PypoFile(Thread):
|
class PypoFile(Thread):
|
||||||
|
|
||||||
def __init__(self, schedule_queue):
|
def __init__(self, schedule_queue):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.logger = logging.getLogger()
|
self.logger = logging.getLogger()
|
||||||
self.media_queue = schedule_queue
|
self.media_queue = schedule_queue
|
||||||
self.media = None
|
self.media = None
|
||||||
self.cache_dir = os.path.join(config["cache_dir"], "scheduler")
|
self.cache_dir = os.path.join(config["cache_dir"], "scheduler")
|
||||||
|
|
||||||
def copy_file(self, media_item):
|
def copy_file(self, media_item):
|
||||||
"""
|
"""
|
||||||
Copy media_item from local library directory to local cache directory.
|
Copy media_item from local library directory to local cache directory.
|
||||||
"""
|
"""
|
||||||
src = media_item['uri']
|
src = media_item['uri']
|
||||||
dst = media_item['dst']
|
dst = media_item['dst']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
src_size = os.path.getsize(src)
|
src_size = os.path.getsize(src)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
@ -59,24 +59,31 @@ class PypoFile(Thread):
|
||||||
dst_size = os.path.getsize(dst)
|
dst_size = os.path.getsize(dst)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
dst_exists = False
|
dst_exists = False
|
||||||
|
|
||||||
do_copy = False
|
do_copy = False
|
||||||
if dst_exists:
|
if dst_exists:
|
||||||
if src_size != dst_size:
|
if src_size != dst_size:
|
||||||
do_copy = True
|
do_copy = True
|
||||||
else:
|
else:
|
||||||
do_copy = True
|
do_copy = True
|
||||||
|
|
||||||
if do_copy:
|
if do_copy:
|
||||||
self.logger.debug("copying from %s to local cache %s" % (src, dst))
|
self.logger.debug("copying from %s to local cache %s" % (src, dst))
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
media_item['started_copying'] = True
|
||||||
|
|
||||||
"""
|
"""
|
||||||
copy will overwrite dst if it already exists
|
copy will overwrite dst if it already exists
|
||||||
"""
|
"""
|
||||||
shutil.copy(src, dst)
|
shutil.copy(src, dst)
|
||||||
except:
|
|
||||||
|
#make file world readable
|
||||||
|
os.chmod(dst, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
|
||||||
|
except Exception, e:
|
||||||
self.logger.error("Could not copy from %s to %s" % (src, dst))
|
self.logger.error("Could not copy from %s to %s" % (src, dst))
|
||||||
|
self.logger.error(e)
|
||||||
|
|
||||||
def get_highest_priority_media_item(self, schedule):
|
def get_highest_priority_media_item(self, schedule):
|
||||||
"""
|
"""
|
||||||
Get highest priority media_item in the queue. Currently the highest
|
Get highest priority media_item in the queue. Currently the highest
|
||||||
|
@ -84,17 +91,17 @@ class PypoFile(Thread):
|
||||||
"""
|
"""
|
||||||
if schedule is None or len(schedule) == 0:
|
if schedule is None or len(schedule) == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sorted_keys = sorted(schedule.keys())
|
sorted_keys = sorted(schedule.keys())
|
||||||
|
|
||||||
if len(sorted_keys) == 0:
|
if len(sorted_keys) == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
highest_priority = sorted_keys[0]
|
highest_priority = sorted_keys[0]
|
||||||
media_item = schedule[highest_priority]
|
media_item = schedule[highest_priority]
|
||||||
|
|
||||||
self.logger.debug("Highest priority item: %s" % highest_priority)
|
self.logger.debug("Highest priority item: %s" % highest_priority)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Remove this media_item from the dictionary. On the next iteration
|
Remove this media_item from the dictionary. On the next iteration
|
||||||
(from the main function) we won't consider it for prioritization
|
(from the main function) we won't consider it for prioritization
|
||||||
|
@ -103,11 +110,11 @@ class PypoFile(Thread):
|
||||||
again. In this situation, the worst possible case is that we try to
|
again. In this situation, the worst possible case is that we try to
|
||||||
copy the file again and realize we already have it (thus aborting the copy).
|
copy the file again and realize we already have it (thus aborting the copy).
|
||||||
"""
|
"""
|
||||||
del schedule[highest_priority]
|
del schedule[highest_priority]
|
||||||
|
|
||||||
return media_item
|
return media_item
|
||||||
|
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -128,7 +135,7 @@ class PypoFile(Thread):
|
||||||
self.media = self.media_queue.get_nowait()
|
self.media = self.media_queue.get_nowait()
|
||||||
except Empty, e:
|
except Empty, e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
media_item = self.get_highest_priority_media_item(self.media)
|
media_item = self.get_highest_priority_media_item(self.media)
|
||||||
if media_item is not None:
|
if media_item is not None:
|
||||||
|
@ -139,7 +146,7 @@ class PypoFile(Thread):
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
self.logger.error(top)
|
self.logger.error(top)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
Entry point of the thread
|
Entry point of the thread
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
|
||||||
import sys
|
import sys
|
||||||
from configobj import ConfigObj
|
from configobj import ConfigObj
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
@ -39,7 +38,7 @@ class PypoMessageHandler(Thread):
|
||||||
self.logger = logging.getLogger('message_h')
|
self.logger = logging.getLogger('message_h')
|
||||||
self.pypo_queue = pq
|
self.pypo_queue = pq
|
||||||
self.recorder_queue = rq
|
self.recorder_queue = rq
|
||||||
|
|
||||||
def init_rabbit_mq(self):
|
def init_rabbit_mq(self):
|
||||||
self.logger.info("Initializing RabbitMQ stuff")
|
self.logger.info("Initializing RabbitMQ stuff")
|
||||||
try:
|
try:
|
||||||
|
@ -51,21 +50,21 @@ class PypoMessageHandler(Thread):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Handle a message from RabbitMQ, put it into our yucky global var.
|
Handle a message from RabbitMQ, put it into our yucky global var.
|
||||||
Hopefully there is a better way to do this.
|
Hopefully there is a better way to do this.
|
||||||
"""
|
"""
|
||||||
def handle_message(self, message):
|
def handle_message(self, message):
|
||||||
try:
|
try:
|
||||||
self.logger.info("Received event from RabbitMQ: %s" % message)
|
self.logger.info("Received event from RabbitMQ: %s" % message)
|
||||||
|
|
||||||
m = json.loads(message)
|
m = json.loads(message)
|
||||||
command = m['event_type']
|
command = m['event_type']
|
||||||
self.logger.info("Handling command: " + command)
|
self.logger.info("Handling command: " + command)
|
||||||
|
|
||||||
if command == 'update_schedule':
|
if command == 'update_schedule':
|
||||||
self.logger.info("Updating schdule...")
|
self.logger.info("Updating schdule...")
|
||||||
self.pypo_queue.put(message)
|
self.pypo_queue.put(message)
|
||||||
|
@ -121,15 +120,13 @@ class PypoMessageHandler(Thread):
|
||||||
while loop and eat all the CPU
|
while loop and eat all the CPU
|
||||||
"""
|
"""
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
There is a problem with the RabbitMq messenger service. Let's
|
There is a problem with the RabbitMq messenger service. Let's
|
||||||
log the error and get the schedule via HTTP polling
|
log the error and get the schedule via HTTP polling
|
||||||
"""
|
"""
|
||||||
import traceback
|
|
||||||
top = traceback.format_exc()
|
|
||||||
self.logger.error('Exception: %s', e)
|
self.logger.error('Exception: %s', e)
|
||||||
self.logger.error("traceback: %s", top)
|
self.logger.error("traceback: %s", traceback.format_exc())
|
||||||
|
|
||||||
loops += 1
|
loops += 1
|
||||||
|
|
||||||
|
|
|
@ -15,19 +15,9 @@ Main case:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# python defaults (debian default)
|
from optparse import OptionParser
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import traceback
|
|
||||||
from optparse import *
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
import datetime
|
|
||||||
import logging
|
|
||||||
import logging.config
|
import logging.config
|
||||||
import urllib
|
|
||||||
import urllib2
|
|
||||||
import string
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# additional modules (should be checked)
|
# additional modules (should be checked)
|
||||||
|
@ -38,9 +28,6 @@ from configobj import ConfigObj
|
||||||
from api_clients import *
|
from api_clients import *
|
||||||
from std_err_override import LogWriter
|
from std_err_override import LogWriter
|
||||||
|
|
||||||
# Set up command-line options
|
|
||||||
parser = OptionParser()
|
|
||||||
|
|
||||||
# help screeen / info
|
# help screeen / info
|
||||||
usage = "%prog [options]" + " - notification gateway"
|
usage = "%prog [options]" + " - notification gateway"
|
||||||
parser = OptionParser(usage=usage)
|
parser = OptionParser(usage=usage)
|
||||||
|
@ -59,8 +46,8 @@ parser.add_option("-y", "--source-status", help="source connection stauts", meta
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
# configure logging
|
# configure logging
|
||||||
logging.config.fileConfig("logging.cfg")
|
logging.config.fileConfig("notify_logging.cfg")
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger('notify')
|
||||||
LogWriter.override_std_err(logger)
|
LogWriter.override_std_err(logger)
|
||||||
|
|
||||||
#need to wait for Python 2.7 for this..
|
#need to wait for Python 2.7 for this..
|
||||||
|
@ -69,54 +56,54 @@ LogWriter.override_std_err(logger)
|
||||||
# loading config file
|
# loading config file
|
||||||
try:
|
try:
|
||||||
config = ConfigObj('/etc/airtime/pypo.cfg')
|
config = ConfigObj('/etc/airtime/pypo.cfg')
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logger.error('Error loading config file: %s', e)
|
logger.error('Error loading config file: %s', e)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
class Notify:
|
class Notify:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.api_client = api_client.api_client_factory(config)
|
self.api_client = api_client.api_client_factory(config)
|
||||||
|
|
||||||
def notify_media_start_playing(self, data, media_id):
|
def notify_media_start_playing(self, data, media_id):
|
||||||
logger = logging.getLogger("notify")
|
logger = logging.getLogger("notify")
|
||||||
|
|
||||||
logger.debug('#################################################')
|
logger.debug('#################################################')
|
||||||
logger.debug('# Calling server to update about what\'s playing #')
|
logger.debug('# Calling server to update about what\'s playing #')
|
||||||
logger.debug('#################################################')
|
logger.debug('#################################################')
|
||||||
logger.debug('data = '+ str(data))
|
logger.debug('data = ' + str(data))
|
||||||
response = self.api_client.notify_media_item_start_playing(data, media_id)
|
response = self.api_client.notify_media_item_start_playing(data, media_id)
|
||||||
logger.debug("Response: "+json.dumps(response))
|
logger.debug("Response: " + json.dumps(response))
|
||||||
|
|
||||||
# @pram time: time that LS started
|
# @pram time: time that LS started
|
||||||
def notify_liquidsoap_status(self, msg, stream_id, time):
|
def notify_liquidsoap_status(self, msg, stream_id, time):
|
||||||
logger = logging.getLogger("notify")
|
logger = logging.getLogger("notify")
|
||||||
|
|
||||||
logger.debug('#################################################')
|
logger.debug('#################################################')
|
||||||
logger.debug('# Calling server to update liquidsoap status #')
|
logger.debug('# Calling server to update liquidsoap status #')
|
||||||
logger.debug('#################################################')
|
logger.debug('#################################################')
|
||||||
logger.debug('msg = '+ str(msg))
|
logger.debug('msg = ' + str(msg))
|
||||||
response = self.api_client.notify_liquidsoap_status(msg, stream_id, time)
|
response = self.api_client.notify_liquidsoap_status(msg, stream_id, time)
|
||||||
logger.debug("Response: "+json.dumps(response))
|
logger.debug("Response: " + json.dumps(response))
|
||||||
|
|
||||||
def notify_source_status(self, source_name, status):
|
def notify_source_status(self, source_name, status):
|
||||||
logger = logging.getLogger("notify")
|
logger = logging.getLogger("notify")
|
||||||
|
|
||||||
logger.debug('#################################################')
|
logger.debug('#################################################')
|
||||||
logger.debug('# Calling server to update source status #')
|
logger.debug('# Calling server to update source status #')
|
||||||
logger.debug('#################################################')
|
logger.debug('#################################################')
|
||||||
logger.debug('msg = '+ str(source_name) + ' : ' + str(status))
|
logger.debug('msg = ' + str(source_name) + ' : ' + str(status))
|
||||||
response = self.api_client.notify_source_status(source_name, status)
|
response = self.api_client.notify_source_status(source_name, status)
|
||||||
logger.debug("Response: "+json.dumps(response))
|
logger.debug("Response: " + json.dumps(response))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print
|
print
|
||||||
print '#########################################'
|
print '#########################################'
|
||||||
print '# *** pypo *** #'
|
print '# *** pypo *** #'
|
||||||
print '# pypo notification gateway #'
|
print '# pypo notification gateway #'
|
||||||
print '#########################################'
|
print '#########################################'
|
||||||
|
|
||||||
# initialize
|
# initialize
|
||||||
logger = logging.getLogger("notify")
|
logger = logging.getLogger("notify")
|
||||||
if options.error and options.stream_id:
|
if options.error and options.stream_id:
|
||||||
|
@ -141,11 +128,11 @@ if __name__ == '__main__':
|
||||||
if not options.data:
|
if not options.data:
|
||||||
print "NOTICE: 'data' command-line argument not given."
|
print "NOTICE: 'data' command-line argument not given."
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
if not options.media_id:
|
if not options.media_id:
|
||||||
print "NOTICE: 'media_id' command-line argument not given."
|
print "NOTICE: 'media_id' command-line argument not given."
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
n = Notify()
|
n = Notify()
|
||||||
n.notify_media_start_playing(options.data, options.media_id)
|
n.notify_media_start_playing(options.data, options.media_id)
|
|
@ -5,11 +5,9 @@ from datetime import timedelta
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
|
||||||
import logging.config
|
import logging.config
|
||||||
import telnetlib
|
import telnetlib
|
||||||
import calendar
|
import calendar
|
||||||
import json
|
|
||||||
import math
|
import math
|
||||||
from pypofetch import PypoFetch
|
from pypofetch import PypoFetch
|
||||||
|
|
||||||
|
@ -51,52 +49,58 @@ class PypoPush(Thread):
|
||||||
|
|
||||||
self.pushed_objects = {}
|
self.pushed_objects = {}
|
||||||
self.logger = logging.getLogger('push')
|
self.logger = logging.getLogger('push')
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
loops = 0
|
loops = 0
|
||||||
heartbeat_period = math.floor(30/PUSH_INTERVAL)
|
heartbeat_period = math.floor(30 / PUSH_INTERVAL)
|
||||||
|
|
||||||
next_media_item_chain = None
|
next_media_item_chain = None
|
||||||
media_schedule = None
|
media_schedule = None
|
||||||
time_until_next_play = None
|
time_until_next_play = None
|
||||||
chains = None
|
chains = None
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
if time_until_next_play is None:
|
if time_until_next_play is None:
|
||||||
media_schedule = self.queue.get(block=True)
|
media_schedule = self.queue.get(block=True)
|
||||||
else:
|
else:
|
||||||
media_schedule = self.queue.get(block=True, timeout=time_until_next_play)
|
media_schedule = self.queue.get(block=True, timeout=time_until_next_play)
|
||||||
|
|
||||||
chains = self.get_all_chains(media_schedule)
|
chains = self.get_all_chains(media_schedule)
|
||||||
|
|
||||||
#We get to the following lines only if a schedule was received.
|
#We get to the following lines only if a schedule was received.
|
||||||
liquidsoap_queue_approx = self.get_queue_items_from_liquidsoap()
|
liquidsoap_queue_approx = self.get_queue_items_from_liquidsoap()
|
||||||
|
|
||||||
current_event_chain = self.get_current_chain(chains)
|
tnow = datetime.utcnow()
|
||||||
|
current_event_chain, original_chain = self.get_current_chain(chains, tnow)
|
||||||
if len(current_event_chain) > 0 and len(liquidsoap_queue_approx) == 0:
|
if len(current_event_chain) > 0 and len(liquidsoap_queue_approx) == 0:
|
||||||
#Something is scheduled but Liquidsoap is not playing anything!
|
#Something is scheduled but Liquidsoap is not playing anything!
|
||||||
#Need to schedule it immediately..this might happen if Liquidsoap crashed.
|
#Need to schedule it immediately..this might happen if Liquidsoap crashed.
|
||||||
chains.remove(current_event_chain)
|
try:
|
||||||
|
chains.remove(original_chain)
|
||||||
|
except ValueError, e:
|
||||||
|
self.logger.error(str(e))
|
||||||
|
|
||||||
self.modify_cue_point(current_event_chain[0])
|
self.modify_cue_point(current_event_chain[0])
|
||||||
next_media_item_chain = current_event_chain
|
next_media_item_chain = current_event_chain
|
||||||
time_until_next_play = 0
|
time_until_next_play = 0
|
||||||
#sleep for 0.2 seconds to give pypo-file time to copy. This is a quick
|
#sleep for 0.2 seconds to give pypo-file time to copy.
|
||||||
#fix that will be improved in 2.1.1
|
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
else:
|
else:
|
||||||
media_chain = filter(lambda item: (item["type"] == "file"), current_event_chain)
|
media_chain = filter(lambda item: (item["type"] == "file"), current_event_chain)
|
||||||
self.handle_new_media_schedule(media_schedule, liquidsoap_queue_approx, media_chain)
|
self.handle_new_media_schedule(media_schedule, liquidsoap_queue_approx, media_chain)
|
||||||
|
|
||||||
next_media_item_chain = self.get_next_schedule_chain(chains)
|
next_media_item_chain = self.get_next_schedule_chain(chains, tnow)
|
||||||
|
|
||||||
self.logger.debug("Next schedule chain: %s", next_media_item_chain)
|
self.logger.debug("Next schedule chain: %s", next_media_item_chain)
|
||||||
if next_media_item_chain is not None:
|
if next_media_item_chain is not None:
|
||||||
chains.remove(next_media_item_chain)
|
try:
|
||||||
tnow = datetime.utcnow()
|
chains.remove(next_media_item_chain)
|
||||||
|
except ValueError, e:
|
||||||
|
self.logger.error(str(e))
|
||||||
|
|
||||||
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
time_until_next_play = self.date_interval_to_seconds(chain_start - tnow)
|
time_until_next_play = self.date_interval_to_seconds(chain_start - datetime.utcnow())
|
||||||
self.logger.debug("Blocking %s seconds until show start", time_until_next_play)
|
self.logger.debug("Blocking %s seconds until show start", time_until_next_play)
|
||||||
else:
|
else:
|
||||||
self.logger.debug("Blocking indefinitely since no show scheduled")
|
self.logger.debug("Blocking indefinitely since no show scheduled")
|
||||||
|
@ -104,17 +108,18 @@ class PypoPush(Thread):
|
||||||
except Empty, e:
|
except Empty, e:
|
||||||
#We only get here when a new chain of tracks are ready to be played.
|
#We only get here when a new chain of tracks are ready to be played.
|
||||||
self.push_to_liquidsoap(next_media_item_chain)
|
self.push_to_liquidsoap(next_media_item_chain)
|
||||||
|
|
||||||
next_media_item_chain = self.get_next_schedule_chain(chains)
|
next_media_item_chain = self.get_next_schedule_chain(chains, datetime.utcnow())
|
||||||
if next_media_item_chain is not None:
|
if next_media_item_chain is not None:
|
||||||
tnow = datetime.utcnow()
|
|
||||||
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
time_until_next_play = self.date_interval_to_seconds(chain_start - tnow)
|
time_until_next_play = self.date_interval_to_seconds(chain_start - datetime.utcnow())
|
||||||
self.logger.debug("Blocking %s seconds until show start", time_until_next_play)
|
self.logger.debug("Blocking %s seconds until show start", time_until_next_play)
|
||||||
else:
|
else:
|
||||||
self.logger.debug("Blocking indefinitely since no show scheduled next")
|
self.logger.debug("Blocking indefinitely since no show scheduled next")
|
||||||
time_until_next_play = None
|
time_until_next_play = None
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error(str(e))
|
||||||
|
|
||||||
if loops % heartbeat_period == 0:
|
if loops % heartbeat_period == 0:
|
||||||
self.logger.info("heartbeat")
|
self.logger.info("heartbeat")
|
||||||
loops = 0
|
loops = 0
|
||||||
|
@ -127,7 +132,7 @@ class PypoPush(Thread):
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
|
||||||
msg = 'queue.queue\n'
|
msg = 'queue.queue\n'
|
||||||
tn.write(msg)
|
tn.write(msg)
|
||||||
response = tn.read_until("\r\n").strip(" \r\n")
|
response = tn.read_until("\r\n").strip(" \r\n")
|
||||||
|
@ -138,14 +143,14 @@ class PypoPush(Thread):
|
||||||
response = []
|
response = []
|
||||||
finally:
|
finally:
|
||||||
self.telnet_lock.release()
|
self.telnet_lock.release()
|
||||||
|
|
||||||
liquidsoap_queue_approx = []
|
liquidsoap_queue_approx = []
|
||||||
|
|
||||||
if len(response) > 0:
|
if len(response) > 0:
|
||||||
items_in_queue = response.split(" ")
|
items_in_queue = response.split(" ")
|
||||||
|
|
||||||
self.logger.debug("items_in_queue: %s", items_in_queue)
|
self.logger.debug("items_in_queue: %s", items_in_queue)
|
||||||
|
|
||||||
for item in items_in_queue:
|
for item in items_in_queue:
|
||||||
if item in self.pushed_objects:
|
if item in self.pushed_objects:
|
||||||
liquidsoap_queue_approx.append(self.pushed_objects[item])
|
liquidsoap_queue_approx.append(self.pushed_objects[item])
|
||||||
|
@ -158,9 +163,9 @@ class PypoPush(Thread):
|
||||||
self.clear_liquidsoap_queue()
|
self.clear_liquidsoap_queue()
|
||||||
liquidsoap_queue_approx = []
|
liquidsoap_queue_approx = []
|
||||||
break
|
break
|
||||||
|
|
||||||
return liquidsoap_queue_approx
|
return liquidsoap_queue_approx
|
||||||
|
|
||||||
def handle_new_media_schedule(self, media_schedule, liquidsoap_queue_approx, media_chain):
|
def handle_new_media_schedule(self, media_schedule, liquidsoap_queue_approx, media_chain):
|
||||||
"""
|
"""
|
||||||
This function's purpose is to gracefully handle situations where
|
This function's purpose is to gracefully handle situations where
|
||||||
|
@ -169,27 +174,27 @@ class PypoPush(Thread):
|
||||||
call other functions that will connect to Liquidsoap and alter its
|
call other functions that will connect to Liquidsoap and alter its
|
||||||
queue.
|
queue.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
problem_at_iteration, problem_start_time = self.find_removed_items(media_schedule, liquidsoap_queue_approx)
|
problem_at_iteration = self.find_removed_items(media_schedule, liquidsoap_queue_approx)
|
||||||
|
|
||||||
if problem_at_iteration is not None:
|
if problem_at_iteration is not None:
|
||||||
#Items that are in Liquidsoap's queue aren't scheduled anymore. We need to connect
|
#Items that are in Liquidsoap's queue aren't scheduled anymore. We need to connect
|
||||||
#and remove these items.
|
#and remove these items.
|
||||||
self.logger.debug("Change in link %s of current chain", problem_at_iteration)
|
self.logger.debug("Change in link %s of current chain", problem_at_iteration)
|
||||||
self.remove_from_liquidsoap_queue(problem_at_iteration, liquidsoap_queue_approx[problem_at_iteration:])
|
self.remove_from_liquidsoap_queue(problem_at_iteration, liquidsoap_queue_approx[problem_at_iteration:])
|
||||||
|
|
||||||
if problem_at_iteration is None and len(media_chain) > len(liquidsoap_queue_approx):
|
if problem_at_iteration is None and len(media_chain) > len(liquidsoap_queue_approx):
|
||||||
self.logger.debug("New schedule has longer current chain.")
|
self.logger.debug("New schedule has longer current chain.")
|
||||||
problem_at_iteration = len(liquidsoap_queue_approx)
|
problem_at_iteration = len(liquidsoap_queue_approx)
|
||||||
|
|
||||||
if problem_at_iteration is not None:
|
if problem_at_iteration is not None:
|
||||||
self.logger.debug("Change in chain at link %s", problem_at_iteration)
|
self.logger.debug("Change in chain at link %s", problem_at_iteration)
|
||||||
|
|
||||||
chain_to_push = media_chain[problem_at_iteration:]
|
chain_to_push = media_chain[problem_at_iteration:]
|
||||||
if len(chain_to_push) > 0:
|
if len(chain_to_push) > 0:
|
||||||
self.modify_cue_point(chain_to_push[0])
|
self.modify_cue_point(chain_to_push[0])
|
||||||
self.push_to_liquidsoap(chain_to_push)
|
self.push_to_liquidsoap(chain_to_push)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Compare whats in the liquidsoap_queue to the new schedule we just
|
Compare whats in the liquidsoap_queue to the new schedule we just
|
||||||
received in media_schedule. This function only iterates over liquidsoap_queue_approx
|
received in media_schedule. This function only iterates over liquidsoap_queue_approx
|
||||||
|
@ -201,7 +206,6 @@ class PypoPush(Thread):
|
||||||
#see if they are the same as the newly received schedule
|
#see if they are the same as the newly received schedule
|
||||||
iteration = 0
|
iteration = 0
|
||||||
problem_at_iteration = None
|
problem_at_iteration = None
|
||||||
problem_start_time = None
|
|
||||||
for queue_item in liquidsoap_queue_approx:
|
for queue_item in liquidsoap_queue_approx:
|
||||||
if queue_item['start'] in media_schedule.keys():
|
if queue_item['start'] in media_schedule.keys():
|
||||||
media_item = media_schedule[queue_item['start']]
|
media_item = media_schedule[queue_item['start']]
|
||||||
|
@ -211,33 +215,30 @@ class PypoPush(Thread):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
problem_at_iteration = iteration
|
problem_at_iteration = iteration
|
||||||
problem_start_time = queue_item['start']
|
break
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
#A different item has been scheduled at the same time! Need to remove
|
#A different item has been scheduled at the same time! Need to remove
|
||||||
#all tracks from the Liquidsoap queue starting at this point, and re-add
|
#all tracks from the Liquidsoap queue starting at this point, and re-add
|
||||||
#them.
|
#them.
|
||||||
problem_at_iteration = iteration
|
problem_at_iteration = iteration
|
||||||
problem_start_time = queue_item['start']
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
#There are no more items scheduled for this time! The user has shortened
|
#There are no more items scheduled for this time! The user has shortened
|
||||||
#the playlist, so we simply need to remove tracks from the queue.
|
#the playlist, so we simply need to remove tracks from the queue.
|
||||||
problem_at_iteration = iteration
|
problem_at_iteration = iteration
|
||||||
problem_start_time = queue_item['start']
|
|
||||||
break
|
break
|
||||||
iteration+=1
|
iteration += 1
|
||||||
return (problem_at_iteration, problem_start_time)
|
return problem_at_iteration
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_chains(self, media_schedule):
|
def get_all_chains(self, media_schedule):
|
||||||
chains = []
|
chains = []
|
||||||
|
|
||||||
current_chain = []
|
current_chain = []
|
||||||
|
|
||||||
sorted_keys = sorted(media_schedule.keys())
|
sorted_keys = sorted(media_schedule.keys())
|
||||||
|
|
||||||
for mkey in sorted_keys:
|
for mkey in sorted_keys:
|
||||||
media_item = media_schedule[mkey]
|
media_item = media_schedule[mkey]
|
||||||
if media_item['type'] == "event":
|
if media_item['type'] == "event":
|
||||||
|
@ -251,54 +252,68 @@ class PypoPush(Thread):
|
||||||
#Start a new one instead
|
#Start a new one instead
|
||||||
chains.append(current_chain)
|
chains.append(current_chain)
|
||||||
current_chain = [media_item]
|
current_chain = [media_item]
|
||||||
|
|
||||||
if len(current_chain) > 0:
|
if len(current_chain) > 0:
|
||||||
chains.append(current_chain)
|
chains.append(current_chain)
|
||||||
|
|
||||||
return chains
|
return chains
|
||||||
|
|
||||||
def modify_cue_point(self, link):
|
def modify_cue_point(self, link):
|
||||||
tnow = datetime.utcnow()
|
tnow = datetime.utcnow()
|
||||||
|
|
||||||
link_start = datetime.strptime(link['start'], "%Y-%m-%d-%H-%M-%S")
|
link_start = datetime.strptime(link['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
diff_td = tnow - link_start
|
diff_td = tnow - link_start
|
||||||
diff_sec = self.date_interval_to_seconds(diff_td)
|
diff_sec = self.date_interval_to_seconds(diff_td)
|
||||||
|
|
||||||
if diff_sec > 0:
|
if diff_sec > 0:
|
||||||
self.logger.debug("media item was supposed to start %s ago. Preparing to start..", diff_sec)
|
self.logger.debug("media item was supposed to start %s ago. Preparing to start..", diff_sec)
|
||||||
original_cue_in_td = timedelta(seconds=float(link['cue_in']))
|
original_cue_in_td = timedelta(seconds=float(link['cue_in']))
|
||||||
link['cue_in'] = self.date_interval_to_seconds(original_cue_in_td) + diff_sec
|
link['cue_in'] = self.date_interval_to_seconds(original_cue_in_td) + diff_sec
|
||||||
|
|
||||||
|
"""
|
||||||
|
Returns two chains, original chain and current_chain. current_chain is a subset of
|
||||||
|
original_chain but can also be equal to original chain.
|
||||||
|
|
||||||
|
We return original chain because the user of this function may want to clean
|
||||||
|
up the input 'chains' list
|
||||||
|
|
||||||
def get_current_chain(self, chains):
|
chain, original = get_current_chain(chains)
|
||||||
tnow = datetime.utcnow()
|
|
||||||
|
and
|
||||||
|
chains.remove(chain) can throw a ValueError exception
|
||||||
|
|
||||||
|
but
|
||||||
|
chains.remove(original) won't
|
||||||
|
"""
|
||||||
|
def get_current_chain(self, chains, tnow):
|
||||||
current_chain = []
|
current_chain = []
|
||||||
|
original_chain = None
|
||||||
|
|
||||||
for chain in chains:
|
for chain in chains:
|
||||||
iteration = 0
|
iteration = 0
|
||||||
for link in chain:
|
for link in chain:
|
||||||
link_start = datetime.strptime(link['start'], "%Y-%m-%d-%H-%M-%S")
|
link_start = datetime.strptime(link['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
link_end = datetime.strptime(link['end'], "%Y-%m-%d-%H-%M-%S")
|
link_end = datetime.strptime(link['end'], "%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
self.logger.debug("tnow %s, chain_start %s", tnow, link_start)
|
self.logger.debug("tnow %s, chain_start %s", tnow, link_start)
|
||||||
if link_start <= tnow and tnow < link_end:
|
if link_start <= tnow and tnow < link_end:
|
||||||
current_chain = chain[iteration:]
|
current_chain = chain[iteration:]
|
||||||
|
original_chain = chain
|
||||||
break
|
break
|
||||||
iteration += 1
|
iteration += 1
|
||||||
|
|
||||||
return current_chain
|
return current_chain, original_chain
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The purpose of this function is to take a look at the last received schedule from
|
The purpose of this function is to take a look at the last received schedule from
|
||||||
pypo-fetch and return the next chain of media_items. A chain is defined as a sequence
|
pypo-fetch and return the next chain of media_items. A chain is defined as a sequence
|
||||||
of media_items where the end time of media_item 'n' is the start time of media_item
|
of media_items where the end time of media_item 'n' is the start time of media_item
|
||||||
'n+1'
|
'n+1'
|
||||||
"""
|
"""
|
||||||
def get_next_schedule_chain(self, chains):
|
def get_next_schedule_chain(self, chains, tnow):
|
||||||
#all media_items are now divided into chains. Let's find the one that
|
#all media_items are now divided into chains. Let's find the one that
|
||||||
#starts closest in the future.
|
#starts closest in the future.
|
||||||
tnow = datetime.utcnow()
|
|
||||||
closest_start = None
|
closest_start = None
|
||||||
closest_chain = None
|
closest_chain = None
|
||||||
for chain in chains:
|
for chain in chains:
|
||||||
|
@ -307,19 +322,32 @@ class PypoPush(Thread):
|
||||||
if (closest_start == None or chain_start < closest_start) and chain_start > tnow:
|
if (closest_start == None or chain_start < closest_start) and chain_start > tnow:
|
||||||
closest_start = chain_start
|
closest_start = chain_start
|
||||||
closest_chain = chain
|
closest_chain = chain
|
||||||
|
|
||||||
return closest_chain
|
return closest_chain
|
||||||
|
|
||||||
|
|
||||||
def date_interval_to_seconds(self, interval):
|
def date_interval_to_seconds(self, interval):
|
||||||
return (interval.microseconds + (interval.seconds + interval.days * 24 * 3600) * 10**6) / float(10**6)
|
return (interval.microseconds + (interval.seconds + interval.days * 24 * 3600) * 10 ** 6) / float(10 ** 6)
|
||||||
|
|
||||||
def push_to_liquidsoap(self, event_chain):
|
def push_to_liquidsoap(self, event_chain):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for media_item in event_chain:
|
for media_item in event_chain:
|
||||||
if media_item['type'] == "file":
|
if media_item['type'] == "file":
|
||||||
self.telnet_to_liquidsoap(media_item)
|
|
||||||
|
"""
|
||||||
|
Wait maximum 5 seconds (50 iterations) for file to become ready, otherwise
|
||||||
|
give up on it.
|
||||||
|
"""
|
||||||
|
iter_num = 0
|
||||||
|
while not media_item['started_copying'] and iter_num < 50:
|
||||||
|
time.sleep(0.1)
|
||||||
|
iter_num += 1
|
||||||
|
|
||||||
|
if media_item['started_copying']:
|
||||||
|
self.telnet_to_liquidsoap(media_item)
|
||||||
|
else:
|
||||||
|
self.logger.warn("File %s did not become ready in less than 5 seconds. Skipping...", media_item['dst'])
|
||||||
elif media_item['type'] == "event":
|
elif media_item['type'] == "event":
|
||||||
if media_item['event_type'] == "kick_out":
|
if media_item['event_type'] == "kick_out":
|
||||||
PypoFetch.disconnect_source(self.logger, self.telnet_lock, "live_dj")
|
PypoFetch.disconnect_source(self.logger, self.telnet_lock, "live_dj")
|
||||||
|
@ -327,27 +355,27 @@ class PypoPush(Thread):
|
||||||
PypoFetch.switch_source(self.logger, self.telnet_lock, "live_dj", "off")
|
PypoFetch.switch_source(self.logger, self.telnet_lock, "live_dj", "off")
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error('Pypo Push Exception: %s', e)
|
self.logger.error('Pypo Push Exception: %s', e)
|
||||||
|
|
||||||
def clear_liquidsoap_queue(self):
|
def clear_liquidsoap_queue(self):
|
||||||
self.logger.debug("Clearing Liquidsoap queue")
|
self.logger.debug("Clearing Liquidsoap queue")
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
msg = "source.skip\n"
|
msg = "source.skip\n"
|
||||||
tn.write(msg)
|
tn.write(msg)
|
||||||
tn.write("exit\n")
|
tn.write("exit\n")
|
||||||
tn.read_all()
|
tn.read_all()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
finally:
|
finally:
|
||||||
self.telnet_lock.release()
|
self.telnet_lock.release()
|
||||||
|
|
||||||
def remove_from_liquidsoap_queue(self, problem_at_iteration, liquidsoap_queue_approx):
|
def remove_from_liquidsoap_queue(self, problem_at_iteration, liquidsoap_queue_approx):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
|
||||||
if problem_at_iteration == 0:
|
if problem_at_iteration == 0:
|
||||||
msg = "source.skip\n"
|
msg = "source.skip\n"
|
||||||
self.logger.debug(msg)
|
self.logger.debug(msg)
|
||||||
|
@ -355,13 +383,13 @@ class PypoPush(Thread):
|
||||||
else:
|
else:
|
||||||
# Remove things in reverse order.
|
# Remove things in reverse order.
|
||||||
queue_copy = liquidsoap_queue_approx[::-1]
|
queue_copy = liquidsoap_queue_approx[::-1]
|
||||||
|
|
||||||
for queue_item in queue_copy:
|
for queue_item in queue_copy:
|
||||||
msg = "queue.remove %s\n" % queue_item['queue_id']
|
msg = "queue.remove %s\n" % queue_item['queue_id']
|
||||||
self.logger.debug(msg)
|
self.logger.debug(msg)
|
||||||
tn.write(msg)
|
tn.write(msg)
|
||||||
response = tn.read_until("\r\n").strip("\r\n")
|
response = tn.read_until("\r\n").strip("\r\n")
|
||||||
|
|
||||||
if "No such request in my queue" in response:
|
if "No such request in my queue" in response:
|
||||||
"""
|
"""
|
||||||
Cannot remove because Liquidsoap started playing the item. Need
|
Cannot remove because Liquidsoap started playing the item. Need
|
||||||
|
@ -370,33 +398,33 @@ class PypoPush(Thread):
|
||||||
msg = "source.skip\n"
|
msg = "source.skip\n"
|
||||||
self.logger.debug(msg)
|
self.logger.debug(msg)
|
||||||
tn.write(msg)
|
tn.write(msg)
|
||||||
|
|
||||||
msg = "queue.queue\n"
|
msg = "queue.queue\n"
|
||||||
self.logger.debug(msg)
|
self.logger.debug(msg)
|
||||||
tn.write(msg)
|
tn.write(msg)
|
||||||
|
|
||||||
tn.write("exit\n")
|
tn.write("exit\n")
|
||||||
self.logger.debug(tn.read_all())
|
self.logger.debug(tn.read_all())
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
finally:
|
finally:
|
||||||
self.telnet_lock.release()
|
self.telnet_lock.release()
|
||||||
|
|
||||||
def sleep_until_start(self, media_item):
|
def sleep_until_start(self, media_item):
|
||||||
"""
|
"""
|
||||||
The purpose of this function is to look at the difference between
|
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.
|
"now" and when the media_item starts, and sleep for that period of time.
|
||||||
After waking from sleep, this function returns.
|
After waking from sleep, this function returns.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mi_start = media_item['start'][0:19]
|
mi_start = media_item['start'][0:19]
|
||||||
|
|
||||||
#strptime returns struct_time in local time
|
#strptime returns struct_time in local time
|
||||||
epoch_start = calendar.timegm(time.strptime(mi_start, '%Y-%m-%d-%H-%M-%S'))
|
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.
|
#Return the time as a floating point number expressed in seconds since the epoch, in UTC.
|
||||||
epoch_now = time.time()
|
epoch_now = time.time()
|
||||||
|
|
||||||
self.logger.debug("Epoch start: %s" % epoch_start)
|
self.logger.debug("Epoch start: %s" % epoch_start)
|
||||||
self.logger.debug("Epoch now: %s" % epoch_now)
|
self.logger.debug("Epoch now: %s" % epoch_now)
|
||||||
|
|
||||||
|
@ -417,43 +445,43 @@ class PypoPush(Thread):
|
||||||
try:
|
try:
|
||||||
self.telnet_lock.acquire()
|
self.telnet_lock.acquire()
|
||||||
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
|
||||||
|
|
||||||
#tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
|
#tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
|
||||||
|
|
||||||
annotation = self.create_liquidsoap_annotation(media_item)
|
annotation = self.create_liquidsoap_annotation(media_item)
|
||||||
msg = 'queue.push %s\n' % annotation.encode('utf-8')
|
msg = 'queue.push %s\n' % annotation.encode('utf-8')
|
||||||
self.logger.debug(msg)
|
self.logger.debug(msg)
|
||||||
tn.write(msg)
|
tn.write(msg)
|
||||||
queue_id = tn.read_until("\r\n").strip("\r\n")
|
queue_id = tn.read_until("\r\n").strip("\r\n")
|
||||||
|
|
||||||
#remember the media_item's queue id which we may use
|
#remember the media_item's queue id which we may use
|
||||||
#later if we need to remove it from the queue.
|
#later if we need to remove it from the queue.
|
||||||
media_item['queue_id'] = queue_id
|
media_item['queue_id'] = queue_id
|
||||||
|
|
||||||
#add media_item to the end of our queue
|
#add media_item to the end of our queue
|
||||||
self.pushed_objects[queue_id] = media_item
|
self.pushed_objects[queue_id] = media_item
|
||||||
|
|
||||||
show_name = media_item['show_name']
|
show_name = media_item['show_name']
|
||||||
msg = 'vars.show_name %s\n' % show_name.encode('utf-8')
|
msg = 'vars.show_name %s\n' % show_name.encode('utf-8')
|
||||||
tn.write(msg)
|
tn.write(msg)
|
||||||
self.logger.debug(msg)
|
self.logger.debug(msg)
|
||||||
|
|
||||||
tn.write("exit\n")
|
tn.write("exit\n")
|
||||||
self.logger.debug(tn.read_all())
|
self.logger.debug(tn.read_all())
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
finally:
|
finally:
|
||||||
self.telnet_lock.release()
|
self.telnet_lock.release()
|
||||||
|
|
||||||
def create_liquidsoap_annotation(self, media):
|
def create_liquidsoap_annotation(self, media):
|
||||||
# we need lia_start_next value in the annotate. That is the value that controlls overlap duration of crossfade.
|
# we need lia_start_next value in the annotate. That is the value that controlls overlap duration of crossfade.
|
||||||
return 'annotate:media_id="%s",liq_start_next="0",liq_fade_in="%s",liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",schedule_table_id="%s":%s' \
|
return 'annotate:media_id="%s",liq_start_next="0",liq_fade_in="%s",liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",schedule_table_id="%s":%s' \
|
||||||
% (media['id'], float(media['fade_in'])/1000, float(media['fade_out'])/1000, float(media['cue_in']), float(media['cue_out']), media['row_id'], media['dst'])
|
% (media['id'], float(media['fade_in']) / 1000, float(media['fade_out']) / 1000, float(media['cue_in']), float(media['cue_out']), media['row_id'], media['dst'])
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try: self.main()
|
try: self.main()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
import traceback
|
import traceback
|
||||||
top = traceback.format_exc()
|
top = traceback.format_exc()
|
||||||
self.logger.error('Pypo Push Exception: %s', top)
|
self.logger.error('Pypo Push Exception: %s', top)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -55,7 +54,7 @@ class ShowRecorder(Thread):
|
||||||
self.p = None
|
self.p = None
|
||||||
|
|
||||||
def record_show(self):
|
def record_show(self):
|
||||||
length = str(self.filelength)+".0"
|
length = str(self.filelength) + ".0"
|
||||||
filename = self.start_time
|
filename = self.start_time
|
||||||
filename = filename.replace(" ", "-")
|
filename = filename.replace(" ", "-")
|
||||||
|
|
||||||
|
@ -128,7 +127,7 @@ class ShowRecorder(Thread):
|
||||||
time = md[1].replace(":", "-")
|
time = md[1].replace(":", "-")
|
||||||
self.logger.info("time: %s" % time)
|
self.logger.info("time: %s" % time)
|
||||||
|
|
||||||
name = time+"-"+self.show_name
|
name = time + "-" + self.show_name
|
||||||
artist = "Airtime Show Recorder"
|
artist = "Airtime Show Recorder"
|
||||||
|
|
||||||
#set some metadata for our file daemon
|
#set some metadata for our file daemon
|
||||||
|
@ -181,7 +180,7 @@ class Recorder(Thread):
|
||||||
def handle_message(self):
|
def handle_message(self):
|
||||||
if not self.queue.empty():
|
if not self.queue.empty():
|
||||||
message = self.queue.get()
|
message = self.queue.get()
|
||||||
msg = json.loads(message)
|
msg = json.loads(message)
|
||||||
command = msg["event_type"]
|
command = msg["event_type"]
|
||||||
self.logger.info("Received msg from Pypo Message Handler: %s", msg)
|
self.logger.info("Received msg from Pypo Message Handler: %s", msg)
|
||||||
if command == 'cancel_recording':
|
if command == 'cancel_recording':
|
||||||
|
@ -190,10 +189,10 @@ class Recorder(Thread):
|
||||||
else:
|
else:
|
||||||
self.process_recorder_schedule(msg)
|
self.process_recorder_schedule(msg)
|
||||||
self.loops = 0
|
self.loops = 0
|
||||||
|
|
||||||
if self.shows_to_record:
|
if self.shows_to_record:
|
||||||
self.start_record()
|
self.start_record()
|
||||||
|
|
||||||
def process_recorder_schedule(self, m):
|
def process_recorder_schedule(self, m):
|
||||||
self.logger.info("Parsing recording show schedules...")
|
self.logger.info("Parsing recording show schedules...")
|
||||||
temp_shows_to_record = {}
|
temp_shows_to_record = {}
|
||||||
|
@ -217,7 +216,7 @@ class Recorder(Thread):
|
||||||
delta = next_show - tnow
|
delta = next_show - tnow
|
||||||
s = '%s.%s' % (delta.seconds, delta.microseconds)
|
s = '%s.%s' % (delta.seconds, delta.microseconds)
|
||||||
out = float(s)
|
out = float(s)
|
||||||
|
|
||||||
if out < 5:
|
if out < 5:
|
||||||
self.logger.debug("Shows %s", self.shows_to_record)
|
self.logger.debug("Shows %s", self.shows_to_record)
|
||||||
self.logger.debug("Next show %s", next_show)
|
self.logger.debug("Next show %s", next_show)
|
||||||
|
@ -231,26 +230,26 @@ class Recorder(Thread):
|
||||||
if delta < 5:
|
if delta < 5:
|
||||||
self.logger.debug("sleeping %s seconds until show", delta)
|
self.logger.debug("sleeping %s seconds until show", delta)
|
||||||
time.sleep(delta)
|
time.sleep(delta)
|
||||||
|
|
||||||
sorted_show_keys = sorted(self.shows_to_record.keys())
|
sorted_show_keys = sorted(self.shows_to_record.keys())
|
||||||
start_time = sorted_show_keys[0]
|
start_time = sorted_show_keys[0]
|
||||||
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]
|
||||||
server_timezone = self.shows_to_record[start_time][3]
|
server_timezone = self.shows_to_record[start_time][3]
|
||||||
|
|
||||||
T = pytz.timezone(server_timezone)
|
T = pytz.timezone(server_timezone)
|
||||||
start_time_on_UTC = getDateTimeObj(start_time)
|
start_time_on_UTC = getDateTimeObj(start_time)
|
||||||
start_time_on_server = start_time_on_UTC.replace(tzinfo=pytz.utc).astimezone(T)
|
start_time_on_server = start_time_on_UTC.replace(tzinfo=pytz.utc).astimezone(T)
|
||||||
start_time_formatted = '%(year)d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(sec)02d' % \
|
start_time_formatted = '%(year)d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(sec)02d' % \
|
||||||
{'year': start_time_on_server.year, 'month': start_time_on_server.month, 'day': start_time_on_server.day,\
|
{'year': start_time_on_server.year, 'month': start_time_on_server.month, 'day': start_time_on_server.day, \
|
||||||
'hour': start_time_on_server.hour, 'min': start_time_on_server.minute, 'sec': start_time_on_server.second}
|
'hour': start_time_on_server.hour, 'min': start_time_on_server.minute, 'sec': start_time_on_server.second}
|
||||||
self.sr = ShowRecorder(show_instance, show_name, show_length.seconds, start_time_formatted)
|
self.sr = ShowRecorder(show_instance, show_name, show_length.seconds, start_time_formatted)
|
||||||
self.sr.start()
|
self.sr.start()
|
||||||
#remove show from shows to record.
|
#remove show from shows to record.
|
||||||
del self.shows_to_record[start_time]
|
del self.shows_to_record[start_time]
|
||||||
#self.time_till_next_show = self.get_time_till_next_show()
|
#self.time_till_next_show = self.get_time_till_next_show()
|
||||||
except Exception,e :
|
except Exception, e :
|
||||||
import traceback
|
import traceback
|
||||||
top = traceback.format_exc()
|
top = traceback.format_exc()
|
||||||
self.logger.error('Exception: %s', e)
|
self.logger.error('Exception: %s', e)
|
||||||
|
@ -273,12 +272,12 @@ class Recorder(Thread):
|
||||||
self.logger.info("Bootstrap recorder schedule received: %s", temp)
|
self.logger.info("Bootstrap recorder schedule received: %s", temp)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.error(e)
|
self.logger.error(e)
|
||||||
|
|
||||||
self.logger.info("Bootstrap complete: got initial copy of the schedule")
|
self.logger.info("Bootstrap complete: got initial copy of the schedule")
|
||||||
|
|
||||||
self.loops = 0
|
self.loops = 0
|
||||||
heartbeat_period = math.floor(30/PUSH_INTERVAL)
|
heartbeat_period = math.floor(30 / PUSH_INTERVAL)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if self.loops % heartbeat_period == 0:
|
if self.loops % heartbeat_period == 0:
|
||||||
self.logger.info("heartbeat")
|
self.logger.info("heartbeat")
|
||||||
|
@ -299,7 +298,7 @@ class Recorder(Thread):
|
||||||
self.logger.error('Pypo Recorder Exception: %s', e)
|
self.logger.error('Pypo Recorder Exception: %s', e)
|
||||||
time.sleep(PUSH_INTERVAL)
|
time.sleep(PUSH_INTERVAL)
|
||||||
self.loops += 1
|
self.loops += 1
|
||||||
except Exception,e :
|
except Exception, e :
|
||||||
import traceback
|
import traceback
|
||||||
top = traceback.format_exc()
|
top = traceback.format_exc()
|
||||||
self.logger.error('Exception: %s', e)
|
self.logger.error('Exception: %s', e)
|
||||||
|
|
Loading…
Reference in New Issue