Merge branch 'master' of dev.sourcefabric.org:airtime

Conflicts:
	install/airtime-install.php
This commit is contained in:
Paul Baranowski 2011-03-28 15:41:49 -04:00
commit c17ee178d1
237 changed files with 2270 additions and 23932 deletions

4
.gitignore vendored
View File

@ -1,5 +1,5 @@
.* .*
*.pyc *.pyc
files/ /files
pypo/liquidsoap/liquidsoap python_apps/pypo/liquidsoap/liquidsoap
build/build.properties build/build.properties

View File

@ -1,7 +1,7 @@
<?php <?php
require_once (__DIR__."/configs/navigation.php"); require_once __DIR__."/configs/navigation.php";
require_once (__DIR__."/configs/ACL.php"); require_once __DIR__."/configs/ACL.php";
require_once 'propel/runtime/lib/Propel.php'; require_once 'propel/runtime/lib/Propel.php';
Propel::init(__DIR__."/configs/airtime-conf.php"); Propel::init(__DIR__."/configs/airtime-conf.php");
@ -10,17 +10,20 @@ Propel::init(__DIR__."/configs/airtime-conf.php");
$tz = ini_get('date.timezone') ? ini_get('date.timezone') : 'UTC'; $tz = ini_get('date.timezone') ? ini_get('date.timezone') : 'UTC';
date_default_timezone_set($tz); date_default_timezone_set($tz);
require_once (__DIR__."/configs/constants.php"); require_once __DIR__."/configs/constants.php";
require_once (__DIR__."/configs/conf.php"); require_once __DIR__."/configs/conf.php";
require_once 'DB.php'; require_once 'DB.php';
require_once 'Soundcloud.php';
require_once 'Playlist.php'; require_once 'Playlist.php';
require_once 'StoredFile.php'; require_once 'StoredFile.php';
require_once 'Schedule.php'; require_once 'Schedule.php';
require_once 'Shows.php'; require_once 'Shows.php';
require_once 'Users.php'; require_once 'Users.php';
require_once 'RabbitMq.php';
require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php';
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$dsn = $CC_CONFIG['dsn']; $dsn = $CC_CONFIG['dsn'];
$CC_DBC = DB::connect($dsn, FALSE); $CC_DBC = DB::connect($dsn, FALSE);
@ -33,6 +36,9 @@ $CC_DBC->setFetchMode(DB_FETCHMODE_ASSOC);
//Zend_Session::start(); //Zend_Session::start();
Zend_Validate::setDefaultNamespaces("Zend"); Zend_Validate::setDefaultNamespaces("Zend");
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new RabbitMqPlugin());
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{ {
protected function _initDoctype() protected function _initDoctype()
@ -45,9 +51,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
protected function _initHeadLink() protected function _initHeadLink()
{ {
$view = $this->getResource('view'); $view = $this->getResource('view');
$view->headLink()->appendStylesheet('/css/redmond/jquery-ui-1.8.8.custom.css'); $view->headLink()->appendStylesheet('/css/redmond/jquery-ui-1.8.8.custom.css');
$this->view->headLink()->appendStylesheet('/css/pro_dropdown_3.css'); $this->view->headLink()->appendStylesheet('/css/pro_dropdown_3.css');
$this->view->headLink()->appendStylesheet('/css/styles.css'); $this->view->headLink()->appendStylesheet('/css/styles.css');
} }
@ -64,17 +68,20 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
$this->view->headScript()->appendFile('/js/playlist/helperfunctions.js','text/javascript'); $this->view->headScript()->appendFile('/js/playlist/helperfunctions.js','text/javascript');
$this->view->headScript()->appendFile('/js/playlist/playlist.js','text/javascript'); $this->view->headScript()->appendFile('/js/playlist/playlist.js','text/javascript');
$view->headScript()->appendFile('/js/airtime/common/common.js','text/javascript'); $view->headScript()->appendFile('/js/airtime/common/common.js','text/javascript');
} }
protected function _initViewHelpers(){ protected function _initViewHelpers()
{
$view = $this->getResource('view'); $view = $this->getResource('view');
$view->addHelperPath('../application/views/helpers', 'Airtime_View_Helper'); $view->addHelperPath('../application/views/helpers', 'Airtime_View_Helper');
} }
protected function _initTitle(){ protected function _initTitle()
{
$view = $this->getResource('view'); $view = $this->getResource('view');
$view->headTitle(Application_Model_Preference::GetHeadTitle()); $view->headTitle(Application_Model_Preference::GetHeadTitle());
} }
} }

View File

@ -1,14 +1,15 @@
<?php <?php
define('AIRTIME_VERSION', '1.7.0 alpha'); define('AIRTIME_VERSION', '1.7.0-alpha');
define('AIRTIME_COPYRIGHT_DATE', '2010-2011'); define('AIRTIME_COPYRIGHT_DATE', '2010-2011');
define('AIRTIME_REST_VERSION', '1.1');
// These are the default values for the config. // These are the default values for the config.
global $CC_CONFIG; global $CC_CONFIG;
$values = load_airtime_config(); $values = load_airtime_config();
// ********************************** // **********************************
// ***** START CUSTOMIZING HERE ***** // ***** START CUSTOMIZING HERE *****
// ********************************** // **********************************
// Set the location where you want to store all of your audio files. // Set the location where you want to store all of your audio files.
// //
@ -25,10 +26,16 @@ $CC_CONFIG = array(
// Name of the web server user // Name of the web server user
'webServerUser' => 'www-data', 'webServerUser' => 'www-data',
// *********************************************************************** 'rabbitmq' => array("host" => "127.0.0.1",
"port" => "5672",
"user" => "guest",
"password" => "guest",
"vhost" => "/"),
// ***********************************************************************
// STOP CUSTOMIZING HERE // STOP CUSTOMIZING HERE
// //
// You don't need to touch anything below this point. // You don't need to touch anything below this point.
// *********************************************************************** // ***********************************************************************
'baseFilesDir' => $baseFilesDir, 'baseFilesDir' => $baseFilesDir,
@ -46,6 +53,9 @@ $CC_CONFIG = array(
'apiKey' => $values['api_key'], 'apiKey' => $values['api_key'],
'apiPath' => '/api/', 'apiPath' => '/api/',
'soundcloud-client-id' => '2CLCxcSXYzx7QhhPVHN4A',
'soundcloud-client-secret' => 'pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs',
"rootDir" => __DIR__."/../..", "rootDir" => __DIR__."/../..",
'pearPath' => dirname(__FILE__).'/../../library/pear', 'pearPath' => dirname(__FILE__).'/../../library/pear',
'zendPath' => dirname(__FILE__).'/../../library/Zend', 'zendPath' => dirname(__FILE__).'/../../library/Zend',
@ -55,26 +65,26 @@ $CC_CONFIG = array(
//'AdminsGr' => 'Admins', //'AdminsGr' => 'Admins',
// name of station preferences group // name of station preferences group
'StationPrefsGr'=> 'StationPrefs', // 'StationPrefsGr'=> 'StationPrefs',
// name of 'all users' group // name of 'all users' group
//'AllGr' => 'All', //'AllGr' => 'All',
/* ==================================== application-specific configuration */ /* ==================================== application-specific configuration */
'objtypes' => array( // 'objtypes' => array(
'Storage' => array(/*'Folder',*/ 'File' /*, 'Replica'*/), // 'Storage' => array(/*'Folder',*/ 'File' /*, 'Replica'*/),
'File' => array(), // 'File' => array(),
'audioclip' => array(), // 'audioclip' => array(),
'playlist' => array(), // 'playlist' => array(),
), // ),
'allowedActions'=> array( // 'allowedActions'=> array(
'File' => array('editPrivs', 'write', 'read'), // 'File' => array('editPrivs', 'write', 'read'),
'audioclip' => array('editPrivs', 'write', 'read'), // 'audioclip' => array('editPrivs', 'write', 'read'),
'playlist' => array('editPrivs', 'write', 'read'), // 'playlist' => array('editPrivs', 'write', 'read'),
), // ),
'allActions' => array( // 'allActions' => array(
'editPrivs', 'write', 'read', 'subjects' // 'editPrivs', 'write', 'read', 'subjects'
), // ),
/* =================================================== cron configuration */ /* =================================================== cron configuration */
'cronUserName' => 'www-data', 'cronUserName' => 'www-data',
@ -82,7 +92,7 @@ $CC_CONFIG = array(
'lockfile' => dirname(__FILE__).'/stor/buffer/cron.lock', 'lockfile' => dirname(__FILE__).'/stor/buffer/cron.lock',
'cronfile' => dirname(__FILE__).'/cron/croncall.php', 'cronfile' => dirname(__FILE__).'/cron/croncall.php',
'paramdir' => dirname(__FILE__).'/cron/params', 'paramdir' => dirname(__FILE__).'/cron/params',
'systemPrefId' => "0", // ID for system prefs in prefs table // 'systemPrefId' => "0", // ID for system prefs in prefs table
); );
// Add database table names // Add database table names
@ -94,10 +104,8 @@ $CC_CONFIG['permTable'] = $CC_CONFIG['tblNamePrefix'].'perms';
$CC_CONFIG['sessTable'] = $CC_CONFIG['tblNamePrefix'].'sess'; $CC_CONFIG['sessTable'] = $CC_CONFIG['tblNamePrefix'].'sess';
$CC_CONFIG['subjTable'] = $CC_CONFIG['tblNamePrefix'].'subjs'; $CC_CONFIG['subjTable'] = $CC_CONFIG['tblNamePrefix'].'subjs';
$CC_CONFIG['smembTable'] = $CC_CONFIG['tblNamePrefix'].'smemb'; $CC_CONFIG['smembTable'] = $CC_CONFIG['tblNamePrefix'].'smemb';
$CC_CONFIG['transTable'] = $CC_CONFIG['tblNamePrefix'].'trans';
$CC_CONFIG['prefTable'] = $CC_CONFIG['tblNamePrefix'].'pref'; $CC_CONFIG['prefTable'] = $CC_CONFIG['tblNamePrefix'].'pref';
$CC_CONFIG['scheduleTable'] = $CC_CONFIG['tblNamePrefix'].'schedule'; $CC_CONFIG['scheduleTable'] = $CC_CONFIG['tblNamePrefix'].'schedule';
$CC_CONFIG['backupTable'] = $CC_CONFIG['tblNamePrefix'].'backup';
$CC_CONFIG['playListTimeView'] = $CC_CONFIG['tblNamePrefix'].'playlisttimes'; $CC_CONFIG['playListTimeView'] = $CC_CONFIG['tblNamePrefix'].'playlisttimes';
$CC_CONFIG['showSchedule'] = $CC_CONFIG['tblNamePrefix'].'show_schedule'; $CC_CONFIG['showSchedule'] = $CC_CONFIG['tblNamePrefix'].'show_schedule';
$CC_CONFIG['showDays'] = $CC_CONFIG['tblNamePrefix'].'show_days'; $CC_CONFIG['showDays'] = $CC_CONFIG['tblNamePrefix'].'show_days';
@ -106,16 +114,15 @@ $CC_CONFIG['showInstances'] = $CC_CONFIG['tblNamePrefix'].'show_instances';
$CC_CONFIG['playListSequence'] = $CC_CONFIG['playListTable'].'_id'; $CC_CONFIG['playListSequence'] = $CC_CONFIG['playListTable'].'_id';
$CC_CONFIG['filesSequence'] = $CC_CONFIG['filesTable'].'_id'; $CC_CONFIG['filesSequence'] = $CC_CONFIG['filesTable'].'_id';
$CC_CONFIG['transSequence'] = $CC_CONFIG['transTable'].'_id';
$CC_CONFIG['prefSequence'] = $CC_CONFIG['prefTable'].'_id'; $CC_CONFIG['prefSequence'] = $CC_CONFIG['prefTable'].'_id';
$CC_CONFIG['permSequence'] = $CC_CONFIG['permTable'].'_id'; $CC_CONFIG['permSequence'] = $CC_CONFIG['permTable'].'_id';
$CC_CONFIG['subjSequence'] = $CC_CONFIG['subjTable'].'_id'; $CC_CONFIG['subjSequence'] = $CC_CONFIG['subjTable'].'_id';
$CC_CONFIG['smembSequence'] = $CC_CONFIG['smembTable'].'_id'; $CC_CONFIG['smembSequence'] = $CC_CONFIG['smembTable'].'_id';
// System users/groups - they cannot be deleted // System users/groups - they cannot be deleted
$CC_CONFIG['sysSubjs'] = array( //$CC_CONFIG['sysSubjs'] = array(
'root', /*$CC_CONFIG['AdminsGr'],*/ /*$CC_CONFIG['AllGr'],*/ $CC_CONFIG['StationPrefsGr'] // 'root', /*$CC_CONFIG['AdminsGr'],*/ /*$CC_CONFIG['AllGr'],*/ $CC_CONFIG['StationPrefsGr']
); //);
// Add libs to the PHP path // Add libs to the PHP path
$old_include_path = get_include_path(); $old_include_path = get_include_path();
@ -125,9 +132,9 @@ set_include_path('.'.PATH_SEPARATOR.$CC_CONFIG['pearPath']
function load_airtime_config(){ function load_airtime_config(){
$ini_array = parse_ini_file(dirname(__FILE__).'/../../build/airtime.conf', true); $ini_array = parse_ini_file(dirname(__FILE__).'/../../build/airtime.conf', true);
return array( return array(
'database' => array( 'database' => array(
'username' => $ini_array['database']['dbuser'], 'username' => $ini_array['database']['dbuser'],
'password' => $ini_array['database']['dbpass'], 'password' => $ini_array['database']['dbpass'],
'hostspec' => $ini_array['database']['host'], 'hostspec' => $ini_array['database']['host'],

View File

@ -2,7 +2,7 @@
/* /*
* Navigation container (config/array) * Navigation container (config/array)
* Each element in the array will be passed to * Each element in the array will be passed to
* Zend_Navigation_Page::factory() when constructing * Zend_Navigation_Page::factory() when constructing
* the navigation container below. * the navigation container below.
@ -16,7 +16,7 @@ $pages = array(
'resource' => 'nowplaying' 'resource' => 'nowplaying'
), ),
array( array(
'label' => 'Add Audio', 'label' => 'Add Media',
'module' => 'default', 'module' => 'default',
'controller' => 'Plupload', 'controller' => 'Plupload',
'action' => 'plupload', 'action' => 'plupload',
@ -51,7 +51,7 @@ $pages = array(
'module' => 'default', 'module' => 'default',
'controller' => 'user', 'controller' => 'user',
'action' => 'add-user', 'action' => 'add-user',
'resource' => 'user' 'resource' => 'user'
) )
) )
), ),
@ -64,10 +64,10 @@ $pages = array(
) )
); );
// Create container from array // Create container from array
$container = new Zend_Navigation($pages); $container = new Zend_Navigation($pages);
$container->id = "nav"; $container->id = "nav";
//store it in the registry: //store it in the registry:
Zend_Registry::set('Zend_Navigation', $container); Zend_Registry::set('Zend_Navigation', $container);

View File

@ -6,8 +6,10 @@ class ApiController extends Zend_Controller_Action
public function init() public function init()
{ {
/* Initialize action controller here */ /* Initialize action controller here */
$ajaxContext = $this->_helper->getHelper('AjaxContext'); $context = $this->_helper->getHelper('contextSwitch');
$ajaxContext->addActionContext('version', 'json') $context->addActionContext('version', 'json')
->addActionContext('recorded-shows', 'json')
->addActionContext('upload-recorded', 'json')
->initContext(); ->initContext();
} }
@ -24,7 +26,7 @@ class ApiController extends Zend_Controller_Action
* in application/conf.php * in application/conf.php
* *
* @return void * @return void
* *
*/ */
public function versionAction() public function versionAction()
{ {
@ -101,6 +103,29 @@ class ApiController extends Zend_Controller_Action
exit; exit;
} }
public function liveInfoAction(){
global $CC_CONFIG;
// disable the view and the layout
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$result = Schedule::GetPlayOrderRange(0, 1);
$date = new Application_Model_DateHelper;
$timeNow = $date->getDate();
$result = array("env"=>APPLICATION_ENV,
"schedulerTime"=>gmdate("Y-m-d H:i:s"),
"currentShow"=>Show_DAL::GetCurrentShow($timeNow),
"nextShow"=>Show_DAL::GetNextShows($timeNow, 5),
"timezone"=> date("T"),
"timezoneOffset"=> date("Z"));
//echo json_encode($result);
header("Content-type: text/javascript");
echo $_GET['callback'].'('.json_encode($result).')';
}
public function scheduleAction() public function scheduleAction()
{ {
global $CC_CONFIG; global $CC_CONFIG;
@ -110,6 +135,7 @@ class ApiController extends Zend_Controller_Action
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->viewRenderer->setNoRender(true);
$api_key = $this->_getParam('api_key'); $api_key = $this->_getParam('api_key');
if(!in_array($api_key, $CC_CONFIG["apiKey"])) if(!in_array($api_key, $CC_CONFIG["apiKey"]))
{ {
header('HTTP/1.0 401 Unauthorized'); header('HTTP/1.0 401 Unauthorized');
@ -119,18 +145,10 @@ class ApiController extends Zend_Controller_Action
PEAR::setErrorHandling(PEAR_ERROR_RETURN); PEAR::setErrorHandling(PEAR_ERROR_RETURN);
$from = $this->_getParam("from"); $result = Schedule::GetScheduledPlaylists();
$to = $this->_getParam("to"); echo json_encode($result);
if (Schedule::ValidPypoTimeFormat($from) && Schedule::ValidPypoTimeFormat($to)) {
$result = Schedule::ExportRangeAsJson($from, $to);
$result['stream_metadata'] = array();
$result['stream_metadata']['format'] = Application_Model_Preference::GetStreamLabelFormat();
$result['stream_metadata']['station_name'] = Application_Model_Preference::GetStationName();
echo json_encode($result);
}
} }
public function notifyMediaItemStartPlayAction() public function notifyMediaItemStartPlayAction()
{ {
global $CC_CONFIG; global $CC_CONFIG;
@ -197,5 +215,53 @@ class ApiController extends Zend_Controller_Action
exit; exit;
} }
} }
public function recordedShowsAction()
{
global $CC_CONFIG;
$api_key = $this->_getParam('api_key');
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
{
header('HTTP/1.0 401 Unauthorized');
print 'You are not allowed to access this resource.';
exit;
}
$today_timestamp = date("Y-m-d H:i:s");
$this->view->shows = Show::getShows($today_timestamp, null, $excludeInstance=NULL, $onlyRecord=TRUE);
}
public function uploadRecordedAction()
{
global $CC_CONFIG;
$api_key = $this->_getParam('api_key');
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
{
header('HTTP/1.0 401 Unauthorized');
print 'You are not allowed to access this resource.';
exit;
}
$upload_dir = ini_get("upload_tmp_dir");
$file = StoredFile::uploadFile($upload_dir);
$show_instance = $this->_getParam('show_instance');
$show_inst = new ShowInstance($show_instance);
$show_inst->setRecordedFile($file->getId());
if(Application_Model_Preference::GetDoSoundCloudUpload())
{
$show = new Show($show_inst->getShowId());
$description = $show->getDescription();
$soundcloud = new ATSoundcloud();
$soundcloud->uploadTrack($file->getRealFilePath(), $file->getName(), $description);
}
$this->view->id = $file->getId();
}
} }

View File

@ -11,144 +11,6 @@ class PluploadController extends Zend_Controller_Action
->initContext(); ->initContext();
} }
public function upload($targetDir)
{
// HTTP headers for no cache etc
header('Content-type: text/plain; charset=UTF-8');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
// Settings
//$targetDir = ini_get("upload_tmp_dir"); //. DIRECTORY_SEPARATOR . "plupload";
$cleanupTargetDir = false; // Remove old files
$maxFileAge = 60 * 60; // Temp file age in seconds
// 5 minutes execution time
@set_time_limit(5 * 60);
// usleep(5000);
// Get parameters
$chunk = isset($_REQUEST["chunk"]) ? $_REQUEST["chunk"] : 0;
$chunks = isset($_REQUEST["chunks"]) ? $_REQUEST["chunks"] : 0;
$fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';
// Clean the fileName for security reasons
//$fileName = preg_replace('/[^\w\._]+/', '', $fileName);
// Create target dir
if (!file_exists($targetDir))
@mkdir($targetDir);
// Remove old temp files
if (is_dir($targetDir) && ($dir = opendir($targetDir))) {
while (($file = readdir($dir)) !== false) {
$filePath = $targetDir . DIRECTORY_SEPARATOR . $file;
// Remove temp files if they are older than the max age
if (preg_match('/\.tmp$/', $file) && (filemtime($filePath) < time() - $maxFileAge))
@unlink($filePath);
}
closedir($dir);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
// Look for the content type header
if (isset($_SERVER["HTTP_CONTENT_TYPE"]))
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
if (isset($_SERVER["CONTENT_TYPE"]))
$contentType = $_SERVER["CONTENT_TYPE"];
if (strpos($contentType, "multipart") !== false) {
if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name'])) {
// Open temp file
$out = fopen($targetDir . DIRECTORY_SEPARATOR . $fileName, $chunk == 0 ? "wb" : "ab");
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen($_FILES['file']['tmp_name'], "rb");
if ($in) {
while ($buff = fread($in, 4096))
fwrite($out, $buff);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
fclose($out);
unlink($_FILES['file']['tmp_name']);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
} else {
// Open temp file
$out = fopen($targetDir . DIRECTORY_SEPARATOR . $fileName, $chunk == 0 ? "wb" : "ab");
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen("php://input", "rb");
if ($in) {
while ($buff = fread($in, 4096))
fwrite($out, $buff);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
fclose($out);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}
$audio_file = $targetDir . DIRECTORY_SEPARATOR . $fileName;
$md5 = md5_file($audio_file);
$duplicate = StoredFile::RecallByMd5($md5);
if ($duplicate) {
if (PEAR::isError($duplicate)) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' . $duplicate->getMessage() .'}}');
}
else {
$duplicateName = $duplicate->getMetadataValue(UI_MDATA_KEY_TITLE);
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "An identical audioclip named ' . $duplicateName . ' already exists in the storage server."}}');
}
}
$metadata = Metadata::LoadFromFile($audio_file);
if (PEAR::isError($metadata)) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' + $metadata->getMessage() + '}}');
}
// #2196 no id tag -> use the original filename
if (basename($audio_file) == $metadata[UI_MDATA_KEY_TITLE]) {
$metadata[UI_MDATA_KEY_TITLE] = basename($audio_file);
$metadata[UI_MDATA_KEY_FILENAME] = basename($audio_file);
}
// setMetadataBatch doesnt like these values
unset($metadata['audio']);
unset($metadata['playtime_seconds']);
$values = array(
"filename" => basename($audio_file),
"filepath" => $audio_file,
"filetype" => "audioclip",
"mime" => $metadata[UI_MDATA_KEY_FORMAT],
"md5" => $md5
);
$storedFile = StoredFile::Insert($values);
if (PEAR::isError($storedFile)) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' + $storedFile->getMessage() + '}}');
}
$storedFile->setMetadataBatch($metadata);
// Return JSON-RPC response
die('{"jsonrpc" : "2.0", "id" : '.$storedFile->getId().' }');
}
public function indexAction() public function indexAction()
{ {
@ -158,13 +20,9 @@ class PluploadController extends Zend_Controller_Action
public function uploadAction() public function uploadAction()
{ {
$upload_dir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload"; $upload_dir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
$this->upload($upload_dir); $file = StoredFile::uploadFile($upload_dir);
}
public function uploadRecordedAction() die('{"jsonrpc" : "2.0", "id" : '.$file->getId().' }');
{
$upload_dir = ini_get("upload_tmp_dir");
$this->upload($upload_dir);
} }
public function pluploadAction() public function pluploadAction()

View File

@ -29,7 +29,11 @@ class PreferenceController extends Zend_Controller_Action
$values = $form->getValues(); $values = $form->getValues();
Application_Model_Preference::SetHeadTitle($values["stationName"], $this->view); Application_Model_Preference::SetHeadTitle($values["stationName"], $this->view);
Application_Model_Preference::SetDefaultFade($values["stationDefaultFade"]); Application_Model_Preference::SetDefaultFade($values["stationDefaultFade"]);
Application_Model_Preference::SetStreamLabelFormat($values["streamFormat"]); Application_Model_Preference::SetStreamLabelFormat($values["streamFormat"]);
Application_Model_Preference::SetDoSoundCloudUpload($values["UseSoundCloud"]);
Application_Model_Preference::SetSoundCloudUser($values["SoundCloudUser"]);
Application_Model_Preference::SetSoundCloudPassword($values["SoundCloudPassword"]);
Application_Model_Preference::SetSoundCloudTags($values["SoundCloudTags"]);
$this->view->statusMsg = "Preferences Updated."; $this->view->statusMsg = "Preferences Updated.";
} }

View File

@ -1,28 +0,0 @@
<?php
class RecorderController extends Zend_Controller_Action
{
public function init()
{
$ajaxContext = $this->_helper->getHelper('contextSwitch');
$ajaxContext->addActionContext('get-show-schedule', 'json')
->addActionContext('get-uploaded-file', 'json')
->initContext();
}
public function indexAction()
{
// action body
}
public function getShowScheduleAction()
{
$today_timestamp = date("Y-m-d H:i:s");
$this->view->shows = Show::getShows($today_timestamp, null, $excludeInstance=NULL, $onlyRecord=TRUE);
}
}

View File

@ -19,9 +19,9 @@ class ScheduleController extends Zend_Controller_Action
->addActionContext('schedule-show-dialog', 'json') ->addActionContext('schedule-show-dialog', 'json')
->addActionContext('show-content-dialog', 'json') ->addActionContext('show-content-dialog', 'json')
->addActionContext('clear-show', 'json') ->addActionContext('clear-show', 'json')
->addActionContext('get-current-playlist', 'json') ->addActionContext('get-current-playlist', 'json')
->addActionContext('find-playlists', 'json') ->addActionContext('find-playlists', 'json')
->addActionContext('remove-group', 'json') ->addActionContext('remove-group', 'json')
->addActionContext('edit-show', 'json') ->addActionContext('edit-show', 'json')
->addActionContext('add-show', 'json') ->addActionContext('add-show', 'json')
->addActionContext('cancel-show', 'json') ->addActionContext('cancel-show', 'json')
@ -64,7 +64,7 @@ class ScheduleController extends Zend_Controller_Action
$formRepeats->removeDecorator('DtDdWrapper'); $formRepeats->removeDecorator('DtDdWrapper');
$formStyle->removeDecorator('DtDdWrapper'); $formStyle->removeDecorator('DtDdWrapper');
$formRecord->removeDecorator('DtDdWrapper'); $formRecord->removeDecorator('DtDdWrapper');
$this->view->what = $formWhat; $this->view->what = $formWhat;
$this->view->when = $formWhen; $this->view->when = $formWhen;
@ -84,7 +84,7 @@ class ScheduleController extends Zend_Controller_Action
{ {
$start = $this->_getParam('start', null); $start = $this->_getParam('start', null);
$end = $this->_getParam('end', null); $end = $this->_getParam('end', null);
$userInfo = Zend_Auth::getInstance()->getStorage()->read(); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
$user = new User($userInfo->id); $user = new User($userInfo->id);
if($user->isAdmin()) if($user->isAdmin())
@ -111,6 +111,7 @@ class ScheduleController extends Zend_Controller_Action
if(isset($error)) if(isset($error))
$this->view->error = $error; $this->view->error = $error;
} }
public function resizeShowAction() public function resizeShowAction()
@ -134,7 +135,7 @@ class ScheduleController extends Zend_Controller_Action
public function deleteShowAction() public function deleteShowAction()
{ {
$showInstanceId = $this->_getParam('id'); $showInstanceId = $this->_getParam('id');
$userInfo = Zend_Auth::getInstance()->getStorage()->read(); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
$user = new User($userInfo->id); $user = new User($userInfo->id);
@ -158,41 +159,41 @@ class ScheduleController extends Zend_Controller_Action
if (strtotime($today_timestamp) < strtotime($show->getShowStart())) { if (strtotime($today_timestamp) < strtotime($show->getShowStart())) {
if (($user->isHost($show->getShowId()) || $user->isAdmin()) && !$show->isRecorded() && !$show->isRebroadcast()) { if (($user->isHost($show->getShowId()) || $user->isAdmin()) && !$show->isRecorded() && !$show->isRebroadcast()) {
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/schedule-show-dialog'.$params,
'callback' => 'window["buildScheduleDialog"]'), 'title' => 'Add Content');
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/clear-show'.$params, $menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/schedule-show-dialog'.$params,
'callback' => 'window["buildScheduleDialog"]'), 'title' => 'Add / Remove Content');
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/clear-show'.$params,
'callback' => 'window["scheduleRefetchEvents"]'), 'title' => 'Remove All Content'); 'callback' => 'window["scheduleRefetchEvents"]'), 'title' => 'Remove All Content');
} }
} }
if(!$show->isRecorded()) { if(!$show->isRecorded()) {
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/show-content-dialog'.$params, $menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/show-content-dialog'.$params,
'callback' => 'window["buildContentDialog"]'), 'title' => 'Show Content'); 'callback' => 'window["buildContentDialog"]'), 'title' => 'Show Content');
} }
if (strtotime($show->getShowStart()) <= strtotime($today_timestamp) && if (strtotime($show->getShowStart()) <= strtotime($today_timestamp) &&
strtotime($today_timestamp) < strtotime($show->getShowEnd())) { strtotime($today_timestamp) < strtotime($show->getShowEnd())) {
$menu[] = array('action' => array('type' => 'fn', $menu[] = array('action' => array('type' => 'fn',
//'url' => '/Schedule/cancel-current-show'.$params, //'url' => '/Schedule/cancel-current-show'.$params,
'callback' => "window['confirmCancelShow']($id)"), 'callback' => "window['confirmCancelShow']($id)"),
'title' => 'Cancel Current Show'); 'title' => 'Cancel Current Show');
} }
if (strtotime($today_timestamp) < strtotime($show->getShowStart())) { if (strtotime($today_timestamp) < strtotime($show->getShowStart())) {
if ($user->isAdmin()) { if ($user->isAdmin()) {
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/delete-show'.$params, $menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/delete-show'.$params,
'callback' => 'window["scheduleRefetchEvents"]'), 'title' => 'Delete This Instance'); 'callback' => 'window["scheduleRefetchEvents"]'), 'title' => 'Delete This Instance');
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/cancel-show'.$params, $menu[] = array('action' => array('type' => 'ajax', 'url' => '/Schedule/cancel-show'.$params,
'callback' => 'window["scheduleRefetchEvents"]'), 'title' => 'Delete This Instance and All Following'); 'callback' => 'window["scheduleRefetchEvents"]'), 'title' => 'Delete This Instance and All Following');
} }
} }
//returns format jjmenu is looking for. //returns format jjmenu is looking for.
die(json_encode($menu)); die(json_encode($menu));
} }
@ -219,7 +220,7 @@ class ScheduleController extends Zend_Controller_Action
$this->view->timeFilled = $show->getTimeScheduled(); $this->view->timeFilled = $show->getTimeScheduled();
$this->view->percentFilled = $show->getPercentScheduled(); $this->view->percentFilled = $show->getPercentScheduled();
$this->view->chosen = $this->view->render('schedule/scheduled-content.phtml'); $this->view->chosen = $this->view->render('schedule/scheduled-content.phtml');
unset($this->view->showContent); unset($this->view->showContent);
} }
@ -242,7 +243,7 @@ class ScheduleController extends Zend_Controller_Action
public function findPlaylistsAction() public function findPlaylistsAction()
{ {
$post = $this->getRequest()->getPost(); $post = $this->getRequest()->getPost();
$show = new ShowInstance($this->sched_sess->showInstanceId); $show = new ShowInstance($this->sched_sess->showInstanceId);
$playlists = $show->searchPlaylistsForShow($post); $playlists = $show->searchPlaylistsForShow($post);
@ -267,7 +268,7 @@ class ScheduleController extends Zend_Controller_Action
$this->view->showContent = $show->getShowContent(); $this->view->showContent = $show->getShowContent();
$this->view->timeFilled = $show->getTimeScheduled(); $this->view->timeFilled = $show->getTimeScheduled();
$this->view->percentFilled = $show->getPercentScheduled(); $this->view->percentFilled = $show->getPercentScheduled();
$this->view->chosen = $this->view->render('schedule/scheduled-content.phtml'); $this->view->chosen = $this->view->render('schedule/scheduled-content.phtml');
unset($this->view->showContent); unset($this->view->showContent);
} }
@ -275,7 +276,7 @@ class ScheduleController extends Zend_Controller_Action
{ {
$showInstanceId = $this->_getParam('id'); $showInstanceId = $this->_getParam('id');
$this->sched_sess->showInstanceId = $showInstanceId; $this->sched_sess->showInstanceId = $showInstanceId;
$show = new ShowInstance($showInstanceId); $show = new ShowInstance($showInstanceId);
$start_timestamp = $show->getShowStart(); $start_timestamp = $show->getShowStart();
$end_timestamp = $show->getShowEnd(); $end_timestamp = $show->getShowEnd();
@ -285,14 +286,14 @@ class ScheduleController extends Zend_Controller_Action
$this->view->error = "cannot schedule an overlapping show."; $this->view->error = "cannot schedule an overlapping show.";
return; return;
} }
$start = explode(" ", $start_timestamp); $start = explode(" ", $start_timestamp);
$end = explode(" ", $end_timestamp); $end = explode(" ", $end_timestamp);
$startTime = explode(":", $start[1]); $startTime = explode(":", $start[1]);
$endTime = explode(":", $end[1]); $endTime = explode(":", $end[1]);
$dateInfo_s = getDate(strtotime($start_timestamp)); $dateInfo_s = getDate(strtotime($start_timestamp));
$dateInfo_e = getDate(strtotime($end_timestamp)); $dateInfo_e = getDate(strtotime($end_timestamp));
$this->view->showContent = $show->getShowContent(); $this->view->showContent = $show->getShowContent();
$this->view->timeFilled = $show->getTimeScheduled(); $this->view->timeFilled = $show->getTimeScheduled();
$this->view->showName = $show->getName(); $this->view->showName = $show->getName();
@ -308,7 +309,7 @@ class ScheduleController extends Zend_Controller_Action
$this->view->startTime = sprintf("%d:%02d", $startTime[0], $startTime[1]); $this->view->startTime = sprintf("%d:%02d", $startTime[0], $startTime[1]);
$this->view->endTime = sprintf("%d:%02d", $endTime[0], $endTime[1]); $this->view->endTime = sprintf("%d:%02d", $endTime[0], $endTime[1]);
$this->view->chosen = $this->view->render('schedule/scheduled-content.phtml'); $this->view->chosen = $this->view->render('schedule/scheduled-content.phtml');
$this->view->dialog = $this->view->render('schedule/schedule-show-dialog.phtml'); $this->view->dialog = $this->view->render('schedule/schedule-show-dialog.phtml');
unset($this->view->showContent); unset($this->view->showContent);
} }
@ -335,7 +336,7 @@ class ScheduleController extends Zend_Controller_Action
{ {
$js = $this->_getParam('data'); $js = $this->_getParam('data');
$data = array(); $data = array();
//need to convert from serialized jQuery array. //need to convert from serialized jQuery array.
foreach($js as $j){ foreach($js as $j){
$data[$j["name"]] = $j["value"]; $data[$j["name"]] = $j["value"];
@ -392,8 +393,8 @@ class ScheduleController extends Zend_Controller_Action
$rebroadAb = $formAbsoluteRebroadcast->isValid($data); $rebroadAb = $formAbsoluteRebroadcast->isValid($data);
$rebroad = $formRebroadcast->isValid($data); $rebroad = $formRebroadcast->isValid($data);
if ($what && $when && $repeats && $who && $style && $record && $rebroadAb && $rebroad) { if ($what && $when && $repeats && $who && $style && $record && $rebroadAb && $rebroad) {
$userInfo = Zend_Auth::getInstance()->getStorage()->read(); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
$user = new User($userInfo->id); $user = new User($userInfo->id);
if($user->isAdmin()) { if($user->isAdmin()) {
@ -413,7 +414,7 @@ class ScheduleController extends Zend_Controller_Action
$formRecord->reset(); $formRecord->reset();
$formAbsoluteRebroadcast->reset(); $formAbsoluteRebroadcast->reset();
$formRebroadcast->reset(); $formRebroadcast->reset();
$this->view->newForm = $this->view->render('schedule/add-show-form.phtml'); $this->view->newForm = $this->view->render('schedule/add-show-form.phtml');
} }
else { else {
@ -426,7 +427,7 @@ class ScheduleController extends Zend_Controller_Action
{ {
$userInfo = Zend_Auth::getInstance()->getStorage()->read(); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
$user = new User($userInfo->id); $user = new User($userInfo->id);
if($user->isAdmin()) { if($user->isAdmin()) {
$showInstanceId = $this->_getParam('id'); $showInstanceId = $this->_getParam('id');
@ -434,14 +435,14 @@ class ScheduleController extends Zend_Controller_Action
$show = new Show($showInstance->getShowId()); $show = new Show($showInstance->getShowId());
$show->cancelShow($showInstance->getShowStart()); $show->cancelShow($showInstance->getShowStart());
} }
} }
public function cancelCurrentShowAction() public function cancelCurrentShowAction()
{ {
$userInfo = Zend_Auth::getInstance()->getStorage()->read(); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
$user = new User($userInfo->id); $user = new User($userInfo->id);
if($user->isAdmin()) { if($user->isAdmin()) {
$showInstanceId = $this->_getParam('id'); $showInstanceId = $this->_getParam('id');
$show = new ShowInstance($showInstanceId); $show = new ShowInstance($showInstanceId);

View File

@ -23,6 +23,8 @@ class UserController extends Zend_Controller_Action
$this->view->headScript()->appendFile('/js/airtime/user/user.js','text/javascript'); $this->view->headScript()->appendFile('/js/airtime/user/user.js','text/javascript');
$request = $this->getRequest(); $request = $this->getRequest();
$form = new Application_Form_AddUser(); $form = new Application_Form_AddUser();
$this->view->successMessage = "";
if ($request->isPost()) { if ($request->isPost()) {
if ($form->isValid($request->getPost())) { if ($form->isValid($request->getPost())) {
@ -42,6 +44,12 @@ class UserController extends Zend_Controller_Action
$user->save(); $user->save();
$form->reset(); $form->reset();
if (strlen($formdata['user_id']) == 0){
$this->view->successMessage = "<div class='success'>User added successfully!</div>";
} else {
$this->view->successMessage = "<div class='success'>User updated successfully!</div>";
}
} }
} }
} }

View File

@ -110,7 +110,7 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
{ {
$controller = strtolower($request->getControllerName()); $controller = strtolower($request->getControllerName());
if ($controller == 'api' || $controller == 'recorder' || $controller == 'plupload' && $request->getActionName() == 'upload-recorded'){ if ($controller == 'api'){
$this->setRoleName("G"); $this->setRoleName("G");
} }

View File

@ -0,0 +1,9 @@
<?php
class RabbitMqPlugin extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopShutdown()
{
RabbitMq::PushScheduleFinal();
}
}

View File

@ -9,17 +9,18 @@ class Application_Form_AddShowRebroadcastDates extends Zend_Form_SubForm
array('ViewScript', array('viewScript' => 'form/add-show-rebroadcast.phtml')) array('ViewScript', array('viewScript' => 'form/add-show-rebroadcast.phtml'))
)); ));
$relativeDates = array();
$relativeDates[""] = "";
for($i=0; $i <=30; $i++) {
$relativeDates["$i days"] = "+$i days";
}
//Add date select //Add date select
$this->addElement('select', 'add_show_rebroadcast_date_1', array( $this->addElement('select', 'add_show_rebroadcast_date_1', array(
'required' => false, 'required' => false,
'class' => ' input_select', 'class' => ' input_select',
'multiOptions' => array( 'multiOptions' => $relativeDates,
"" => "",
"0 days" => "+0 days",
"1 day" => "+1 day",
"2 days" => "+2 days",
"3 days" => "+3 days"
),
'decorators' => array( 'decorators' => array(
'ViewHelper' 'ViewHelper'
) )
@ -44,13 +45,7 @@ class Application_Form_AddShowRebroadcastDates extends Zend_Form_SubForm
$this->addElement('select', 'add_show_rebroadcast_date_2', array( $this->addElement('select', 'add_show_rebroadcast_date_2', array(
'required' => false, 'required' => false,
'class' => ' input_select', 'class' => ' input_select',
'multiOptions' => array( 'multiOptions' => $relativeDates,
"" => "",
"0 days" => "+0 days",
"1 day" => "+1 day",
"2 days" => "+2 days",
"3 days" => "+3 days"
),
'decorators' => array( 'decorators' => array(
'ViewHelper' 'ViewHelper'
) )
@ -75,13 +70,7 @@ class Application_Form_AddShowRebroadcastDates extends Zend_Form_SubForm
$this->addElement('select', 'add_show_rebroadcast_date_3', array( $this->addElement('select', 'add_show_rebroadcast_date_3', array(
'required' => false, 'required' => false,
'class' => ' input_select', 'class' => ' input_select',
'multiOptions' => array( 'multiOptions' => $relativeDates,
"" => "",
"0 days" => "+0 days",
"1 day" => "+1 day",
"2 days" => "+2 days",
"3 days" => "+3 days"
),
'decorators' => array( 'decorators' => array(
'ViewHelper' 'ViewHelper'
) )
@ -106,13 +95,7 @@ class Application_Form_AddShowRebroadcastDates extends Zend_Form_SubForm
$this->addElement('select', 'add_show_rebroadcast_date_4', array( $this->addElement('select', 'add_show_rebroadcast_date_4', array(
'required' => false, 'required' => false,
'class' => ' input_select', 'class' => ' input_select',
'multiOptions' => array( 'multiOptions' => $relativeDates,
"" => "",
"0 days" => "+0 days",
"1 day" => "+1 day",
"2 days" => "+2 days",
"3 days" => "+3 days"
),
'decorators' => array( 'decorators' => array(
'ViewHelper' 'ViewHelper'
) )
@ -137,13 +120,7 @@ class Application_Form_AddShowRebroadcastDates extends Zend_Form_SubForm
$this->addElement('select', 'add_show_rebroadcast_date_5', array( $this->addElement('select', 'add_show_rebroadcast_date_5', array(
'required' => false, 'required' => false,
'class' => ' input_select', 'class' => ' input_select',
'multiOptions' => array( 'multiOptions' => $relativeDates,
"" => "",
"0 days" => "+0 days",
"1 day" => "+1 day",
"2 days" => "+2 days",
"3 days" => "+3 days"
),
'decorators' => array( 'decorators' => array(
'ViewHelper' 'ViewHelper'
) )

View File

@ -16,7 +16,7 @@ class Application_Form_AddShowWhat extends Zend_Form_SubForm
// Add URL element // Add URL element
$this->addElement('text', 'add_show_url', array( $this->addElement('text', 'add_show_url', array(
'label' => 'Show URL:', 'label' => 'Website:',
'class' => 'input_text', 'class' => 'input_text',
'required' => false, 'required' => false,
'filters' => array('StringTrim'), 'filters' => array('StringTrim'),

View File

@ -68,12 +68,13 @@ class Application_Form_AddUser extends Zend_Form
$this->addElement($jabber); $this->addElement($jabber);
$select = new Zend_Form_Element_Select('type'); $select = new Zend_Form_Element_Select('type');
$select->setLabel('User Type:');
$select->setAttrib('class', 'input_select'); $select->setAttrib('class', 'input_select');
$select->setAttrib('style', 'width: 40%'); $select->setAttrib('style', 'width: 40%');
$select->setMultiOptions(array( $select->setMultiOptions(array(
"A" => "admin", "G" => "Guest",
"H" => "host", "H" => "Host",
"G" => "guest", "A" => "Admin"
)); ));
$select->setRequired(true); $select->setRequired(true);
$this->addElement($select); $this->addElement($select);

View File

@ -7,7 +7,7 @@ class Application_Form_Preferences extends Zend_Form
{ {
$this->setAction('/Preference/update')->setMethod('post'); $this->setAction('/Preference/update')->setMethod('post');
// Add login element //Station name
$this->addElement('text', 'stationName', array( $this->addElement('text', 'stationName', array(
'class' => 'input_text', 'class' => 'input_text',
'label' => 'Station Name:', 'label' => 'Station Name:',
@ -17,12 +17,12 @@ class Application_Form_Preferences extends Zend_Form
'value' => Application_Model_Preference::GetValue("station_name") 'value' => Application_Model_Preference::GetValue("station_name")
)); ));
$defaultFade = Application_Model_Preference::GetValue("default_fade"); $defaultFade = Application_Model_Preference::GetDefaultFade();
if($defaultFade == ""){ if($defaultFade == ""){
$defaultFade = '00:00:00.000000'; $defaultFade = '00:00:00.000000';
} }
// Add login element //Default station fade
$this->addElement('text', 'stationDefaultFade', array( $this->addElement('text', 'stationDefaultFade', array(
'class' => 'input_text', 'class' => 'input_text',
'label' => 'Default Fade:', 'label' => 'Default Fade:',
@ -42,11 +42,46 @@ class Application_Form_Preferences extends Zend_Form
$stream_format->setValue(Application_Model_Preference::GetStreamLabelFormat()); $stream_format->setValue(Application_Model_Preference::GetStreamLabelFormat());
$this->addElement($stream_format); $this->addElement($stream_format);
$this->addElement('checkbox', 'UseSoundCloud', array(
'label' => 'Automatically Upload Recorded Shows To SoundCloud',
'required' => false,
'value' => Application_Model_Preference::GetDoSoundCloudUpload()
));
//SoundCloud Username
$this->addElement('text', 'SoundCloudUser', array(
'class' => 'input_text',
'label' => 'SoundCloud Email:',
'required' => false,
'filters' => array('StringTrim'),
'value' => Application_Model_Preference::GetSoundCloudUser()
));
//SoundCloud Password
$this->addElement('text', 'SoundCloudPassword', array(
'class' => 'input_text',
'label' => 'SoundCloud Password:',
'required' => false,
'filters' => array('StringTrim'),
'value' => Application_Model_Preference::GetSoundCloudPassword()
));
// Add the description element
$this->addElement('textarea', 'SoundCloudTags', array(
'label' => 'space separated SoundCloud Tags',
'required' => false,
'class' => 'input_text_area',
'value' => Application_Model_Preference::GetSoundCloudTags()
));
$this->addElement('submit', 'submit', array( $this->addElement('submit', 'submit', array(
'class' => 'ui-button ui-state-default', 'class' => 'ui-button ui-state-default',
'ignore' => true, 'ignore' => true,
'label' => 'Submit', 'label' => 'Submit',
)); ));
} }
} }

View File

@ -2,61 +2,85 @@
class Application_Model_DateHelper class Application_Model_DateHelper
{ {
private $_timestamp; private $_timestamp;
function __construct() { function __construct()
{
$this->_timestamp = date("U"); $this->_timestamp = date("U");
} }
function getDate(){ /**
return date("Y-m-d H:i:s", $this->_timestamp); * Get time of object construction in the format
} * YYYY-MM-DD HH:mm:ss
*/
function getTime(){ function getDate()
return date("H:i:s", $this->_timestamp); {
} return date("Y-m-d H:i:s", $this->_timestamp);
}
function setDate($dateString){
/**
* Get time of object construction in the format
* HH:mm:ss
*/
function getTime()
{
return date("H:i:s", $this->_timestamp);
}
/**
* Set the internal timestamp of the object.
*/
function setDate($dateString)
{
$this->_timestamp = strtotime($dateString); $this->_timestamp = strtotime($dateString);
} }
function getNowDayStartDiff(){
$dayStartTS = strtotime(date("Y-m-d", $this->_timestamp));
return $this->_timestamp - $dayStartTS;
}
function getNowDayEndDiff(){ /**
$dayEndTS = strtotime(date("Y-m-d", $this->_timestamp+(86400))); *
return $dayEndTS - $this->_timestamp; * Enter description here ...
} */
function getNowDayStartDiff()
{
$dayStartTS = strtotime(date("Y-m-d", $this->_timestamp));
return $this->_timestamp - $dayStartTS;
}
function getEpochTime(){ function getNowDayEndDiff()
{
$dayEndTS = strtotime(date("Y-m-d", $this->_timestamp+(86400)));
return $dayEndTS - $this->_timestamp;
}
function getEpochTime()
{
return $this->_timestamp; return $this->_timestamp;
} }
public static function TimeDiff($time1, $time2){ public static function TimeDiff($time1, $time2)
{
return strtotime($time2) - strtotime($time1); return strtotime($time2) - strtotime($time1);
} }
public static function ConvertMSToHHMMSSmm($time){ public static function ConvertMSToHHMMSSmm($time)
{
$hours = floor($time / 3600000); $hours = floor($time / 3600000);
$time -= 3600000*$hours; $time -= 3600000*$hours;
$minutes = floor($time / 60000); $minutes = floor($time / 60000);
$time -= 60000*$minutes; $time -= 60000*$minutes;
$seconds = floor($time / 1000); $seconds = floor($time / 1000);
$time -= 1000*$seconds; $time -= 1000*$seconds;
$ms = $time; $ms = $time;
if (strlen($hours) == 1) if (strlen($hours) == 1)
$hours = "0".$hours; $hours = "0".$hours;
if (strlen($minutes) == 1) if (strlen($minutes) == 1)
$minutes = "0".$minutes; $minutes = "0".$minutes;
if (strlen($seconds) == 1) if (strlen($seconds) == 1)
$seconds = "0".$seconds; $seconds = "0".$seconds;
return $hours.":".$minutes.":".$seconds.".".$ms; return $hours.":".$minutes.":".$seconds.".".$ms;
} }
} }

View File

@ -8,30 +8,30 @@ class Application_Model_Preference
$auth = Zend_Auth::getInstance(); $auth = Zend_Auth::getInstance();
$id = $auth->getIdentity()->id; $id = $auth->getIdentity()->id;
//Check if key already exists //Check if key already exists
$sql = "SELECT COUNT(*) FROM cc_pref" $sql = "SELECT COUNT(*) FROM cc_pref"
." WHERE keystr = '$key'"; ." WHERE keystr = '$key'";
$result = $CC_DBC->GetOne($sql); $result = $CC_DBC->GetOne($sql);
if ($result == 1){ if ($result == 1){
$sql = "UPDATE cc_pref" $sql = "UPDATE cc_pref"
." SET subjid = $id, valstr = '$value'" ." SET subjid = $id, valstr = '$value'"
." WHERE keystr = '$key'"; ." WHERE keystr = '$key'";
} else { } else {
$sql = "INSERT INTO cc_pref (subjid, keystr, valstr)" $sql = "INSERT INTO cc_pref (subjid, keystr, valstr)"
." VALUES ($id, '$key', '$value')"; ." VALUES ($id, '$key', '$value')";
} }
return $CC_DBC->query($sql); return $CC_DBC->query($sql);
} }
public static function GetValue($key){ public static function GetValue($key){
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
//Check if key already exists //Check if key already exists
$sql = "SELECT COUNT(*) FROM cc_pref" $sql = "SELECT COUNT(*) FROM cc_pref"
." WHERE keystr = '$key'"; ." WHERE keystr = '$key'";
$result = $CC_DBC->GetOne($sql); $result = $CC_DBC->GetOne($sql);
if ($result == 0) if ($result == 0)
return ""; return "";
else { else {
@ -40,9 +40,9 @@ class Application_Model_Preference
$result = $CC_DBC->GetOne($sql); $result = $CC_DBC->GetOne($sql);
return $result; return $result;
} }
} }
public static function GetHeadTitle(){ public static function GetHeadTitle(){
/* Caches the title name as a session variable so we dont access /* Caches the title name as a session variable so we dont access
* the database on every page load. */ * the database on every page load. */
@ -55,31 +55,32 @@ class Application_Model_Preference
} }
if (strlen($title) > 0) if (strlen($title) > 0)
$title .= " - "; $title .= " - ";
return $title."Airtime"; return $title."Airtime";
} }
public static function SetHeadTitle($title, $view){ public static function SetHeadTitle($title, $view){
Application_Model_Preference::SetValue("station_name", $title); Application_Model_Preference::SetValue("station_name", $title);
$defaultNamespace = new Zend_Session_Namespace('title_name'); $defaultNamespace = new Zend_Session_Namespace('title_name');
$defaultNamespace->title = $title; $defaultNamespace->title = $title;
RabbitMq::PushSchedule();
//set session variable to new station name so that html title is updated. //set session variable to new station name so that html title is updated.
//should probably do this in a view helper to keep this controller as minimal as possible. //should probably do this in a view helper to keep this controller as minimal as possible.
$view->headTitle()->exchangeArray(array()); //clear headTitle ArrayObject $view->headTitle()->exchangeArray(array()); //clear headTitle ArrayObject
$view->headTitle(Application_Model_Preference::GetHeadTitle()); $view->headTitle(Application_Model_Preference::GetHeadTitle());
} }
public static function SetShowsPopulatedUntil($timestamp) { public static function SetShowsPopulatedUntil($timestamp) {
Application_Model_Preference::SetValue("shows_populated_until", $timestamp); Application_Model_Preference::SetValue("shows_populated_until", $timestamp);
} }
public static function GetShowsPopulatedUntil() { public static function GetShowsPopulatedUntil() {
return Application_Model_Preference::GetValue("shows_populated_until"); return Application_Model_Preference::GetValue("shows_populated_until");
} }
public static function SetDefaultFade($fade) { public static function SetDefaultFade($fade) {
Application_Model_Preference::SetValue("default_fade", $fade); Application_Model_Preference::SetValue("default_fade", $fade);
} }
public static function GetDefaultFade() { public static function GetDefaultFade() {
@ -88,6 +89,7 @@ class Application_Model_Preference
public static function SetStreamLabelFormat($type){ public static function SetStreamLabelFormat($type){
Application_Model_Preference::SetValue("stream_label_format", $type); Application_Model_Preference::SetValue("stream_label_format", $type);
RabbitMq::PushSchedule();
} }
public static function GetStreamLabelFormat(){ public static function GetStreamLabelFormat(){
@ -97,5 +99,38 @@ class Application_Model_Preference
public static function GetStationName(){ public static function GetStationName(){
return Application_Model_Preference::getValue("station_name"); return Application_Model_Preference::getValue("station_name");
} }
public static function SetDoSoundCloudUpload($upload) {
Application_Model_Preference::SetValue("soundcloud_upload", $upload);
}
public static function GetDoSoundCloudUpload() {
return Application_Model_Preference::GetValue("soundcloud_upload");
}
public static function SetSoundCloudUser($user) {
Application_Model_Preference::SetValue("soundcloud_user", $user);
}
public static function GetSoundCloudUser() {
return Application_Model_Preference::GetValue("soundcloud_user");
}
public static function SetSoundCloudPassword($password) {
Application_Model_Preference::SetValue("soundcloud_password", $password);
}
public static function GetSoundCloudPassword() {
return Application_Model_Preference::GetValue("soundcloud_password");
}
public static function SetSoundCloudTags($tags) {
Application_Model_Preference::SetValue("soundcloud_tags", $tags);
}
public static function GetSoundCloudTags() {
return Application_Model_Preference::GetValue("soundcloud_tags");
}
} }

View File

@ -0,0 +1,44 @@
<?php
require_once 'php-amqplib/amqp.inc';
class RabbitMq
{
static private $doPush = FALSE;
/**
* Sets a flag to push the schedule at the end of the request.
*/
public static function PushSchedule() {
RabbitMq::$doPush = TRUE;
}
/**
* Push the current schedule to RabbitMQ, to be picked up by Pypo.
* Will push the schedule in the range from 24 hours ago to 24 hours
* in the future.
*/
public static function PushScheduleFinal()
{
global $CC_CONFIG;
if (RabbitMq::$doPush) {
$conn = new AMQPConnection($CC_CONFIG["rabbitmq"]["host"],
$CC_CONFIG["rabbitmq"]["port"],
$CC_CONFIG["rabbitmq"]["user"],
$CC_CONFIG["rabbitmq"]["password"]);
$channel = $conn->channel();
$channel->access_request($CC_CONFIG["rabbitmq"]["vhost"], false, false, true, true);
$EXCHANGE = 'airtime-schedule';
$channel->exchange_declare($EXCHANGE, 'direct', false, true);
$data = json_encode(Schedule::GetScheduledPlaylists());
$msg = new AMQPMessage($data, array('content_type' => 'text/plain'));
$channel->basic_publish($msg, $EXCHANGE);
$channel->close();
$conn->close();
}
}
}

View File

@ -24,41 +24,6 @@ class ScheduleGroup {
return $result != "0"; return $result != "0";
} }
/**
* Convert a date to an ID by stripping out all characters
* and padding with zeros.
*
* @param string $p_dateStr
*/
public static function dateToId($p_dateStr) {
$p_dateStr = str_replace(":", "", $p_dateStr);
$p_dateStr = str_replace(" ", "", $p_dateStr);
$p_dateStr = str_replace(".", "", $p_dateStr);
$p_dateStr = str_replace("-", "", $p_dateStr);
$p_dateStr = substr($p_dateStr, 0, 17);
$p_dateStr = str_pad($p_dateStr, 17, "0");
return $p_dateStr;
}
/**
* Add the two times together, return the result.
*
* @param string $p_baseTime
* Specified as YYYY-MM-DD HH:MM:SS
*
* @param string $p_addTime
* Specified as HH:MM:SS.nnnnnn
*
* @return string
* The end time, to the nearest second.
*/
// protected function calculateEndTime($p_startTime, $p_trackTime) {
// $p_trackTime = substr($p_startTime, 0, );
// $start = new DateTime();
// $interval = new DateInterval()
//
// }
/** /**
* Add a music clip or playlist to the schedule. * Add a music clip or playlist to the schedule.
* *
@ -77,6 +42,7 @@ class ScheduleGroup {
*/ */
public function add($show_instance, $p_datetime, $p_audioFileId = null, $p_playlistId = null, $p_options = null) { public function add($show_instance, $p_datetime, $p_audioFileId = null, $p_playlistId = null, $p_options = null) {
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
if (!is_null($p_audioFileId)) { if (!is_null($p_audioFileId)) {
// Schedule a single audio track // Schedule a single audio track
@ -92,27 +58,24 @@ class ScheduleGroup {
if (empty($length)) { if (empty($length)) {
return new PEAR_Error("Length is empty."); return new PEAR_Error("Length is empty.");
} }
if (!Schedule::isScheduleEmptyInRange($p_datetime, $length)) {
return new PEAR_Error("Schedule conflict.", 555);
}
// Insert into the table // Insert into the table
$this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')"); $this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')");
$id = $this->dateToId($p_datetime);
$sql = "INSERT INTO ".$CC_CONFIG["scheduleTable"] $sql = "INSERT INTO ".$CC_CONFIG["scheduleTable"]
." (playlist_id, starts, ends, clip_length, group_id, file_id)" ." (instance_id, starts, ends, clip_length, group_id, file_id, cue_out)"
." VALUES (0, TIMESTAMP '$p_datetime', " ." VALUES ($show_instance, TIMESTAMP '$p_datetime', "
." (TIMESTAMP '$p_datetime' + INTERVAL '$length')," ." (TIMESTAMP '$p_datetime' + INTERVAL '$length'),"
." '$length'," ." '$length',"
." {$this->groupId}, $p_audioFileId)"; ." {$this->groupId}, $p_audioFileId, '$length')";
$result = $CC_DBC->query($sql); $result = $CC_DBC->query($sql);
if (PEAR::isError($result)) { if (PEAR::isError($result)) {
//var_dump($sql); //var_dump($sql);
return $result; return $result;
} }
return $this->groupId;
} elseif (!is_null($p_playlistId)){ }
elseif (!is_null($p_playlistId)){
// Schedule a whole playlist // Schedule a whole playlist
// Load existing playlist // Load existing playlist
@ -130,7 +93,6 @@ class ScheduleGroup {
// Insert all items into the schedule // Insert all items into the schedule
$this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')"); $this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')");
$id = $this->dateToId($p_datetime);
$itemStartTime = $p_datetime; $itemStartTime = $p_datetime;
$plItems = $playlist->getContents(); $plItems = $playlist->getContents();
@ -151,13 +113,13 @@ class ScheduleGroup {
return $result; return $result;
} }
$itemStartTime = $CC_DBC->getOne("SELECT TIMESTAMP '$itemStartTime' + INTERVAL '$trackLength'"); $itemStartTime = $CC_DBC->getOne("SELECT TIMESTAMP '$itemStartTime' + INTERVAL '$trackLength'");
$id = $this->dateToId($itemStartTime);
} }
return $this->groupId;
} }
RabbitMq::PushSchedule();
return $this->groupId;
} }
public function addAfter($show_instance, $p_groupId, $p_audioFileId) { public function addFileAfter($show_instance, $p_groupId, $p_audioFileId) {
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
// Get the end time for the given entry // Get the end time for the given entry
$sql = "SELECT MAX(ends) FROM ".$CC_CONFIG["scheduleTable"] $sql = "SELECT MAX(ends) FROM ".$CC_CONFIG["scheduleTable"]
@ -176,10 +138,6 @@ class ScheduleGroup {
return $this->add($show_instance, $startTime, null, $p_playlistId); return $this->add($show_instance, $startTime, null, $p_playlistId);
} }
public function update() {
}
/** /**
* Remove the group from the schedule. * Remove the group from the schedule.
* Note: does not check if it is in the past, you can remove anything. * Note: does not check if it is in the past, you can remove anything.
@ -195,7 +153,9 @@ class ScheduleGroup {
$sql = "DELETE FROM ".$CC_CONFIG["scheduleTable"] $sql = "DELETE FROM ".$CC_CONFIG["scheduleTable"]
." WHERE group_id = ".$this->groupId; ." WHERE group_id = ".$this->groupId;
//echo $sql; //echo $sql;
return $CC_DBC->query($sql); $retVal = $CC_DBC->query($sql);
RabbitMq::PushSchedule();
return $retVal;
} }
/** /**
@ -231,17 +191,13 @@ class ScheduleGroup {
return $CC_DBC->GetAll($sql); return $CC_DBC->GetAll($sql);
} }
public function reschedule($toDateTime) {
global $CC_CONFIG, $CC_DBC;
// $sql = "UPDATE ".$CC_CONFIG["scheduleTable"]. " SET id=, starts=,ends="
}
public function notifyGroupStartPlay() { public function notifyGroupStartPlay() {
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$sql = "UPDATE ".$CC_CONFIG['scheduleTable'] $sql = "UPDATE ".$CC_CONFIG['scheduleTable']
." SET schedule_group_played=TRUE" ." SET schedule_group_played=TRUE"
." WHERE group_id=".$this->groupId; ." WHERE group_id=".$this->groupId;
return $CC_DBC->query($sql); $retVal = $CC_DBC->query($sql);
return $retVal;
} }
public function notifyMediaItemStartPlay($p_fileId) { public function notifyMediaItemStartPlay($p_fileId) {
@ -250,7 +206,8 @@ class ScheduleGroup {
." SET media_item_played=TRUE" ." SET media_item_played=TRUE"
." WHERE group_id=".$this->groupId ." WHERE group_id=".$this->groupId
." AND file_id=".pg_escape_string($p_fileId); ." AND file_id=".pg_escape_string($p_fileId);
return $CC_DBC->query($sql); $retVal = $CC_DBC->query($sql);
return $retVal;
} }
} }
@ -334,9 +291,10 @@ class Schedule {
return $res; return $res;
} }
public static function GetPercentScheduled($instance_id, $s_datetime, $e_datetime){ public static function GetPercentScheduled($instance_id, $s_datetime, $e_datetime)
{
$time = Schedule::GetTotalShowTime($instance_id); $time = Schedule::GetTotalShowTime($instance_id);
$s_epoch = strtotime($s_datetime); $s_epoch = strtotime($s_datetime);
$e_epoch = strtotime($e_datetime); $e_epoch = strtotime($e_datetime);
@ -396,12 +354,14 @@ class Schedule {
* @return array * @return array
* Returns empty array if nothing found * Returns empty array if nothing found
*/ */
public static function GetItems($p_fromDateTime, $p_toDateTime, $p_playlistsOnly = true) {
public static function GetItems($p_currentDateTime, $p_toDateTime, $p_playlistsOnly = true)
{
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$rows = array(); $rows = array();
if (!$p_playlistsOnly) { if (!$p_playlistsOnly) {
$sql = "SELECT * FROM ".$CC_CONFIG["scheduleTable"] $sql = "SELECT * FROM ".$CC_CONFIG["scheduleTable"]
." WHERE (starts >= TIMESTAMP '$p_fromDateTime') " ." WHERE (starts >= TIMESTAMP '$p_currentDateTime') "
." AND (ends <= TIMESTAMP '$p_toDateTime')"; ." AND (ends <= TIMESTAMP '$p_toDateTime')";
$rows = $CC_DBC->GetAll($sql); $rows = $CC_DBC->GetAll($sql);
foreach ($rows as &$row) { foreach ($rows as &$row) {
@ -429,11 +389,11 @@ class Schedule {
." ON st.instance_id = si.id" ." ON st.instance_id = si.id"
." LEFT JOIN $CC_CONFIG[showTable] as sh" ." LEFT JOIN $CC_CONFIG[showTable] as sh"
." ON si.show_id = sh.id" ." ON si.show_id = sh.id"
." WHERE (st.starts >= TIMESTAMP '$p_fromDateTime')" ." WHERE (st.ends >= TIMESTAMP '$p_currentDateTime')"
." AND (st.ends <= TIMESTAMP '$p_toDateTime')" ." AND (st.ends <= TIMESTAMP '$p_toDateTime')"
//next line makes sure that we aren't returning items that //next line makes sure that we aren't returning items that
//are past the show's scheduled timeslot. //are past the show's scheduled timeslot.
." AND (st.starts < si.ends)" ." AND (st.starts < si.ends)"
." GROUP BY st.group_id" ." GROUP BY st.group_id"
." ORDER BY starts"; ." ORDER BY starts";
@ -457,7 +417,8 @@ class Schedule {
* @param int $next * @param int $next
* @return date * @return date
*/ */
public static function GetPlayOrderRange($prev = 1, $next = 1) { public static function GetPlayOrderRange($prev = 1, $next = 1)
{
if (!is_int($prev) || !is_int($next)){ if (!is_int($prev) || !is_int($next)){
//must enter integers to specify ranges //must enter integers to specify ranges
return array(); return array();
@ -469,11 +430,11 @@ class Schedule {
$timeNow = $date->getDate(); $timeNow = $date->getDate();
return array("env"=>APPLICATION_ENV, return array("env"=>APPLICATION_ENV,
"schedulerTime"=>gmdate("Y-m-d H:i:s"), "schedulerTime"=>gmdate("Y-m-d H:i:s"),
"previous"=>Schedule::Get_Scheduled_Item_Data($timeNow, -1, $prev, "24 hours"), "previous"=>Schedule::GetScheduledItemData($timeNow, -1, $prev, "24 hours"),
"current"=>Schedule::Get_Scheduled_Item_Data($timeNow, 0), "current"=>Schedule::GetScheduledItemData($timeNow, 0),
"next"=>Schedule::Get_Scheduled_Item_Data($timeNow, 1, $next, "48 hours"), "next"=>Schedule::GetScheduledItemData($timeNow, 1, $next, "48 hours"),
"currentShow"=>Show_DAL::GetCurrentShow($timeNow), "currentShow"=>Show_DAL::GetCurrentShow($timeNow),
"nextShow"=>Show_DAL::GetNextShow($timeNow), "nextShow"=>Show_DAL::GetNextShows($timeNow, 1),
"timezone"=> date("T"), "timezone"=> date("T"),
"timezoneOffset"=> date("Z"), "timezoneOffset"=> date("Z"),
"apiKey"=>$CC_CONFIG['apiKey'][0]); "apiKey"=>$CC_CONFIG['apiKey'][0]);
@ -501,7 +462,8 @@ class Schedule {
* want to search the database. For example "5 days", "18 hours", "60 minutes", * want to search the database. For example "5 days", "18 hours", "60 minutes",
* "30 seconds" etc. * "30 seconds" etc.
*/ */
public static function Get_Scheduled_Item_Data($timeStamp, $timePeriod=0, $count = 0, $interval="0 hours"){ public static function GetScheduledItemData($timeStamp, $timePeriod=0, $count = 0, $interval="0 hours")
{
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$sql = "SELECT DISTINCT pt.name, ft.track_title, ft.artist_name, ft.album_title, st.starts, st.ends, st.clip_length, st.media_item_played, st.group_id, show.name as show_name, st.instance_id" $sql = "SELECT DISTINCT pt.name, ft.track_title, ft.artist_name, ft.album_title, st.starts, st.ends, st.clip_length, st.media_item_played, st.group_id, show.name as show_name, st.instance_id"
@ -531,7 +493,8 @@ class Schedule {
return $rows; return $rows;
} }
public static function GetShowInstanceItems($instance_id){ public static function GetShowInstanceItems($instance_id)
{
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$sql = "SELECT DISTINCT pt.name, ft.track_title, ft.artist_name, ft.album_title, st.starts, st.ends, st.clip_length, st.media_item_played, st.group_id, show.name as show_name, st.instance_id" $sql = "SELECT DISTINCT pt.name, ft.track_title, ft.artist_name, ft.album_title, st.starts, st.ends, st.clip_length, st.media_item_played, st.group_id, show.name as show_name, st.instance_id"
@ -547,12 +510,14 @@ class Schedule {
return $rows; return $rows;
} }
public static function UpdateMediaPlayedStatus($id){ public static function UpdateMediaPlayedStatus($p_id)
{
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$sql = "UPDATE ".$CC_CONFIG['scheduleTable'] $sql = "UPDATE ".$CC_CONFIG['scheduleTable']
." SET media_item_played=TRUE" ." SET media_item_played=TRUE"
." WHERE id=$id"; ." WHERE id=$p_id";
return $CC_DBC->query($sql); $retVal = $CC_DBC->query($sql);
return $retVal;
} }
@ -563,7 +528,7 @@ class Schedule {
* @param string $p_time * @param string $p_time
* @return string * @return string
*/ */
private static function CcTimeToPypoTime($p_time) private static function AirtimeTimeToPypoTime($p_time)
{ {
$p_time = substr($p_time, 0, 19); $p_time = substr($p_time, 0, 19);
$p_time = str_replace(" ", "-", $p_time); $p_time = str_replace(" ", "-", $p_time);
@ -578,7 +543,7 @@ class Schedule {
* @param string $p_time * @param string $p_time
* @return string * @return string
*/ */
private static function PypoTimeToCcTime($p_time) private static function PypoTimeToAirtimeTime($p_time)
{ {
$t = explode("-", $p_time); $t = explode("-", $p_time);
return $t[0]."-".$t[1]."-".$t[2]." ".$t[3].":".$t[4].":00"; return $t[0]."-".$t[1]."-".$t[2]." ".$t[3].":".$t[4].":00";
@ -658,17 +623,21 @@ class Schedule {
/** /**
* Export the schedule in json formatted for pypo (the liquidsoap scheduler) * Export the schedule in json formatted for pypo (the liquidsoap scheduler)
* *
* @param string $range * @param string $p_fromDateTime
* In the format "YYYY-MM-DD HH:mm:ss" * In the format "YYYY-MM-DD-HH-mm-SS"
* @param string $source * @param string $p_toDateTime
* In the format "YYYY-MM-DD HH:mm:ss" * In the format "YYYY-MM-DD-HH-mm-SS"
*/ */
public static function ExportRangeAsJson($p_fromDateTime, $p_toDateTime) public static function GetScheduledPlaylists()
{ {
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$range_start = Schedule::PypoTimeToCcTime($p_fromDateTime); $t1 = new DateTime();
$range_end = Schedule::PypoTimeToCcTime($p_toDateTime); $range_start = $t1->format("Y-m-d H:i:s");
$t2 = new DateTime();
$t2->add(new DateInterval("PT24H"));
$range_end = $t2->format("Y-m-d H:i:s");
// Scheduler wants everything in a playlist // Scheduler wants everything in a playlist
$data = Schedule::GetItems($range_start, $range_end, true); $data = Schedule::GetItems($range_start, $range_end, true);
@ -684,10 +653,10 @@ class Schedule {
$start = substr($start, 0, 19); $start = substr($start, 0, 19);
//Start time is the array key, needs to be in the format "YYYY-MM-DD-HH-mm-ss" //Start time is the array key, needs to be in the format "YYYY-MM-DD-HH-mm-ss"
$pkey = Schedule::CcTimeToPypoTime($start); $pkey = Schedule::AirtimeTimeToPypoTime($start);
$timestamp = strtotime($start); $timestamp = strtotime($start);
$playlists[$pkey]['source'] = "PLAYLIST"; $playlists[$pkey]['source'] = "PLAYLIST";
$playlists[$pkey]['x_ident'] = $dx["playlist_id"]; $playlists[$pkey]['x_ident'] = $dx['group_id'];
$playlists[$pkey]['subtype'] = '1'; // Just needs to be between 1 and 4 inclusive $playlists[$pkey]['subtype'] = '1'; // Just needs to be between 1 and 4 inclusive
$playlists[$pkey]['timestamp'] = $timestamp; $playlists[$pkey]['timestamp'] = $timestamp;
$playlists[$pkey]['duration'] = $dx['clip_length']; $playlists[$pkey]['duration'] = $dx['clip_length'];
@ -695,9 +664,9 @@ class Schedule {
$playlists[$pkey]['schedule_id'] = $dx['group_id']; $playlists[$pkey]['schedule_id'] = $dx['group_id'];
$playlists[$pkey]['show_name'] = $dx['show_name']; $playlists[$pkey]['show_name'] = $dx['show_name'];
$playlists[$pkey]['user_id'] = 0; $playlists[$pkey]['user_id'] = 0;
$playlists[$pkey]['id'] = $dx["playlist_id"]; $playlists[$pkey]['id'] = $dx['group_id'];
$playlists[$pkey]['start'] = Schedule::CcTimeToPypoTime($dx["start"]); $playlists[$pkey]['start'] = Schedule::AirtimeTimeToPypoTime($dx["start"]);
$playlists[$pkey]['end'] = Schedule::CcTimeToPypoTime($dx["end"]); $playlists[$pkey]['end'] = Schedule::AirtimeTimeToPypoTime($dx["end"]);
} }
} }
@ -734,30 +703,15 @@ class Schedule {
$result = array(); $result = array();
$result['status'] = array('range' => array('start' => $range_start, 'end' => $range_end), $result['status'] = array('range' => array('start' => $range_start, 'end' => $range_end),
'version' => "1.1"); 'version' => AIRTIME_REST_VERSION);
$result['playlists'] = $playlists; $result['playlists'] = $playlists;
$result['check'] = 1; $result['check'] = 1;
$result['stream_metadata'] = array();
$result['stream_metadata']['format'] = Application_Model_Preference::GetStreamLabelFormat();
$result['stream_metadata']['station_name'] = Application_Model_Preference::GetStationName();
$result['server_timezone'] = date('O');
return $result; return $result;
} }
/**
* Remove all items from the schedule in the given range.
*
* @param string $p_start
* In the format YYYY-MM-DD HH:MM:SS.nnnnnn
* @param string $p_end
* In the format YYYY-MM-DD HH:MM:SS.nnnnnn
*/
public static function RemoveItemsInRange($p_start, $p_end)
{
$items = Schedule::GetItems($p_start, $p_end);
foreach ($items as $item) {
$scheduleGroup = new ScheduleGroup($item["group_id"]);
$scheduleGroup->remove();
}
}
} }

View File

@ -6,50 +6,60 @@ class Show {
public function __construct($showId=NULL) public function __construct($showId=NULL)
{ {
$this->_showId = $showId; $this->_showId = $showId;
} }
public function getName() { public function getName()
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
return $show->getDbName(); return $show->getDbName();
} }
public function setName($name) { public function setName($name)
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
$show->setDbName($name); $show->setDbName($name);
RabbitMq::PushSchedule();
} }
public function getDescription() { public function getDescription()
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
return $show->getDbDescription(); return $show->getDbDescription();
} }
public function setDescription($description) { public function setDescription($description)
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
$show->setDbDescription($description); $show->setDbDescription($description);
} }
public function getColor() { public function getColor()
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
return $show->getDbColor(); return $show->getDbColor();
} }
public function setColor($color) { public function setColor($color)
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
$show->setDbColor($color); $show->setDbColor($color);
} }
public function getBackgroundColor() { public function getBackgroundColor()
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
return $show->getDbBackgroundColor(); return $show->getDbBackgroundColor();
} }
public function setBackgroundColor($backgroundColor) { public function setBackgroundColor($backgroundColor)
{
$show = CcShowQuery::create()->findPK($this->_showId); $show = CcShowQuery::create()->findPK($this->_showId);
$show->setDbBackgroundColor($backgroundColor); $show->setDbBackgroundColor($backgroundColor);
} }
public function cancelShow($day_timestamp) { public function cancelShow($day_timestamp)
{
global $CC_DBC; global $CC_DBC;
$timeinfo = explode(" ", $day_timestamp); $timeinfo = explode(" ", $day_timestamp);
@ -62,20 +72,21 @@ class Show {
WHERE starts >= '{$day_timestamp}' AND show_id = {$this->_showId}"; WHERE starts >= '{$day_timestamp}' AND show_id = {$this->_showId}";
$CC_DBC->query($sql); $CC_DBC->query($sql);
RabbitMq::PushSchedule();
} }
//end dates are non inclusive. //end dates are non inclusive.
public static function addShow($data) { public static function addShow($data)
{
$con = Propel::getConnection(CcShowPeer::DATABASE_NAME); $con = Propel::getConnection(CcShowPeer::DATABASE_NAME);
$sql = "SELECT time '{$data['add_show_start_time']}' + INTERVAL '{$data['add_show_duration']} hour' "; $sql = "SELECT time '{$data['add_show_start_time']}' + INTERVAL '{$data['add_show_duration']} hour' ";
$r = $con->query($sql); $r = $con->query($sql);
$endTime = $r->fetchColumn(0); $endTime = $r->fetchColumn(0);
$sql = "SELECT EXTRACT(DOW FROM TIMESTAMP '{$data['add_show_start_date']} {$data['add_show_start_time']}')"; $sql = "SELECT EXTRACT(DOW FROM TIMESTAMP '{$data['add_show_start_date']} {$data['add_show_start_time']}')";
$r = $con->query($sql); $r = $con->query($sql);
$startDow = $r->fetchColumn(0); $startDow = $r->fetchColumn(0);
if($data['add_show_no_end']) { if($data['add_show_no_end']) {
$endDate = NULL; $endDate = NULL;
@ -84,13 +95,13 @@ class Show {
else if($data['add_show_repeats']) { else if($data['add_show_repeats']) {
$sql = "SELECT date '{$data['add_show_end_date']}' + INTERVAL '1 day' "; $sql = "SELECT date '{$data['add_show_end_date']}' + INTERVAL '1 day' ";
$r = $con->query($sql); $r = $con->query($sql);
$endDate = $r->fetchColumn(0); $endDate = $r->fetchColumn(0);
} }
else { else {
$sql = "SELECT date '{$data['add_show_start_date']}' + INTERVAL '1 day' "; $sql = "SELECT date '{$data['add_show_start_date']}' + INTERVAL '1 day' ";
$r = $con->query($sql); $r = $con->query($sql);
$endDate = $r->fetchColumn(0); $endDate = $r->fetchColumn(0);
} }
//only want the day of the week from the start date. //only want the day of the week from the start date.
if(!$data['add_show_repeats']) { if(!$data['add_show_repeats']) {
@ -98,7 +109,7 @@ class Show {
} }
else if($data['add_show_repeats'] && $data['add_show_day_check'] == "") { else if($data['add_show_repeats'] && $data['add_show_day_check'] == "") {
$data['add_show_day_check'] = array($startDow); $data['add_show_day_check'] = array($startDow);
} }
//find repeat type or set to a non repeating show. //find repeat type or set to a non repeating show.
if($data['add_show_repeats']) { if($data['add_show_repeats']) {
@ -114,7 +125,7 @@ class Show {
$show->setDbUrl($data['add_show_url']); $show->setDbUrl($data['add_show_url']);
$show->setDbColor($data['add_show_color']); $show->setDbColor($data['add_show_color']);
$show->setDbBackgroundColor($data['add_show_background_color']); $show->setDbBackgroundColor($data['add_show_background_color']);
$show->save(); $show->save();
$showId = $show->getDbId(); $showId = $show->getDbId();
@ -127,7 +138,6 @@ class Show {
//don't set day for monthly repeat type, it's invalid. //don't set day for monthly repeat type, it's invalid.
if($data['add_show_repeats'] && $data["add_show_repeat_type"] == 2) { if($data['add_show_repeats'] && $data["add_show_repeat_type"] == 2) {
$showDay = new CcShowDays(); $showDay = new CcShowDays();
$showDay->setDbFirstShow($data['add_show_start_date']); $showDay->setDbFirstShow($data['add_show_start_date']);
$showDay->setDbLastShow($endDate); $showDay->setDbLastShow($endDate);
@ -137,29 +147,25 @@ class Show {
$showDay->setDbShowId($showId); $showDay->setDbShowId($showId);
$showDay->setDbRecord($isRecorded); $showDay->setDbRecord($isRecorded);
$showDay->save(); $showDay->save();
} }
else { else {
foreach ($data['add_show_day_check'] as $day) { foreach ($data['add_show_day_check'] as $day) {
if($startDow !== $day){ if($startDow !== $day){
if($startDow > $day) if ($startDow > $day)
$daysAdd = 6 - $startDow + 1 + $day; $daysAdd = 6 - $startDow + 1 + $day;
else else
$daysAdd = $day - $startDow; $daysAdd = $day - $startDow;
$sql = "SELECT date '{$data['add_show_start_date']}' + INTERVAL '{$daysAdd} day' "; $sql = "SELECT date '{$data['add_show_start_date']}' + INTERVAL '{$daysAdd} day' ";
$r = $con->query($sql); $r = $con->query($sql);
$start = $r->fetchColumn(0); $start = $r->fetchColumn(0);
} }
else { else {
$start = $data['add_show_start_date']; $start = $data['add_show_start_date'];
} }
if(strtotime($start) < strtotime($endDate) || is_null($endDate)) { if(strtotime($start) < strtotime($endDate) || is_null($endDate)) {
$showDay = new CcShowDays(); $showDay = new CcShowDays();
$showDay->setDbFirstShow($start); $showDay->setDbFirstShow($start);
$showDay->setDbLastShow($endDate); $showDay->setDbLastShow($endDate);
@ -180,7 +186,6 @@ class Show {
for($i=1; $i<=5; $i++) { for($i=1; $i<=5; $i++) {
if($data['add_show_rebroadcast_date_'.$i]) { if($data['add_show_rebroadcast_date_'.$i]) {
$showRebroad = new CcShowRebroadcast(); $showRebroad = new CcShowRebroadcast();
$showRebroad->setDbDayOffset($data['add_show_rebroadcast_date_'.$i]); $showRebroad->setDbDayOffset($data['add_show_rebroadcast_date_'.$i]);
$showRebroad->setDbStartTime($data['add_show_rebroadcast_time_'.$i]); $showRebroad->setDbStartTime($data['add_show_rebroadcast_time_'.$i]);
@ -190,14 +195,13 @@ class Show {
} }
} }
else if($data['add_show_record'] && $data['add_show_rebroadcast'] && $repeat_type == -1){ else if($data['add_show_record'] && $data['add_show_rebroadcast'] && $repeat_type == -1){
for($i=1; $i<=5; $i++) { for($i=1; $i<=5; $i++) {
if($data['add_show_rebroadcast_absolute_date_'.$i]) { if($data['add_show_rebroadcast_absolute_date_'.$i]) {
$sql = "SELECT date '{$data['add_show_rebroadcast_absolute_date_'.$i]}' - date '{$data['add_show_start_date']}' "; $sql = "SELECT date '{$data['add_show_rebroadcast_absolute_date_'.$i]}' - date '{$data['add_show_start_date']}' ";
$r = $con->query($sql); $r = $con->query($sql);
$offset_days = $r->fetchColumn(0); $offset_days = $r->fetchColumn(0);
$showRebroad = new CcShowRebroadcast(); $showRebroad = new CcShowRebroadcast();
$showRebroad->setDbDayOffset($offset_days." days"); $showRebroad->setDbDayOffset($offset_days." days");
@ -207,7 +211,7 @@ class Show {
} }
} }
} }
if(is_array($data['add_show_hosts'])) { if(is_array($data['add_show_hosts'])) {
//add selected hosts to cc_show_hosts table. //add selected hosts to cc_show_hosts table.
foreach ($data['add_show_hosts'] as $host) { foreach ($data['add_show_hosts'] as $host) {
@ -219,18 +223,20 @@ class Show {
} }
Show::populateShowUntilLastGeneratedDate($showId); Show::populateShowUntilLastGeneratedDate($showId);
RabbitMq::PushSchedule();
} }
public static function getShows($start_timestamp, $end_timestamp, $excludeInstance=NULL, $onlyRecord=FALSE) { public static function getShows($start_timestamp, $end_timestamp, $excludeInstance=NULL, $onlyRecord=FALSE)
{
global $CC_DBC; global $CC_DBC;
$sql = "SELECT starts, ends, record, rebroadcast, instance_id, show_id, name, description, $sql = "SELECT starts, ends, record, rebroadcast, instance_id, show_id, name, description,
color, background_color, cc_show_instances.id AS instance_id color, background_color, cc_show_instances.id AS instance_id
FROM cc_show_instances FROM cc_show_instances
LEFT JOIN cc_show ON cc_show.id = cc_show_instances.show_id"; LEFT JOIN cc_show ON cc_show.id = cc_show_instances.show_id";
//only want shows that are starting at the time or later. //only want shows that are starting at the time or later.
if($onlyRecord) { if ($onlyRecord) {
$sql = $sql." WHERE (starts >= '{$start_timestamp}' AND starts < timestamp '{$start_timestamp}' + interval '2 hours')"; $sql = $sql." WHERE (starts >= '{$start_timestamp}' AND starts < timestamp '{$start_timestamp}' + interval '2 hours')";
$sql = $sql." AND (record = 1)"; $sql = $sql." AND (record = 1)";
@ -240,10 +246,10 @@ class Show {
$sql = $sql." WHERE ((starts >= '{$start_timestamp}' AND starts < '{$end_timestamp}') $sql = $sql." WHERE ((starts >= '{$start_timestamp}' AND starts < '{$end_timestamp}')
OR (ends > '{$start_timestamp}' AND ends <= '{$end_timestamp}') OR (ends > '{$start_timestamp}' AND ends <= '{$end_timestamp}')
OR (starts <= '{$start_timestamp}' AND ends >= '{$end_timestamp}'))"; OR (starts <= '{$start_timestamp}' AND ends >= '{$end_timestamp}'))";
} }
if(isset($excludeInstance)) {
if (isset($excludeInstance)) {
foreach($excludeInstance as $instance) { foreach($excludeInstance as $instance) {
$sql_exclude[] = "cc_show_instances.id != {$instance}"; $sql_exclude[] = "cc_show_instances.id != {$instance}";
} }
@ -257,8 +263,8 @@ class Show {
return $CC_DBC->GetAll($sql); return $CC_DBC->GetAll($sql);
} }
private static function setNextPop($next_date, $show_id, $day) { private static function setNextPop($next_date, $show_id, $day)
{
$nextInfo = explode(" ", $next_date); $nextInfo = explode(" ", $next_date);
$repeatInfo = CcShowDaysQuery::create() $repeatInfo = CcShowDaysQuery::create()
@ -271,15 +277,16 @@ class Show {
} }
//for a show with repeat_type == -1 //for a show with repeat_type == -1
private static function populateNonRepeatingShow($show_id, $first_show, $start_time, $duration, $day, $record, $end_timestamp) { private static function populateNonRepeatingShow($show_id, $first_show, $start_time, $duration, $day, $record, $end_timestamp)
{
global $CC_DBC; global $CC_DBC;
$next_date = $first_show." ".$start_time; $next_date = $first_show." ".$start_time;
if(strtotime($next_date) < strtotime($end_timestamp)) { if(strtotime($next_date) < strtotime($end_timestamp)) {
$start = $next_date; $start = $next_date;
$sql = "SELECT timestamp '{$start}' + interval '{$duration}'"; $sql = "SELECT timestamp '{$start}' + interval '{$duration}'";
$end = $CC_DBC->GetOne($sql); $end = $CC_DBC->GetOne($sql);
@ -298,10 +305,10 @@ class Show {
foreach($rebroadcasts as $rebroadcast) { foreach($rebroadcasts as $rebroadcast) {
$timeinfo = explode(" ", $start); $timeinfo = explode(" ", $start);
$sql = "SELECT timestamp '{$timeinfo[0]}' + interval '{$rebroadcast["day_offset"]}' + interval '{$rebroadcast["start_time"]}'"; $sql = "SELECT timestamp '{$timeinfo[0]}' + interval '{$rebroadcast["day_offset"]}' + interval '{$rebroadcast["start_time"]}'";
$rebroadcast_start_time = $CC_DBC->GetOne($sql); $rebroadcast_start_time = $CC_DBC->GetOne($sql);
$sql = "SELECT timestamp '{$rebroadcast_start_time}' + interval '{$duration}'"; $sql = "SELECT timestamp '{$rebroadcast_start_time}' + interval '{$duration}'";
$rebroadcast_end_time = $CC_DBC->GetOne($sql); $rebroadcast_end_time = $CC_DBC->GetOne($sql);
@ -315,12 +322,13 @@ class Show {
$newRebroadcastInstance->save(); $newRebroadcastInstance->save();
} }
} }
RabbitMq::PushSchedule();
} }
//for a show with repeat_type == 0,1,2 //for a show with repeat_type == 0,1,2
private static function populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show, private static function populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show,
$start_time, $duration, $day, $record, $end_timestamp, $interval) { $start_time, $duration, $day, $record, $end_timestamp, $interval) {
global $CC_DBC; global $CC_DBC;
if(isset($next_pop_date)) { if(isset($next_pop_date)) {
$next_date = $next_pop_date." ".$start_time; $next_date = $next_pop_date." ".$start_time;
@ -333,9 +341,9 @@ class Show {
$rebroadcasts = $CC_DBC->GetAll($sql); $rebroadcasts = $CC_DBC->GetAll($sql);
while(strtotime($next_date) < strtotime($end_timestamp) && (strtotime($last_show) > strtotime($next_date) || is_null($last_show))) { while(strtotime($next_date) < strtotime($end_timestamp) && (strtotime($last_show) > strtotime($next_date) || is_null($last_show))) {
$start = $next_date; $start = $next_date;
$sql = "SELECT timestamp '{$start}' + interval '{$duration}'"; $sql = "SELECT timestamp '{$start}' + interval '{$duration}'";
$end = $CC_DBC->GetOne($sql); $end = $CC_DBC->GetOne($sql);
@ -351,10 +359,10 @@ class Show {
foreach($rebroadcasts as $rebroadcast) { foreach($rebroadcasts as $rebroadcast) {
$timeinfo = explode(" ", $next_date); $timeinfo = explode(" ", $next_date);
$sql = "SELECT timestamp '{$timeinfo[0]}' + interval '{$rebroadcast["day_offset"]}' + interval '{$rebroadcast["start_time"]}'"; $sql = "SELECT timestamp '{$timeinfo[0]}' + interval '{$rebroadcast["day_offset"]}' + interval '{$rebroadcast["start_time"]}'";
$rebroadcast_start_time = $CC_DBC->GetOne($sql); $rebroadcast_start_time = $CC_DBC->GetOne($sql);
$sql = "SELECT timestamp '{$rebroadcast_start_time}' + interval '{$duration}'"; $sql = "SELECT timestamp '{$rebroadcast_start_time}' + interval '{$duration}'";
$rebroadcast_end_time = $CC_DBC->GetOne($sql); $rebroadcast_end_time = $CC_DBC->GetOne($sql);
@ -373,64 +381,65 @@ class Show {
} }
Show::setNextPop($next_date, $show_id, $day); Show::setNextPop($next_date, $show_id, $day);
RabbitMq::PushSchedule();
} }
private static function populateShow($repeat_type, $show_id, $next_pop_date, private static function populateShow($repeat_type, $show_id, $next_pop_date,
$first_show, $last_show, $start_time, $duration, $day, $record, $end_timestamp) { $first_show, $last_show, $start_time, $duration, $day, $record, $end_timestamp) {
if($repeat_type == -1) { if($repeat_type == -1) {
Show::populateNonRepeatingShow($show_id, $first_show, $start_time, $duration, $day, $record, $end_timestamp); Show::populateNonRepeatingShow($show_id, $first_show, $start_time, $duration, $day, $record, $end_timestamp);
} }
else if($repeat_type == 0) { else if($repeat_type == 0) {
Show::populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show, Show::populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show,
$start_time, $duration, $day, $record, $end_timestamp, '7 days'); $start_time, $duration, $day, $record, $end_timestamp, '7 days');
} }
else if($repeat_type == 1) { else if($repeat_type == 1) {
Show::populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show, Show::populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show,
$start_time, $duration, $day, $record, $end_timestamp, '14 days'); $start_time, $duration, $day, $record, $end_timestamp, '14 days');
} }
else if($repeat_type == 2) { else if($repeat_type == 2) {
Show::populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show, Show::populateRepeatingShow($show_id, $next_pop_date, $first_show, $last_show,
$start_time, $duration, $day, $record, $end_timestamp, '1 month'); $start_time, $duration, $day, $record, $end_timestamp, '1 month');
} }
} }
//used to catch up a newly added show //used to catch up a newly added show
private static function populateShowUntilLastGeneratedDate($show_id) { private static function populateShowUntilLastGeneratedDate($show_id) {
global $CC_DBC; global $CC_DBC;
$showsPopUntil = Application_Model_Preference::GetShowsPopulatedUntil(); $showsPopUntil = Application_Model_Preference::GetShowsPopulatedUntil();
$sql = "SELECT * FROM cc_show_days WHERE show_id = {$show_id}"; $sql = "SELECT * FROM cc_show_days WHERE show_id = {$show_id}";
$res = $CC_DBC->GetAll($sql); $res = $CC_DBC->GetAll($sql);
foreach($res as $row) { foreach($res as $row) {
Show::populateShow($row["repeat_type"], $row["show_id"], $row["next_pop_date"], $row["first_show"], Show::populateShow($row["repeat_type"], $row["show_id"], $row["next_pop_date"], $row["first_show"],
$row["last_show"], $row["start_time"], $row["duration"], $row["day"], $row["record"], $showsPopUntil); $row["last_show"], $row["start_time"], $row["duration"], $row["day"], $row["record"], $showsPopUntil);
} }
} }
public static function populateShowsUntil($pop_timestamp, $end_timestamp) { public static function populateShowsUntil($pop_timestamp, $end_timestamp) {
global $CC_DBC; global $CC_DBC;
if($pop_timestamp != "") { if($pop_timestamp != "") {
$sql = "SELECT * FROM cc_show_days $sql = "SELECT * FROM cc_show_days
WHERE last_show IS NULL WHERE last_show IS NULL
OR first_show < '{$end_timestamp}' AND last_show > '{$pop_timestamp}'"; OR first_show < '{$end_timestamp}' AND last_show > '{$pop_timestamp}'";
} }
else { else {
$today_timestamp = date("Y-m-d"); $today_timestamp = date("Y-m-d");
$sql = "SELECT * FROM cc_show_days $sql = "SELECT * FROM cc_show_days
WHERE last_show IS NULL WHERE last_show IS NULL
OR first_show < '{$end_timestamp}' AND last_show > '{$today_timestamp}'"; OR first_show < '{$end_timestamp}' AND last_show > '{$today_timestamp}'";
} }
$res = $CC_DBC->GetAll($sql); $res = $CC_DBC->GetAll($sql);
foreach($res as $row) { foreach($res as $row) {
Show::populateShow($row["repeat_type"], $row["show_id"], $row["next_pop_date"], $row["first_show"], Show::populateShow($row["repeat_type"], $row["show_id"], $row["next_pop_date"], $row["first_show"],
$row["last_show"], $row["start_time"], $row["duration"], $row["day"], $row["record"], $end_timestamp); $row["last_show"], $row["start_time"], $row["duration"], $row["day"], $row["record"], $end_timestamp);
} }
} }
public static function getFullCalendarEvents($start, $end, $editable=false) { public static function getFullCalendarEvents($start, $end, $editable=false) {
@ -460,19 +469,15 @@ class Show {
private static function makeFullCalendarEvent($show, $options=array()) { private static function makeFullCalendarEvent($show, $options=array()) {
global $CC_DBC; global $CC_DBC;
$event = array(); $event = array();
if($show["rebroadcast"]) { if($show["rebroadcast"]) {
$title = "REBROADCAST ".$show["name"];
$event["disableResizing"] = true; $event["disableResizing"] = true;
} }
else {
$title = $show["name"];
}
$event["id"] = $show["instance_id"]; $event["id"] = $show["instance_id"];
$event["title"] = $title; $event["title"] = $show["name"];
$event["start"] = $show["starts"]; $event["start"] = $show["starts"];
$event["end"] = $show["ends"]; $event["end"] = $show["ends"];
$event["allDay"] = false; $event["allDay"] = false;
@ -500,56 +505,68 @@ class ShowInstance {
public function __construct($instanceId) public function __construct($instanceId)
{ {
$this->_instanceId = $instanceId; $this->_instanceId = $instanceId;
} }
public function getShowId() { public function getShowId()
{
$showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId);
return $showInstance->getDbShowId(); return $showInstance->getDbShowId();
} }
public function getShowInstanceId() { public function getShowInstanceId()
{
return $this->_instanceId; return $this->_instanceId;
} }
public function isRebroadcast() { public function isRebroadcast()
{
$showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId);
return $showInstance->getDbOriginalShow(); return $showInstance->getDbOriginalShow();
} }
public function isRecorded() { public function isRecorded()
{
$showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId);
return $showInstance->getDbRecord(); return $showInstance->getDbRecord();
} }
public function getName() { public function getName()
{
$show = CcShowQuery::create()->findPK($this->getShowId()); $show = CcShowQuery::create()->findPK($this->getShowId());
return $show->getDbName(); return $show->getDbName();
} }
public function getShowStart() { public function getShowStart()
{
$showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId);
return $showInstance->getDbStarts(); return $showInstance->getDbStarts();
} }
public function getShowEnd() { public function getShowEnd()
{
$showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId);
return $showInstance->getDbEnds(); return $showInstance->getDbEnds();
} }
public function setShowStart($start) { public function setShowStart($start)
{
$showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId);
$showInstance->setDbStarts($start) $showInstance->setDbStarts($start)
->save(); ->save();
RabbitMq::PushSchedule();
} }
public function setShowEnd($end) { public function setShowEnd($end)
{
$showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId);
$showInstance->setDbEnds($end) $showInstance->setDbEnds($end)
->save(); ->save();
RabbitMq::PushSchedule();
} }
public function moveScheduledShowContent($deltaDay, $deltaHours, $deltaMin) { public function moveScheduledShowContent($deltaDay, $deltaHours, $deltaMin)
{
global $CC_DBC; global $CC_DBC;
$sql = "UPDATE cc_schedule $sql = "UPDATE cc_schedule
@ -558,9 +575,11 @@ class ShowInstance {
WHERE instance_id = '{$this->_instanceId}'"; WHERE instance_id = '{$this->_instanceId}'";
$CC_DBC->query($sql); $CC_DBC->query($sql);
RabbitMq::PushSchedule();
} }
public function moveShow($deltaDay, $deltaMin){ public function moveShow($deltaDay, $deltaMin)
{
global $CC_DBC; global $CC_DBC;
$hours = $deltaMin/60; $hours = $deltaMin/60;
@ -572,7 +591,7 @@ class ShowInstance {
$mins = abs($deltaMin%60); $mins = abs($deltaMin%60);
$starts = $this->getShowStart(); $starts = $this->getShowStart();
$ends = $this->getShowEnd(); $ends = $this->getShowEnd();
$sql = "SELECT timestamp '{$starts}' + interval '{$deltaDay} days' + interval '{$hours}:{$mins}'"; $sql = "SELECT timestamp '{$starts}' + interval '{$deltaDay} days' + interval '{$hours}:{$mins}'";
$new_starts = $CC_DBC->GetOne($sql); $new_starts = $CC_DBC->GetOne($sql);
@ -595,18 +614,20 @@ class ShowInstance {
if($rebroadcast) { if($rebroadcast) {
$sql = "SELECT timestamp '{$new_starts}' < (SELECT starts FROM cc_show_instances WHERE id = {$rebroadcast})"; $sql = "SELECT timestamp '{$new_starts}' < (SELECT starts FROM cc_show_instances WHERE id = {$rebroadcast})";
$isBeforeRecordedOriginal = $CC_DBC->GetOne($sql); $isBeforeRecordedOriginal = $CC_DBC->GetOne($sql);
if($isBeforeRecordedOriginal === 't'){ if($isBeforeRecordedOriginal === 't'){
return "Cannot move a rebroadcast show before its original"; return "Cannot move a rebroadcast show before its original";
} }
} }
$this->moveScheduledShowContent($deltaDay, $hours, $mins); $this->moveScheduledShowContent($deltaDay, $hours, $mins);
$this->setShowStart($new_starts); $this->setShowStart($new_starts);
$this->setShowEnd($new_ends); $this->setShowEnd($new_ends);
RabbitMq::PushSchedule();
} }
public function resizeShow($deltaDay, $deltaMin){ public function resizeShow($deltaDay, $deltaMin)
{
global $CC_DBC; global $CC_DBC;
$hours = $deltaMin/60; $hours = $deltaMin/60;
@ -618,7 +639,7 @@ class ShowInstance {
$mins = abs($deltaMin%60); $mins = abs($deltaMin%60);
$starts = $this->getShowStart(); $starts = $this->getShowStart();
$ends = $this->getShowEnd(); $ends = $this->getShowEnd();
$sql = "SELECT timestamp '{$ends}' + interval '{$deltaDay} days' + interval '{$hours}:{$mins}'"; $sql = "SELECT timestamp '{$ends}' + interval '{$deltaDay} days' + interval '{$hours}:{$mins}'";
$new_ends = $CC_DBC->GetOne($sql); $new_ends = $CC_DBC->GetOne($sql);
@ -639,106 +660,142 @@ class ShowInstance {
WHERE rebroadcast = 1 AND instance_id = {$this->_instanceId}"; WHERE rebroadcast = 1 AND instance_id = {$this->_instanceId}";
$CC_DBC->query($sql); $CC_DBC->query($sql);
} }
$this->setShowEnd($new_ends); $this->setShowEnd($new_ends);
RabbitMq::PushSchedule();
} }
private function getLastGroupId() { private function getLastGroupId()
{
global $CC_DBC; global $CC_DBC;
$sql = "SELECT group_id FROM cc_schedule WHERE instance_id = '{$this->_instanceId}' ORDER BY ends DESC LIMIT 1"; $sql = "SELECT group_id FROM cc_schedule WHERE instance_id = '{$this->_instanceId}' ORDER BY ends DESC LIMIT 1";
$res = $CC_DBC->GetOne($sql); $res = $CC_DBC->GetOne($sql);
return $res; return $res;
} }
public function addPlaylistToShow($plId) { public function addPlaylistToShow($plId)
{
$sched = new ScheduleGroup(); $sched = new ScheduleGroup();
$lastGroupId = $this->getLastGroupId(); $lastGroupId = $this->getLastGroupId();
if(is_null($lastGroupId)) { if(is_null($lastGroupId)) {
$groupId = $sched->add($this->_instanceId, $this->getShowStart(), null, $plId); $groupId = $sched->add($this->_instanceId, $this->getShowStart(), null, $plId);
} }
else { else {
$groupId = $sched->addPlaylistAfter($this->_instanceId, $lastGroupId, $plId); $groupId = $sched->addPlaylistAfter($this->_instanceId, $lastGroupId, $plId);
} }
RabbitMq::PushSchedule();
} }
public function addFileToShow($file_id)
{
$sched = new ScheduleGroup();
$lastGroupId = $this->getLastGroupId();
if(is_null($lastGroupId)) {
$groupId = $sched->add($this->_instanceId, $this->getShowStart(), $file_id);
}
else {
$groupId = $sched->addFileAfter($this->_instanceId, $lastGroupId, $file_id);
}
RabbitMq::PushSchedule();
}
public function scheduleShow($plIds) { public function scheduleShow($plIds) {
foreach($plIds as $plId) { foreach($plIds as $plId) {
$this->addPlaylistToShow($plId); $this->addPlaylistToShow($plId);
} }
} }
public function removeGroupFromShow($group_id){ public function removeGroupFromShow($group_id)
{
global $CC_DBC; global $CC_DBC;
$sql = "SELECT MAX(ends) as end_timestamp, (MAX(ends) - MIN(starts)) as length $sql = "SELECT MAX(ends) as end_timestamp, (MAX(ends) - MIN(starts)) as length
FROM cc_schedule FROM cc_schedule
WHERE group_id = '{$group_id}'"; WHERE group_id = '{$group_id}'";
$groupBoundry = $CC_DBC->GetRow($sql); $groupBoundry = $CC_DBC->GetRow($sql);
$group = CcScheduleQuery::create() $group = CcScheduleQuery::create()
->filterByDbGroupId($group_id) ->filterByDbGroupId($group_id)
->delete(); ->delete();
$sql = "UPDATE cc_schedule $sql = "UPDATE cc_schedule
SET starts = (starts - INTERVAL '{$groupBoundry["length"]}'), ends = (ends - INTERVAL '{$groupBoundry["length"]}') SET starts = (starts - INTERVAL '{$groupBoundry["length"]}'), ends = (ends - INTERVAL '{$groupBoundry["length"]}')
WHERE starts >= '{$groupBoundry["end_timestamp"]}' AND instance_id = {$this->_instanceId}"; WHERE starts >= '{$groupBoundry["end_timestamp"]}' AND instance_id = {$this->_instanceId}";
$CC_DBC->query($sql); $CC_DBC->query($sql);
RabbitMq::PushSchedule();
} }
public function clearShow() { public function clearShow()
{
CcScheduleQuery::create() CcScheduleQuery::create()
->filterByDbInstanceId($this->_instanceId) ->filterByDbInstanceId($this->_instanceId)
->delete(); ->delete();
RabbitMq::PushSchedule();
} }
public function deleteShow() { public function deleteShow()
{
CcShowInstancesQuery::create() CcShowInstancesQuery::create()
->findPK($this->_instanceId) ->findPK($this->_instanceId)
->delete(); ->delete();
RabbitMq::PushSchedule();
} }
public function getTimeScheduled() { public function setRecordedFile($file_id)
{
$showInstance = CcShowInstancesQuery::create()
->findPK($this->_instanceId);
$showInstance->setDbRecordedFile($file_id)
->save();
$rebroadcasts = CcShowInstancesQuery::create()
->filterByDbOriginalShow($this->_instanceId)
->find();
foreach ($rebroadcasts as $rebroadcast) {
$rebroad = new ShowInstance($rebroadcast->getDbId());
$rebroad->addFileToShow($file_id);
RabbitMq::PushSchedule();
}
}
public function getTimeScheduled()
{
$instance_id = $this->getShowInstanceId(); $instance_id = $this->getShowInstanceId();
$time = Schedule::GetTotalShowTime($instance_id); $time = Schedule::GetTotalShowTime($instance_id);
return $time; return $time;
} }
public function getTimeUnScheduled() { public function getTimeUnScheduled()
{
$start_timestamp = $this->getShowStart(); $start_timestamp = $this->getShowStart();
$end_timestamp = $this->getShowEnd(); $end_timestamp = $this->getShowEnd();
$instance_id = $this->getShowInstanceId(); $instance_id = $this->getShowInstanceId();
$time = Schedule::getTimeUnScheduledInRange($instance_id, $start_timestamp, $end_timestamp); $time = Schedule::getTimeUnScheduledInRange($instance_id, $start_timestamp, $end_timestamp);
return $time; return $time;
} }
public function getPercentScheduled() { public function getPercentScheduled()
{
$start_timestamp = $this->getShowStart(); $start_timestamp = $this->getShowStart();
$end_timestamp = $this->getShowEnd(); $end_timestamp = $this->getShowEnd();
$instance_id = $this->getShowInstanceId(); $instance_id = $this->getShowInstanceId();
return Schedule::GetPercentScheduled($instance_id, $start_timestamp, $end_timestamp); return Schedule::GetPercentScheduled($instance_id, $start_timestamp, $end_timestamp);
} }
public function getShowLength() { public function getShowLength()
{
global $CC_DBC; global $CC_DBC;
$start_timestamp = $this->getShowStart(); $start_timestamp = $this->getShowStart();
$end_timestamp = $this->getShowEnd(); $end_timestamp = $this->getShowEnd();
$sql = "SELECT TIMESTAMP '{$end_timestamp}' - TIMESTAMP '{$start_timestamp}' "; $sql = "SELECT TIMESTAMP '{$end_timestamp}' - TIMESTAMP '{$start_timestamp}' ";
@ -747,26 +804,27 @@ class ShowInstance {
return $length; return $length;
} }
public function searchPlaylistsForShow($datatables){ public function searchPlaylistsForShow($datatables)
{
$time_remaining = $this->getTimeUnScheduled(); $time_remaining = $this->getTimeUnScheduled();
return StoredFile::searchPlaylistsForSchedule($time_remaining, $datatables); return StoredFile::searchPlaylistsForSchedule($time_remaining, $datatables);
} }
public function getShowListContent() { public function getShowListContent()
{
global $CC_DBC; global $CC_DBC;
$sql = "SELECT * $sql = "SELECT *
FROM (cc_schedule AS s LEFT JOIN cc_files AS f ON f.id = s.file_id FROM (cc_schedule AS s LEFT JOIN cc_files AS f ON f.id = s.file_id
LEFT JOIN cc_playlist AS p ON p.id = s.playlist_id ) LEFT JOIN cc_playlist AS p ON p.id = s.playlist_id )
WHERE s.instance_id = '{$this->_instanceId}' ORDER BY starts"; WHERE s.instance_id = '{$this->_instanceId}' ORDER BY starts";
return $CC_DBC->GetAll($sql); return $CC_DBC->GetAll($sql);
} }
public function getShowContent() { public function getShowContent()
{
global $CC_DBC; global $CC_DBC;
$res = $this->getShowListContent(); $res = $this->getShowListContent();
@ -788,7 +846,7 @@ class ShowInstance {
$items[$pl_counter]["pl_name"] = $row["name"]; $items[$pl_counter]["pl_name"] = $row["name"];
$items[$pl_counter]["pl_creator"] = $row["creator"]; $items[$pl_counter]["pl_creator"] = $row["creator"];
$items[$pl_counter]["pl_description"] = $row["description"]; $items[$pl_counter]["pl_description"] = $row["description"];
$items[$pl_counter]["pl_group"] = $row["group_id"]; $items[$pl_counter]["pl_group"] = $row["group_id"];
$sql = "SELECT SUM(clip_length) FROM cc_schedule WHERE group_id = '{$currGroupId}'"; $sql = "SELECT SUM(clip_length) FROM cc_schedule WHERE group_id = '{$currGroupId}'";
$length = $CC_DBC->GetOne($sql); $length = $CC_DBC->GetOne($sql);
@ -802,46 +860,49 @@ class ShowInstance {
$items[$pl_counter]["pl_content"][$f_counter]["f_length"] = $row["length"]; $items[$pl_counter]["pl_content"][$f_counter]["f_length"] = $row["length"];
} }
return $items; return $items;
} }
} }
/* Show Data Access Layer */ /* Show Data Access Layer */
class Show_DAL{ class Show_DAL {
public static function GetCurrentShow($timeNow) { public static function GetCurrentShow($timeNow)
{
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$timestamp = explode(" ", $timeNow); $timestamp = explode(" ", $timeNow);
$date = $timestamp[0]; $date = $timestamp[0];
$time = $timestamp[1]; $time = $timestamp[1];
$sql = "SELECT si.starts as start_timestamp, si.ends as end_timestamp, s.name, s.id, si.id as instance_id, si.record" $sql = "SELECT si.starts as start_timestamp, si.ends as end_timestamp, s.name, s.id, si.id as instance_id, si.record"
." FROM $CC_CONFIG[showInstances] si, $CC_CONFIG[showTable] s" ." FROM $CC_CONFIG[showInstances] si, $CC_CONFIG[showTable] s"
." WHERE si.show_id = s.id" ." WHERE si.show_id = s.id"
." AND si.starts <= TIMESTAMP '$timeNow'" ." AND si.starts <= TIMESTAMP '$timeNow'"
." AND si.ends > TIMESTAMP '$timeNow'"; ." AND si.ends > TIMESTAMP '$timeNow'";
$rows = $CC_DBC->GetAll($sql); $rows = $CC_DBC->GetAll($sql);
return $rows; return $rows;
} }
public static function GetNextShow($timeNow) { public static function GetNextShows($timeNow, $limit)
{
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$sql = "SELECT *, si.starts as start_timestamp, si.ends as end_timestamp FROM " $sql = "SELECT *, si.starts as start_timestamp, si.ends as end_timestamp FROM "
." $CC_CONFIG[showInstances] si, $CC_CONFIG[showTable] s" ." $CC_CONFIG[showInstances] si, $CC_CONFIG[showTable] s"
." WHERE si.show_id = s.id" ." WHERE si.show_id = s.id"
." AND si.starts >= TIMESTAMP '$timeNow'" ." AND si.starts >= TIMESTAMP '$timeNow'"
." AND si.starts < TIMESTAMP '$timeNow' + INTERVAL '48 hours'" ." AND si.starts < TIMESTAMP '$timeNow' + INTERVAL '48 hours'"
." ORDER BY si.starts" ." ORDER BY si.starts"
." LIMIT 1"; ." LIMIT $limit";
$rows = $CC_DBC->GetAll($sql); $rows = $CC_DBC->GetAll($sql);
return $rows; return $rows;
} }
public static function GetShowsInRange($timeNow, $start, $end){ public static function GetShowsInRange($timeNow, $start, $end)
{
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
$sql = "SELECT" $sql = "SELECT"
." si.starts as show_starts," ." si.starts as show_starts,"
@ -873,5 +934,5 @@ class Show_DAL{
return $CC_DBC->GetAll($sql); return $CC_DBC->GetAll($sql);
} }
} }

View File

@ -0,0 +1,69 @@
<?php
require_once 'soundcloud-api/Services/Soundcloud.php';
class ATSoundcloud {
private $_soundcloud;
public function __construct()
{
global $CC_CONFIG;
$this->_soundcloud = new Services_Soundcloud($CC_CONFIG['soundcloud-client-id'], $CC_CONFIG['soundcloud-client-secret']);
}
private function getToken()
{
$username = Application_Model_Preference::GetSoundCloudUser();
$password = Application_Model_Preference::GetSoundCloudPassword();
if($username === "" || $password === "")
{
return false;
}
$token = $this->_soundcloud->accessTokenResourceOwner($username, $password);
return $token;
}
public function uploadTrack($filepath, $filename, $description, $tags=array())
{
if($this->getToken())
{
if(count($tags)) {
$tags = join(" ", $tags);
$tags = $tags." ".Application_Model_Preference::GetSoundCloudTags();
}
else {
$tags = Application_Model_Preference::GetSoundCloudTags();
}
$track_data = array(
'track[sharing]' => 'private',
'track[title]' => $filename,
'track[asset_data]' => '@' . $filepath,
'track[tag_list]' => $tags,
'track[description]' => $description
);
try {
$response = json_decode(
$this->_soundcloud->post('tracks', $track_data),
true
);
echo var_dump($response);
}
catch (Services_Soundcloud_Invalid_Http_Response_Code_Exception $e) {
echo $e->getMessage();
}
}
else
{
echo "could not get soundcloud token";
}
}
}

View File

@ -1647,5 +1647,143 @@ class StoredFile {
return array("sEcho" => intval($data["sEcho"]), "iTotalDisplayRecords" => $totalDisplayRows, "iTotalRecords" => $totalRows, "aaData" => $results); return array("sEcho" => intval($data["sEcho"]), "iTotalDisplayRecords" => $totalDisplayRows, "iTotalRecords" => $totalRows, "aaData" => $results);
} }
public static function uploadFile($targetDir) {
// HTTP headers for no cache etc
header('Content-type: text/plain; charset=UTF-8');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
// Settings
//$targetDir = ini_get("upload_tmp_dir"); //. DIRECTORY_SEPARATOR . "plupload";
$cleanupTargetDir = false; // Remove old files
$maxFileAge = 60 * 60; // Temp file age in seconds
// 5 minutes execution time
@set_time_limit(5 * 60);
// usleep(5000);
// Get parameters
$chunk = isset($_REQUEST["chunk"]) ? $_REQUEST["chunk"] : 0;
$chunks = isset($_REQUEST["chunks"]) ? $_REQUEST["chunks"] : 0;
$fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';
// Clean the fileName for security reasons
//$fileName = preg_replace('/[^\w\._]+/', '', $fileName);
// Create target dir
if (!file_exists($targetDir))
@mkdir($targetDir);
// Remove old temp files
if (is_dir($targetDir) && ($dir = opendir($targetDir))) {
while (($file = readdir($dir)) !== false) {
$filePath = $targetDir . DIRECTORY_SEPARATOR . $file;
// Remove temp files if they are older than the max age
if (preg_match('/\.tmp$/', $file) && (filemtime($filePath) < time() - $maxFileAge))
@unlink($filePath);
}
closedir($dir);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
// Look for the content type header
if (isset($_SERVER["HTTP_CONTENT_TYPE"]))
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
if (isset($_SERVER["CONTENT_TYPE"]))
$contentType = $_SERVER["CONTENT_TYPE"];
if (strpos($contentType, "multipart") !== false) {
if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name'])) {
// Open temp file
$out = fopen($targetDir . DIRECTORY_SEPARATOR . $fileName, $chunk == 0 ? "wb" : "ab");
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen($_FILES['file']['tmp_name'], "rb");
if ($in) {
while ($buff = fread($in, 4096))
fwrite($out, $buff);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
fclose($out);
unlink($_FILES['file']['tmp_name']);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
} else {
// Open temp file
$out = fopen($targetDir . DIRECTORY_SEPARATOR . $fileName, $chunk == 0 ? "wb" : "ab");
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen("php://input", "rb");
if ($in) {
while ($buff = fread($in, 4096))
fwrite($out, $buff);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
fclose($out);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}
$audio_file = $targetDir . DIRECTORY_SEPARATOR . $fileName;
$md5 = md5_file($audio_file);
$duplicate = StoredFile::RecallByMd5($md5);
if ($duplicate) {
if (PEAR::isError($duplicate)) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' . $duplicate->getMessage() .'}}');
}
else {
$duplicateName = $duplicate->getMetadataValue(UI_MDATA_KEY_TITLE);
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "An identical audioclip named ' . $duplicateName . ' already exists in the storage server."}}');
}
}
$metadata = Metadata::LoadFromFile($audio_file);
if (PEAR::isError($metadata)) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' + $metadata->getMessage() + '}}');
}
// #2196 no id tag -> use the original filename
if (basename($audio_file) == $metadata[UI_MDATA_KEY_TITLE]) {
$metadata[UI_MDATA_KEY_TITLE] = basename($audio_file);
$metadata[UI_MDATA_KEY_FILENAME] = basename($audio_file);
}
// setMetadataBatch doesnt like these values
unset($metadata['audio']);
unset($metadata['playtime_seconds']);
$values = array(
"filename" => basename($audio_file),
"filepath" => $audio_file,
"filetype" => "audioclip",
"mime" => $metadata[UI_MDATA_KEY_FORMAT],
"md5" => $md5
);
$storedFile = StoredFile::Insert($values);
if (PEAR::isError($storedFile)) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' + $storedFile->getMessage() + '}}');
}
$storedFile->setMetadataBatch($metadata);
return $storedFile;
}
} }

View File

@ -39,7 +39,7 @@ class CcScheduleTableMap extends TableMap {
$this->setPrimaryKeyMethodInfo('cc_schedule_id_seq'); $this->setPrimaryKeyMethodInfo('cc_schedule_id_seq');
// columns // columns
$this->addPrimaryKey('ID', 'DbId', 'INTEGER', true, null, null); $this->addPrimaryKey('ID', 'DbId', 'INTEGER', true, null, null);
$this->addColumn('PLAYLIST_ID', 'DbPlaylistId', 'INTEGER', true, null, null); $this->addColumn('PLAYLIST_ID', 'DbPlaylistId', 'INTEGER', false, null, null);
$this->addColumn('STARTS', 'DbStarts', 'TIMESTAMP', true, null, null); $this->addColumn('STARTS', 'DbStarts', 'TIMESTAMP', true, null, null);
$this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', true, null, null); $this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', true, null, null);
$this->addColumn('GROUP_ID', 'DbGroupId', 'INTEGER', false, null, null); $this->addColumn('GROUP_ID', 'DbGroupId', 'INTEGER', false, null, null);

View File

@ -110,6 +110,7 @@ class SchedulerTests extends PHPUnit_TestCase {
} }
} }
/*
function testGetItems() { function testGetItems() {
$i1 = new ScheduleGroup(); $i1 = new ScheduleGroup();
$groupId1 = $i1->add('2008-01-01 12:00:00.000', $this->storedFile->getId()); $groupId1 = $i1->add('2008-01-01 12:00:00.000', $this->storedFile->getId());
@ -123,5 +124,6 @@ class SchedulerTests extends PHPUnit_TestCase {
$i1->remove(); $i1->remove();
$i2->remove(); $i2->remove();
} }
*/
} }

View File

@ -1,22 +0,0 @@
<?php
require_once 'soundcloud-api/Services/Soundcloud.php';
class Airtime_View_Helper_SoundCloudLink extends Zend_View_Helper_Abstract
{
public function soundCloudLink()
{
$request = Zend_Controller_Front::getInstance()->getRequest();
$host = $request->getHttpHost();
$controller = $request->getControllerName();
$action = $request->getActionName();
$redirectUrl = "http://{$host}/{$controller}/{$action}";
$soundcloud = new Services_Soundcloud('2CLCxcSXYzx7QhhPVHN4A', 'pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs', $redirectUrl);
$authorizeUrl = $soundcloud->getAuthorizeUrl();
return $authorizeUrl;
}
}

View File

@ -14,7 +14,7 @@
<div class="now-playing-info show"> <div class="now-playing-info show">
<div class="recording-show" style="display: none;"></div> <div class="recording-show" style="display: none;"></div>
<span id="playlist"></span> <span id="playlist"></span>
<span class="lenght">00:00</span> <span id="show-length" class="show-length"></span>
</div> </div>
<div class="progressbar"> <div class="progressbar">
<div class="progress-show" id='progress-show' style="width:0%;"></div> <div class="progress-show" id='progress-show' style="width:0%;"></div>
@ -43,6 +43,6 @@
<div id="jquery_jplayer_1" class="jp-jplayer" style="width:0px; height:0px;"></div> <div id="jquery_jplayer_1" class="jp-jplayer" style="width:0px; height:0px;"></div>
<div id="about-txt" style="display: none;"> <div id="about-txt" style="display: none;">
<a href="http://airtime.sourcefabric.org">Airtime</a>, the open radio software for scheduling and remote station management.<br> <a href="http://airtime.sourcefabric.org">Airtime</a> <?php echo AIRTIME_VERSION ?>, the open radio software for scheduling and remote station management.<br>
© 2011 <a href="http://www.sourcefabric.org">Sourcefabric</a> o.p.s 2011. Airtime is distributed under the <a href="http://www.gnu.org/licenses/gpl-3.0-standalone.html">GNU GPL v.3</a> © 2011 <a href="http://www.sourcefabric.org">Sourcefabric</a> o.p.s 2011. Airtime is distributed under the <a href="http://www.gnu.org/licenses/gpl-3.0-standalone.html">GNU GPL v.3</a>
</div> </div>

View File

@ -10,6 +10,7 @@
<div class="text-row top"> <div class="text-row top">
<span class="spl_playlength"><?php echo $item["cliplength"] ?></span> <span class="spl_playlength"><?php echo $item["cliplength"] ?></span>
<span class="spl_cue ui-state-default"></span>
<span class="spl_title"><?php echo $item["CcFiles"]['track_title'] ?></span> <span class="spl_title"><?php echo $item["CcFiles"]['track_title'] ?></span>
</div> </div>
<div class="text-row"> <div class="text-row">

View File

@ -1,7 +1,10 @@
<div id="schedule-add-show" class="tabs ui-widget ui-widget-content block-shadow alpha-block padded"> <div id="schedule-add-show" class="tabs ui-widget ui-widget-content block-shadow alpha-block padded">
<div class="button-bar"> <div class="button-bar">
<a href="#" id="add-show-close" class="icon-link"><span class="ui-icon ui-icon-circle-close"></span>Close</a> <a href="#" id="add-show-close" class="icon-link"><span class="ui-icon ui-icon-circle-close"></span>Close</a>
<button id="add-show-submit" class="right-floated">Add this show</button> <button aria-disabled="false" role="button" id="add-show-submit" class="right-floated ui-button ui-widget ui-state-default ui-button-text-icon-primary">
<span class="ui-icon ui-icon-plusthick"></span>
<span class="ui-button-text">Add this show</span>
</button>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<h3 class="collapsible-header"><span class="arrow-icon"></span>What</h3> <h3 class="collapsible-header"><span class="arrow-icon"></span>What</h3>

View File

@ -7,7 +7,9 @@
<div id="users_wrapper" class="dataTables_wrapper"> <div id="users_wrapper" class="dataTables_wrapper">
<div class="button-holder"> <div class="button-holder">
<button type="button" id="add_user_button" name="search_add_group" class="ui-button ui-widget ui-state-default ui-button-text-icon-primary"><span class="ui-button-text">New User</span></button> <button type="button" id="add_user_button" name="search_add_group" class="ui-button ui-widget ui-state-default ui-button-text-icon-primary">
<span class="ui-icon ui-icon-plusthick"></span>
<span class="ui-button-text">New User</span></button>
</div> </div>
<table cellspacing="0" cellpadding="0" style="" id="users_datatable" class="datatable"> <table cellspacing="0" cellpadding="0" style="" id="users_datatable" class="datatable">
@ -26,6 +28,7 @@
</div> </div>
</div> </div>
<div class="user-data simple-formblock" id="user_details"> <div class="user-data simple-formblock" id="user_details">
<?php echo $this->successMessage ?>
<fieldset class="padded"> <fieldset class="padded">
<?php echo $this->form ?> <?php echo $this->form ?>
</fieldset> </fieldset>

View File

@ -234,7 +234,7 @@
</table> </table>
<table name="cc_schedule" phpName="CcSchedule"> <table name="cc_schedule" phpName="CcSchedule">
<column name="id" phpName="DbId" type="INTEGER" primaryKey="true" autoIncrement="true" required="true"/> <column name="id" phpName="DbId" type="INTEGER" primaryKey="true" autoIncrement="true" required="true"/>
<column name="playlist_id" phpName="DbPlaylistId" type="INTEGER" required="true"/> <column name="playlist_id" phpName="DbPlaylistId" type="INTEGER" required="false"/>
<column name="starts" phpName="DbStarts" type="TIMESTAMP" required="true"/> <column name="starts" phpName="DbStarts" type="TIMESTAMP" required="true"/>
<column name="ends" phpName="DbEnds" type="TIMESTAMP" required="true"/> <column name="ends" phpName="DbEnds" type="TIMESTAMP" required="true"/>
<column name="group_id" phpName="DbGroupId" type="INTEGER" required="false"/> <column name="group_id" phpName="DbGroupId" type="INTEGER" required="false"/>

View File

@ -345,7 +345,7 @@ DROP TABLE "cc_schedule" CASCADE;
CREATE TABLE "cc_schedule" CREATE TABLE "cc_schedule"
( (
"id" serial NOT NULL, "id" serial NOT NULL,
"playlist_id" INTEGER NOT NULL, "playlist_id" INTEGER,
"starts" TIMESTAMP NOT NULL, "starts" TIMESTAMP NOT NULL,
"ends" TIMESTAMP NOT NULL, "ends" TIMESTAMP NOT NULL,
"group_id" INTEGER, "group_id" INTEGER,

View File

@ -1,3 +0,0 @@
#!/bin/sh
su -l pypo -c "tail -F /etc/service/pypo-fetch/log/main/current"

View File

@ -1,3 +0,0 @@
#!/bin/sh
su -l pypo -c "tail -F /etc/service/pypo-push/log/main/current"

3
dev_tools/pr.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
su -l pypo -c "tail -F /etc/service/recorder/log/main/current"

3
dev_tools/pypoless.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
su -l pypo -c "less /etc/service/pypo/log/main/current"

3
dev_tools/pypotail.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
su -l pypo -c "tail -F /etc/service/pypo/log/main/current"

View File

@ -0,0 +1,19 @@
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version20110312121200 extends AbstractMigration
{
public function up(Schema $schema)
{
$schema->dropTable("cc_backup");
$schema->dropTable("cc_trans");
}
public function down(Schema $schema)
{
}
}

View File

@ -39,7 +39,6 @@ AirtimeInstall::InstallPostgresScriptingLanguage();
echo "* Creating database tables".PHP_EOL; echo "* Creating database tables".PHP_EOL;
AirtimeInstall::CreateDatabaseTables(); AirtimeInstall::CreateDatabaseTables();
AirtimeInstall::MigrateTables(__DIR__);
echo "* Storage directory setup".PHP_EOL; echo "* Storage directory setup".PHP_EOL;
AirtimeInstall::SetupStorageDirectory($CC_CONFIG); AirtimeInstall::SetupStorageDirectory($CC_CONFIG);
@ -47,11 +46,20 @@ AirtimeInstall::SetupStorageDirectory($CC_CONFIG);
echo "* Giving Apache permission to access the storage directory".PHP_EOL; echo "* Giving Apache permission to access the storage directory".PHP_EOL;
AirtimeInstall::ChangeDirOwnerToWebserver($CC_CONFIG["storageDir"]); AirtimeInstall::ChangeDirOwnerToWebserver($CC_CONFIG["storageDir"]);
echo "* Creating /usr/bin symlinks".PHP_EOL;
AirtimeInstall::CreateSymlinks($CC_CONFIG["storageDir"]);
echo "* Importing sample audio clips".PHP_EOL; echo "* Importing sample audio clips".PHP_EOL;
system(__DIR__."/../utils/airtime-import --copy ../audio_samples/ > /dev/null"); system(__DIR__."/../utils/airtime-import --copy ../audio_samples/ > /dev/null");
echo "* Python eggs Setup".PHP_EOL;
AirtimeInstall::SetUpPythonEggs();
echo PHP_EOL."*** Pypo Installation ***".PHP_EOL; echo PHP_EOL."*** Pypo Installation ***".PHP_EOL;
system("python ".__DIR__."/../pypo/install/pypo-install.py"); system("python ".__DIR__."/../python_apps/pypo/install/pypo-install.py");
echo PHP_EOL."*** Recorder Installation ***".PHP_EOL;
system("python ".__DIR__."/../python_apps/show-recorder/install/recorder-install.py");
echo "******************************* Install Complete *******************************".PHP_EOL; echo "******************************* Install Complete *******************************".PHP_EOL;

View File

@ -20,6 +20,7 @@ require_once(dirname(__FILE__).'/installInit.php');
// Need to check that we are superuser before running this. // Need to check that we are superuser before running this.
AirtimeInstall::ExitIfNotRoot(); AirtimeInstall::ExitIfNotRoot();
AirtimeInstall::RemoveSymlinks();
echo "******************************* Uninstall Begin ********************************".PHP_EOL; echo "******************************* Uninstall Begin ********************************".PHP_EOL;
//------------------------------------------------------------------------ //------------------------------------------------------------------------
@ -38,7 +39,7 @@ $command = "sudo -u postgres dropdb {$CC_CONFIG['dsn']['database']} 2> /dev/null
//------------------------------------------------------------------------ //------------------------------------------------------------------------
if ($dbDeleteFailed) { if ($dbDeleteFailed) {
echo " * Couldn't delete the database, so deleting all the DB tables...".PHP_EOL; echo " * Couldn't delete the database, so deleting all the DB tables...".PHP_EOL;
AirtimeInstall::DbConnect(true); AirtimeInstall::DbConnect(false);
if (!PEAR::isError($CC_DBC)) { if (!PEAR::isError($CC_DBC)) {
$sql = "select * from pg_tables where tableowner = 'airtime'"; $sql = "select * from pg_tables where tableowner = 'airtime'";
@ -81,7 +82,10 @@ if ($results == 0) {
AirtimeInstall::DeleteFilesRecursive($CC_CONFIG['storageDir']); AirtimeInstall::DeleteFilesRecursive($CC_CONFIG['storageDir']);
$command = "python ".__DIR__."/../pypo/install/pypo-uninstall.py"; $command = "python ".__DIR__."/../python_apps/pypo/install/pypo-uninstall.py";
system($command);
$command = "python ".__DIR__."/../python_apps/show-recorder/install/recorder-uninstall.py";
system($command); system($command);
echo "****************************** Uninstall Complete ******************************".PHP_EOL; echo "****************************** Uninstall Complete ******************************".PHP_EOL;

View File

@ -79,7 +79,8 @@ class AirtimeInstall {
{ {
$api_key = AirtimeInstall::GenerateRandomString(); $api_key = AirtimeInstall::GenerateRandomString();
AirtimeInstall::UpdateIniValue(__DIR__.'/../build/airtime.conf', 'api_key', $api_key); AirtimeInstall::UpdateIniValue(__DIR__.'/../build/airtime.conf', 'api_key', $api_key);
AirtimeInstall::UpdateIniValue(__DIR__.'/../pypo/config.cfg', 'api_key', "'$api_key'"); AirtimeInstall::UpdateIniValue(__DIR__.'/../python_apps/pypo/config.cfg', 'api_key', "'$api_key'");
AirtimeInstall::UpdateIniValue(__DIR__.'/../python_apps/show-recorder/config.cfg', 'api_key', "'$api_key'");
} }
public static function ExitIfNotRoot() public static function ExitIfNotRoot()
@ -112,7 +113,7 @@ class AirtimeInstall {
public static function SetupStorageDirectory($CC_CONFIG) public static function SetupStorageDirectory($CC_CONFIG)
{ {
global $CC_CONFIG, $CC_DBC; global $CC_CONFIG, $CC_DBC;
echo PHP_EOL."*** Directory Setup ***".PHP_EOL; echo PHP_EOL."*** Directory Setup ***".PHP_EOL;
foreach (array('baseFilesDir', 'storageDir') as $d) { foreach (array('baseFilesDir', 'storageDir') as $d) {
if ( !file_exists($CC_CONFIG[$d]) ) { if ( !file_exists($CC_CONFIG[$d]) ) {
@ -141,7 +142,7 @@ class AirtimeInstall {
// Create the database user // Create the database user
$command = "sudo -u postgres psql postgres --command \"CREATE USER {$CC_CONFIG['dsn']['username']} " $command = "sudo -u postgres psql postgres --command \"CREATE USER {$CC_CONFIG['dsn']['username']} "
." ENCRYPTED PASSWORD '{$CC_CONFIG['dsn']['password']}' LOGIN CREATEDB NOCREATEUSER;\" 2>/dev/null"; ." ENCRYPTED PASSWORD '{$CC_CONFIG['dsn']['password']}' LOGIN CREATEDB NOCREATEUSER;\" 2>/dev/null";
@exec($command, $output, $results); @exec($command, $output, $results);
if ($results == 0) { if ($results == 0) {
echo "* Database user '{$CC_CONFIG['dsn']['username']}' created.".PHP_EOL; echo "* Database user '{$CC_CONFIG['dsn']['username']}' created.".PHP_EOL;
@ -159,7 +160,7 @@ class AirtimeInstall {
public static function CreateDatabase() public static function CreateDatabase()
{ {
global $CC_CONFIG; global $CC_CONFIG;
$command = "sudo -u postgres createdb {$CC_CONFIG['dsn']['database']} --owner {$CC_CONFIG['dsn']['username']} 2> /dev/null"; $command = "sudo -u postgres createdb {$CC_CONFIG['dsn']['database']} --owner {$CC_CONFIG['dsn']['username']} 2> /dev/null";
@exec($command, $output, $results); @exec($command, $output, $results);
if ($results == 0) { if ($results == 0) {
@ -202,10 +203,33 @@ class AirtimeInstall {
system($command); system($command);
} }
public static function SetUpPythonEggs()
{
//install poster streaming upload
$command = "sudo easy_install poster";
@exec($command);
}
public static function DeleteFilesRecursive($p_path) public static function DeleteFilesRecursive($p_path)
{ {
$command = "rm -rf $p_path"; $command = "rm -rf $p_path";
exec($command); exec($command);
} }
} public static function CreateSymlinks(){
AirtimeInstall::RemoveSymlinks();
$dir = realpath(__DIR__."/../utils/airtime-import");
exec("ln -s $dir /usr/bin/airtime-import");
$dir = realpath(__DIR__."/../utils/airtime-clean-storage");
exec("ln -s $dir /usr/bin/airtime-clean-storage");
}
public static function RemoveSymlinks(){
exec("rm -f /usr/bin/airtime-import");
exec("rm -f /usr/bin/airtime-clean-storage");
}
}

View File

@ -0,0 +1,54 @@
#!/usr/bin/php
<?php
/**
* Repeatedly receive messages from queue until it receives a message with
* 'quit' as the body.
*
* @author Sean Murphy<sean@iamseanmurphy.com>
*/
require_once('../amqp.inc');
$HOST = 'localhost';
$PORT = 5672;
$USER = 'guest';
$PASS = 'guest';
$VHOST = '/';
$EXCHANGE = 'airtime-schedule';
$QUEUE = 'airtime-schedule-msgs';
$CONSUMER_TAG = 'airtime-consumer';
$conn = new AMQPConnection($HOST, $PORT, $USER, $PASS);
$ch = $conn->channel();
$ch->access_request($VHOST, false, false, true, true);
$ch->queue_declare($QUEUE);
$ch->exchange_declare($EXCHANGE, 'direct', false, false, false);
$ch->queue_bind($QUEUE, $EXCHANGE);
function process_message($msg) {
global $ch, $CONSUMER_TAG;
echo "\n--------\n";
echo $msg->body;
echo "\n--------\n";
$ch->basic_ack($msg->delivery_info['delivery_tag']);
// Cancel callback
if ($msg->body === 'quit') {
$ch->basic_cancel($CONSUMER_TAG);
}
}
$ch->basic_consume($QUEUE, $CONSUMER_TAG, false, false, false, false, 'process_message');
// Loop as long as the channel has callbacks registered
echo "Waiting for messages...\n";
while(count($ch->callbacks)) {
$ch->wait();
}
$ch->close();
$conn->close();
?>

267
plugins/jquery.showinfo.js Normal file
View File

@ -0,0 +1,267 @@
(function($){
$.fn.airtimeShowSchedule = function(options) {
var defaults = {
updatePeriod: 20, //seconds
sourceDomain: "http://localhost/", //where to get show status from
};
var options = $.extend(defaults, options);
return this.each(function() {
var obj = $(this);
var sd;
getServerData();
function updateWidget(){
var currentShow = sd.getCurrentShow();
var nextShows = sd.getNextShows();
var currentShowName = "";
var nextShowName = ""
if (currentShow.length > 0){
currentShowName = currentShow[0].getName();
}
if (nextShows.length > 0){
nextShowName = nextShows[0].getName();
}
tableString = "";
tableString += "<h3>On air today</h3>";
tableString += "<table width='100%' border='0' cellspacing='0' cellpadding='0' class='widget widget no-playing-list small'>"+
"<tbody>";
var shows=currentShow.concat(nextShows);
obj.empty();
for (var i=0; i<shows.length; i++){
tableString +=
"<tr>" +
"<td class='time'>"+shows[i].getRange()+"</td>" +
"<td><a href='#'>"+shows[i].getName()+"</a> <a href='#' class='listen'>Listen</a></td>" +
"</tr>";
}
tableString += "</tbody></table>";
obj.append(tableString);
}
function processData(data){
sd = new ScheduleData(data);
updateWidget();
}
function getServerData(){
$.ajax({ url: options.sourceDomain + "api/live-info/", dataType:"jsonp", success:function(data){
processData(data);
}, error:function(jqXHR, textStatus, errorThrown){}});
setTimeout(getServerData, defaults.updatePeriod*1000);
}
});
};
})(jQuery);
(function($){
$.fn.airtimeLiveInfo = function(options) {
var defaults = {
updatePeriod: 5, //seconds
sourceDomain: "http://localhost/", //where to get show status from
audioStreamSource: "" //where to get audio stream from
};
var options = $.extend(defaults, options);
return this.each(function() {
var obj = $(this);
var sd;
getServerData();
function updateWidget(){
var currentShow = sd.getCurrentShow();
var nextShows = sd.getNextShows();
var showStatus = "Offline";
var currentShowName = "";
var timeElapsed = "";
var timeRemaining = "";
var nextShowName = "";
var nextShowRange = "";
if (currentShow.length > 0){
showStatus = "On Air Now";
currentShowName = currentShow[0].getName();
timeElapsed = sd.getShowTimeElapsed(currentShow[0]);
timeRemaining = sd.getShowTimeRemaining(currentShow[0]);
}
if (nextShows.length > 0){
nextShowName = nextShows[0].getName();
nextShowRange = nextShows[0].getRange();
}
obj.empty();
obj.append("<a id='listenWadrLive'><span>Listen WADR Live</span></a>");
obj.append("<h4>"+showStatus+" &gt;&gt;</h4>");
obj.append("<ul class='widget no-playing-bar'>" +
"<li class='current'>Current: "+currentShowName+
"<span id='time-elapsed' class='time-elapsed'>"+timeElapsed+"</span>" +
"<span id='time-remaining' class='time-remaining'>"+timeRemaining+"</span>"+
"</li>" +
"<li class='next'>Next: "+nextShowName+"<span>"+nextShowRange+"</span></li>" +
"</ul>");
//refresh the UI to update the elapsed/remaining time
setTimeout(updateWidget, 1000);
}
function processData(data){
sd = new ScheduleData(data);
updateWidget();
}
function getServerData(){
$.ajax({ url: options.sourceDomain + "api/live-info/", dataType:"jsonp", success:function(data){
processData(data);
}, error:function(jqXHR, textStatus, errorThrown){}});
setTimeout(getServerData, defaults.updatePeriod*1000);
}
});
};
})(jQuery);
/* ScheduleData class BEGIN */
function ScheduleData(data){
this.data = data;
this.estimatedSchedulePosixTime;
this.currentShow = new Array();
for (var i=0; i< data.currentShow.length; i++){
this.currentShow[i] = new Show(data.currentShow[i]);
}
this.nextShows = new Array();
for (var i=0; i< data.nextShow.length; i++){
this.nextShows[i] = new Show(data.nextShow[i]);
}
this.schedulePosixTime = convertDateToPosixTime(data.schedulerTime);
this.schedulePosixTime += parseInt(data.timezoneOffset)*1000;
var date = new Date();
this.localRemoteTimeOffset = date.getTime() - this.schedulePosixTime;
}
ScheduleData.prototype.secondsTimer = function(){
var date = new Date();
this.estimatedSchedulePosixTime = date.getTime() - this.localRemoteTimeOffset;
}
ScheduleData.prototype.getCurrentShow = function(){
return this.currentShow;
}
ScheduleData.prototype.getNextShows = function() {
return this.nextShows;
}
ScheduleData.prototype.getShowTimeElapsed = function(show) {
this.secondsTimer();
var showStart = convertDateToPosixTime(show.getStartTimestamp());
return convertToHHMMSS(this.estimatedSchedulePosixTime - showStart);
};
ScheduleData.prototype.getShowTimeRemaining = function(show) {
this.secondsTimer();
var showEnd = convertDateToPosixTime(show.getEndTimestamp());
return convertToHHMMSS(showEnd - this.estimatedSchedulePosixTime);
};
/* ScheduleData class END */
/* Show class BEGIN */
function Show(showData){
this.showData = showData;
}
Show.prototype.getName = function(){
return this.showData.name;
}
Show.prototype.getRange = function(){
return getTime(this.showData.start_timestamp) + " - " + getTime(this.showData.end_timestamp);
}
Show.prototype.getStartTimestamp = function(){
return this.showData.start_timestamp;
}
Show.prototype.getEndTimestamp = function(){
return this.showData.end_timestamp;
}
/* Show class END */
function getTime(timestamp) {
var time = timestamp.split(" ")[1].split(":");
return time[0] + ":" + time[1];
};
/* Takes an input parameter of milliseconds and converts these into
* the format HH:MM:SS */
function convertToHHMMSS(timeInMS){
var time = parseInt(timeInMS);
var hours = parseInt(time / 3600000);
time -= 3600000*hours;
var minutes = parseInt(time / 60000);
time -= 60000*minutes;
var seconds = parseInt(time / 1000);
hours = hours.toString();
minutes = minutes.toString();
seconds = seconds.toString();
if (hours.length == 1)
hours = "0" + hours;
if (minutes.length == 1)
minutes = "0" + minutes;
if (seconds.length == 1)
seconds = "0" + seconds;
if (hours == "00")
return minutes + ":" + seconds;
else
return hours + ":" + minutes + ":" + seconds;
}
/* Takes in a string of format similar to 2011-02-07 02:59:57,
* and converts this to epoch/posix time. */
function convertDateToPosixTime(s){
var datetime = s.split(" ");
var date = datetime[0].split("-");
var time = datetime[1].split(":");
var year = date[0];
var month = date[1];
var day = date[2];
var hour = time[0];
var minute = time[1];
var sec = 0;
var msec = 0;
if (time[2].indexOf(".") != -1){
var temp = time[2].split(".");
sec = temp[0];
msec = temp[1];
} else
sec = time[2];
return Date.UTC(year, month, day, hour, minute, sec, msec);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -15,7 +15,11 @@
#spl_sortable > li, #spl_sortable > li,
#side_playlist > div, #side_playlist > div,
#spl_editor, #spl_editor,
.spl_artist { .spl_artist,
.spl_cue_in,
.spl_fade_in,
.spl_cue_out,
.spl_fade_out {
clear: left; clear: left;
} }
@ -35,8 +39,8 @@
#spl_sortable { #spl_sortable {
list-style: none; list-style: none;
padding:0; padding:0;
height: 400px; height: 300px;
overflow-y: scroll; overflow: auto;
width:100%; width:100%;
margin-top:0; margin-top:0;
} }
@ -52,6 +56,10 @@
border: none; border: none;
} }
#spl_name {
}
.ui-icon-closethick { .ui-icon-closethick {
margin-top: 7px; margin-top: 7px;
} }
@ -72,10 +80,18 @@
font-size:12px; font-size:12px;
} }
/*#spl_editor {
height: 50px;
}*/
#spl_editor > div > span {
/* display: inline-block;
width: 150px;*/
}
.ui-icon-closethick, .ui-icon-closethick,
.ui-icon-play, .ui-icon-play,
.spl_fade_control, .spl_fade_control,
.spl_playlength,
.spl_text_input { .spl_text_input {
cursor: pointer; cursor: pointer;
} }
@ -239,13 +255,12 @@
margin: 0; margin: 0;
} }
dd.edit-error { .edit-error {
color:#b80000; color:#b80000;
margin:0; margin:0;
padding-bottom:0; padding-bottom:0;
font-size:12px; font-size:12px;
display:none; display:none;
clear: left;
} }
/*.edit-error:last-child { /*.edit-error:last-child {
@ -276,3 +291,26 @@ dd.edit-error {
top: 3px; top: 3px;
z-index: 3; z-index: 3;
} }
#spl_sortable li .spl_cue {
background-color: transparent;
float:right;
font-size: 9px;
height: 15px;
right: 35px;
width: 33px;
margin-top:2px;
cursor:pointer;
}
#spl_sortable li .spl_cue.ui-state-default {
background: transparent url(images/cue_playlist.png) no-repeat 0 0;
border:none;
}
#spl_sortable li .spl_cue.ui-state-default:hover {
background: transparent url(images/cue_playlist.png) no-repeat 0 -15px;
border:none;
}
#spl_sortable li .spl_cue.ui-state-active, #spl_sortable li .spl_cue.ui-state-active:hover {
background: transparent url(images/cue_playlist.png) no-repeat 0 -30px;
border:none;
}

View File

@ -184,7 +184,7 @@ select {
.progressbar .progress-show-error { .progressbar .progress-show-error {
background:#d40000 url(images/progressbar_show_error.png) repeat-x 0 0; background:#d40000 url(images/progressbar_show_error.png) repeat-x 0 0;
} }
.now-playing-info .lenght { .now-playing-info .show-length {
color:#c4c4c4; color:#c4c4c4;
padding-left:6px; padding-left:6px;
} }
@ -196,6 +196,7 @@ select {
.time-info-block { .time-info-block {
padding:0 14px 0 2px; padding:0 14px 0 2px;
background:url(images/masterpanel_spacer.png) no-repeat right 0; background:url(images/masterpanel_spacer.png) no-repeat right 0;
min-width:105px;
} }
.time-info-block ul { .time-info-block ul {
margin:0; margin:0;
@ -825,7 +826,6 @@ div.ui-datepicker {
#schedule_playlist_chosen li > h3 > span.ui-icon.ui-icon-triangle-1-e, #schedule_playlist_chosen li > h3 > span.ui-icon.ui-icon-triangle-1-e,
#schedule_playlist_chosen li > h3 > span.ui-icon.ui-icon-triangle-1-s { #schedule_playlist_chosen li > h3 > span.ui-icon.ui-icon-triangle-1-s {
float:left; float:left;
margin-right: 8px; margin-right: 8px;
} }
#schedule_playlist_chosen li > h3 > span.ui-icon.ui-icon-close { #schedule_playlist_chosen li > h3 > span.ui-icon.ui-icon-close {
@ -1219,6 +1219,16 @@ ul.errors li {
margin-bottom:2px; margin-bottom:2px;
border:1px solid #c83f3f; border:1px solid #c83f3f;
} }
div.success{
color:#3B5323;
font-size:11px;
padding:2px 4px;
background:#93DB70;
margin-bottom:2px;
border:1px solid #488214;
}
.collapsible-header { .collapsible-header {
border: 1px solid #8f8f8f; border: 1px solid #8f8f8f;
background-color: #cccccc; background-color: #cccccc;
@ -1474,9 +1484,35 @@ ul.errors li {
margin:-4px 3px -3px 0; margin:-4px 3px -3px 0;
float:right; float:right;
} }
.time-flow { .time-flow {
float:right; float:right;
margin-right:4px; margin-right:4px;
}
.small-icon {
display:block;
width:20px;
height:10px;
float:right;
margin-left:3px;
}
.small-icon.recording {
background:url(images/icon_record.png) no-repeat 0 0;
}
.small-icon.rebroadcast {
background:url(images/icon_rebroadcast.png) no-repeat 0 0;
} }
.medium-icon {
display:block;
width:25px;
height:12px;
float:right;
margin-left:4px;
}
.medium-icon.recording {
background:url(images/icon_record_m.png) no-repeat 0 0;
}
.medium-icon.rebroadcast {
background:url(images/icon_rebroadcast_m.png) no-repeat 0 0;
}

View File

@ -205,13 +205,15 @@ function openFadeEditor(event) {
function openCueEditor(event) { function openCueEditor(event) {
event.stopPropagation(); event.stopPropagation();
var pos, url, li; var pos, url, li, icon;
li = $(this).parent().parent().parent(); li = $(this).parent().parent().parent();
icon = $(this);
pos = li.attr("id").split("_").pop(); pos = li.attr("id").split("_").pop();
if(li.hasClass("ui-state-active")) { if(li.hasClass("ui-state-active")) {
li.removeClass("ui-state-active"); li.removeClass("ui-state-active");
icon.attr("class", "spl_cue ui-state-default");
$("#cues_"+pos) $("#cues_"+pos)
.empty() .empty()
@ -220,6 +222,7 @@ function openCueEditor(event) {
return; return;
} }
icon.attr("class", "spl_cue ui-state-default ui-state-active");
url = '/Playlist/set-cue'; url = '/Playlist/set-cue';
highlightActive(li); highlightActive(li);
@ -253,7 +256,8 @@ function setSPLContent(json) {
$("#spl_sortable .ui-icon-closethick").click(deleteSPLItem); $("#spl_sortable .ui-icon-closethick").click(deleteSPLItem);
$(".spl_fade_control").click(openFadeEditor); $(".spl_fade_control").click(openFadeEditor);
$(".spl_playlength").click(openCueEditor); //$(".spl_playlength").click(openCueEditor);
$(".spl_cue").click(openCueEditor);
return false; return false;
} }
@ -487,7 +491,8 @@ function setUpSPL() {
$("#spl_sortable .ui-icon-closethick").click(deleteSPLItem); $("#spl_sortable .ui-icon-closethick").click(deleteSPLItem);
$(".spl_fade_control").click(openFadeEditor); $(".spl_fade_control").click(openFadeEditor);
$(".spl_playlength").click(openCueEditor); //$(".spl_playlength").click(openCueEditor);
$(".spl_cue").click(openCueEditor);
$("#spl_sortable").droppable(); $("#spl_sortable").droppable();
$("#spl_sortable" ).bind( "drop", addSPLItem); $("#spl_sortable" ).bind( "drop", addSPLItem);

View File

@ -182,7 +182,6 @@ function setAddShowEvents() {
}); });
form.find("#add-show-submit") form.find("#add-show-submit")
.button()
.click(function(event){ .click(function(event){
event.preventDefault(); event.preventDefault();

View File

@ -165,6 +165,25 @@ function eventRender(event, element, view) {
} }
$(element).find(".fc-event-title").after(div); $(element).find(".fc-event-title").after(div);
}
//add the record/rebroadcast icons if needed.
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.record === 1) {
$(element).find(".fc-event-time").after('<span class="small-icon recording"></span>');
}
if(view.name === 'month' && event.record === 1) {
$(element).find(".fc-event-title").after('<span class="small-icon recording"></span>');
}
if((view.name === 'agendaDay' || view.name === 'agendaWeek') && event.rebroadcast === 1) {
$(element).find(".fc-event-time").after('<span class="small-icon rebroadcast"></span>');
}
if(view.name === 'month' && event.rebroadcast === 1) {
$(element).find(".fc-event-title").after('<span class="small-icon rebroadcast"></span>');
} }
if(event.backgroundColor !== "") { if(event.backgroundColor !== "") {

View File

@ -2,6 +2,7 @@ function populateForm(entries){
//$('#user_details').show(); //$('#user_details').show();
$('.errors').remove(); $('.errors').remove();
$('.success').remove();
$('#user_id').val(entries.id); $('#user_id').val(entries.id);
$('#login').val(entries.login); $('#login').val(entries.login);

View File

@ -165,11 +165,12 @@ function updatePlaybar(){
/* Column 1 update */ /* Column 1 update */
$('#playlist').text("Current Show:"); $('#playlist').text("Current Show:");
var recElem = $('.recording-show');
if (currentShow.length > 0){ if (currentShow.length > 0){
$('#playlist').text(currentShow[0].name); $('#playlist').text(currentShow[0].name);
(currentShow[0].record == "1") ? recElem.show(): recElem.hide();
var recElem = $('.recording-show'); } else {
currentShow[0].record ? recElem.show(): recElem.hide(); recElem.hide();
} }
$('#show-length').empty(); $('#show-length').empty();

View File

@ -1,9 +0,0 @@
import airtime_api_client
import obp_api_client
def create_api_client(config):
if config["api_client"] == "airtime":
return campcaster_api_client.AirtimeApiClient(config)
elif config["api_client"] == "obp":
return obp_api_client.ObpApiClient(config)

View File

View File

View File

@ -1,14 +0,0 @@
#!/bin/sh
pypo_user="pypo"
export HOME="/home/pypo/"
# Location of pypo_cli.py Python script
pypo_path="/opt/pypo/bin/"
pypo_script="pypo-cli.py"
echo "*** Daemontools: starting daemon"
cd ${pypo_path}
exec 2>&1
# Note the -u when calling python! we need it to get unbuffered binary stdout and stderr
exec setuidgid ${pypo_user} \
python -u ${pypo_path}${pypo_script} \
-f
# EOF

View File

@ -1,23 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
if os.geteuid() != 0:
print "Please run this as root."
sys.exit(1)
try:
print "Stopping daemontool script pypo-fetch"
os.system("svc -dx /etc/service/pypo-fetch 2>/dev/null")
print "Stopping daemontool script pypo-push"
os.system("svc -dx /etc/service/pypo-push 2>/dev/null")
print "Stopping daemontool script pypo-liquidsoap"
os.system("svc -dx /etc/service/pypo-liquidsoap 2>/dev/null")
os.system("killall liquidsoap")
except Exception, e:
print "exception:" + str(e)

View File

@ -1,60 +0,0 @@
[loggers]
keys=root
[handlers]
keys=consoleHandler,fileHandlerERROR,fileHandlerDEBUG,nullHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandlerERROR,fileHandlerDEBUG
[logger_libs]
handlers=nullHandler
level=DEBUG
qualname="process"
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandlerERROR]
class=FileHandler
level=WARNING
formatter=simpleFormatter
args=("./error.log",)
[handler_fileHandlerDEBUG]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=("./debug.log",)
[handler_nullHandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=("/dev/null",)
[formatter_simpleFormatter]
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
datefmt=
## multitail color sheme
## pyml / python
# colorscheme:pyml:www.obp.net
# cs_re:blue:\[[^ ]*\]
# cs_re:red:CRITICAL:*
# cs_re:red,black,blink:ERROR:*
# cs_re:blue:NOTICE:*
# cs_re:cyan:INFO:*
# cs_re:green:DEBUG:*

Binary file not shown.

View File

@ -13,6 +13,7 @@
import sys import sys
import time import time
import urllib import urllib
import urllib2
import logging import logging
import json import json
import os import os
@ -90,6 +91,12 @@ class ApiClientInterface:
# You will be able to use this data in update_start_playing # You will be able to use this data in update_start_playing
def get_liquidsoap_data(self, pkey, schedule): def get_liquidsoap_data(self, pkey, schedule):
pass pass
def get_shows_to_record(self):
pass
def upload_recorded_show(self):
pass
# Put here whatever tests you want to run to make sure your API is working # Put here whatever tests you want to run to make sure your API is working
def test(self): def test(self):
@ -189,30 +196,10 @@ class AirTimeApiClient(ApiClientInterface):
def get_schedule(self, start=None, end=None): def get_schedule(self, start=None, end=None):
logger = logging.getLogger() logger = logging.getLogger()
"""
calculate start/end time range (format: YYYY-DD-MM-hh-mm-ss,YYYY-DD-MM-hh-mm-ss)
(seconds are ignored, just here for consistency)
"""
tnow = time.localtime(time.time())
if (not start):
tstart = time.localtime(time.time() - 3600 * int(self.config["cache_for"]))
start = "%04d-%02d-%02d-%02d-%02d" % (tstart[0], tstart[1], tstart[2], tstart[3], tstart[4])
if (not end):
tend = time.localtime(time.time() + 3600 * int(self.config["prepare_ahead"]))
end = "%04d-%02d-%02d-%02d-%02d" % (tend[0], tend[1], tend[2], tend[3], tend[4])
range = {}
range['start'] = start
range['end'] = end
# Construct the URL # Construct the URL
export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"] export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"]
# Insert the start and end times into the URL
export_url = export_url.replace('%%from%%', range['start'])
export_url = export_url.replace('%%to%%', range['end'])
logger.info("Fetching schedule from %s", export_url) logger.info("Fetching schedule from %s", export_url)
export_url = export_url.replace('%%api_key%%', self.config["api_key"]) export_url = export_url.replace('%%api_key%%', self.config["api_key"])
@ -225,24 +212,6 @@ class AirTimeApiClient(ApiClientInterface):
except Exception, e: except Exception, e:
print e print e
#schedule = response["playlists"]
#scheduleKeys = sorted(schedule.iterkeys())
#
## Remove all playlists that have passed current time
#try:
# tnow = time.localtime(time.time())
# str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
# toRemove = []
# for pkey in scheduleKeys:
# if (str_tnow_s > schedule[pkey]['end']):
# toRemove.append(pkey)
# else:
# break
# for index in toRemove:
# del schedule[index]
#except Exception, e:
#response["playlists"] = schedule
return status, response return status, response
@ -316,6 +285,41 @@ class AirTimeApiClient(ApiClientInterface):
except Exception, e: except Exception, e:
data["schedule_id"] = 0 data["schedule_id"] = 0
return data return data
def get_shows_to_record(self):
logger = logging.getLogger()
response = ''
try:
url = self.config["base_url"] + self.config["api_base"] + self.config["show_schedule_url"]
logger.debug(url)
url = url.replace("%%api_key%%", self.config["api_key"])
response = urllib.urlopen(url)
response = json.loads(response.read())
logger.info("shows %s", response)
except Exception, e:
logger.error("Exception: %s", e)
return response[u'shows']
def upload_recorded_show(self, data, headers):
logger = logging.getLogger()
response = ''
try:
url = self.config["base_url"] + self.config["api_base"] + self.config["upload_file_url"]
logger.debug(url)
url = url.replace("%%api_key%%", self.config["api_key"])
request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request).read().strip()
logger.info("uploaded show result %s", response)
except Exception, e:
logger.error("Exception: %s", e)
return response

View File

@ -26,6 +26,13 @@ base_url = 'http://localhost/'
ls_host = '127.0.0.1' ls_host = '127.0.0.1'
ls_port = '1234' ls_port = '1234'
############################################
# RabbitMQ settings #
############################################
rabbitmq_host = 'localhost'
rabbitmq_user = 'guest'
rabbitmq_password = 'guest'
############################################ ############################################
# pypo preferences # # pypo preferences #
############################################ ############################################
@ -34,15 +41,13 @@ cache_for = 24 #how long to hold the cache, in hours
# Poll interval in seconds. # Poll interval in seconds.
# #
# This will rarely need to be changed because any schedule changes are
# automatically sent to pypo immediately.
#
# This is how often the poll script downloads new schedules and files from the # This is how often the poll script downloads new schedules and files from the
# server. # server in the event that no changes are made to the schedule.
# #
# For production use, this number depends on whether you plan on making any poll_interval = 3600 # in seconds.
# last-minute changes to your schedule. This number should be set to half of
# the time you expect to "lock-in" your schedule. So if your schedule is set
# 24 hours in advance, this can be set to poll every 12 hours.
#
poll_interval = 5 # in seconds.
# Push interval in seconds. # Push interval in seconds.
@ -52,7 +57,7 @@ poll_interval = 5 # in seconds.
# #
# It's hard to imagine a situation where this should be more than 1 second. # It's hard to imagine a situation where this should be more than 1 second.
# #
push_interval = 2 # in seconds push_interval = 1 # in seconds
# 'pre' or 'otf'. 'pre' cues while playlist preparation # 'pre' or 'otf'. 'pre' cues while playlist preparation
# while 'otf' (on the fly) cues while loading into ls # while 'otf' (on the fly) cues while loading into ls
@ -80,7 +85,7 @@ version_url = 'version/api_key/%%api_key%%'
# Schedule export path. # Schedule export path.
# %%from%% - starting date/time in the form YYYY-MM-DD-hh-mm # %%from%% - starting date/time in the form YYYY-MM-DD-hh-mm
# %%to%% - starting date/time in the form YYYY-MM-DD-hh-mm # %%to%% - starting date/time in the form YYYY-MM-DD-hh-mm
export_url = 'schedule/api_key/%%api_key%%/from/%%from%%/to/%%to%%' export_url = 'schedule/api_key/%%api_key%%'
# Update whether a schedule group has begun playing. # Update whether a schedule group has begun playing.
update_item_url = 'notify-schedule-group-play/api_key/%%api_key%%/schedule_id/%%schedule_id%%' update_item_url = 'notify-schedule-group-play/api_key/%%api_key%%/schedule_id/%%schedule_id%%'

View File

@ -1,10 +1,12 @@
#!/bin/sh #!/bin/sh
ls_user="pypo" ls_user="pypo"
export HOME="/home/pypo/" export HOME="/home/pypo/"
api_client_path="/opt/pypo/"
ls_path="/opt/pypo/bin/liquidsoap/liquidsoap" ls_path="/opt/pypo/bin/liquidsoap/liquidsoap"
ls_param="/opt/pypo/bin/scripts/ls_script.liq" ls_param="/opt/pypo/bin/scripts/ls_script.liq"
echo "*** Daemontools: starting liquidsoap" echo "*** Daemontools: starting liquidsoap"
exec 2>&1 exec 2>&1
echo "exec sudo -u ${ls_user} ${ls_path} ${ls_param} "
cd /opt/pypo/bin/scripts && sudo -u ${ls_user} ${ls_path} ${ls_param} cd /opt/pypo/bin/scripts
sudo PYTHONPATH=${api_client_path} -u ${ls_user} ${ls_path} ${ls_param}
# EOF # EOF

View File

@ -3,12 +3,16 @@ pypo_user="pypo"
export HOME="/home/pypo/" export HOME="/home/pypo/"
# Location of pypo_cli.py Python script # Location of pypo_cli.py Python script
pypo_path="/opt/pypo/bin/" pypo_path="/opt/pypo/bin/"
api_client_path="/opt/pypo/"
pypo_script="pypo-cli.py" pypo_script="pypo-cli.py"
echo "*** Daemontools: starting daemon" echo "*** Daemontools: starting daemon"
cd ${pypo_path} cd ${pypo_path}
exec 2>&1 exec 2>&1
PYTHONPATH=${api_client_path}:$PYTHONPATH
export PYTHONPATH
# Note the -u when calling python! we need it to get unbuffered binary stdout and stderr # Note the -u when calling python! we need it to get unbuffered binary stdout and stderr
exec setuidgid ${pypo_user} \ exec setuidgid ${pypo_user} \
python -u ${pypo_path}${pypo_script} \ python -u ${pypo_path}${pypo_script}
-p
# EOF # EOF

View File

@ -36,14 +36,16 @@ def create_user(username):
os.system("adduser --system --quiet --group --shell /bin/bash "+username) os.system("adduser --system --quiet --group --shell /bin/bash "+username)
#set pypo password #set pypo password
p = os.popen('/usr/bin/passwd pypo', 'w') p = os.popen('/usr/bin/passwd pypo 1>/dev/null 2>&1', 'w')
p.write('pypo\n') p.write('pypo\n')
p.write('pypo\n') p.write('pypo\n')
p.close() p.close()
else: else:
print "User already exists." print "User already exists."
#add pypo to audio group #add pypo to audio group
os.system("adduser " + username + " audio 2>&1 1>/dev/null") os.system("adduser " + username + " audio 1>/dev/null 2>&1")
#add pypo to pulse-access group
#os.system("adduser " + username + " pulse-access 1>/dev/null 2>&1")
def copy_dir(src_dir, dest_dir): def copy_dir(src_dir, dest_dir):
if (os.path.exists(dest_dir)) and (dest_dir != "/"): if (os.path.exists(dest_dir)) and (dest_dir != "/"):
@ -63,7 +65,7 @@ def get_current_script_dir():
try: try:
current_script_dir = get_current_script_dir() current_script_dir = get_current_script_dir()
print "Checking and removing any existing pypo processes" print "Checking and removing any existing pypo processes"
os.system("python %s/pypo-uninstall.py 2>&1 1>/dev/null"% current_script_dir) os.system("python %s/pypo-uninstall.py 1>/dev/null 2>&1"% current_script_dir)
time.sleep(5) time.sleep(5)
# Create users # Create users
@ -79,14 +81,8 @@ try:
create_path(BASE_PATH+"cache") create_path(BASE_PATH+"cache")
create_path(BASE_PATH+"files") create_path(BASE_PATH+"files")
create_path(BASE_PATH+"tmp") create_path(BASE_PATH+"tmp")
create_path(BASE_PATH+"files/basic")
create_path(BASE_PATH+"files/fallback")
create_path(BASE_PATH+"files/jingles")
create_path(BASE_PATH+"archive") create_path(BASE_PATH+"archive")
print "Copying pypo files"
shutil.copy("%s/../scripts/silence.mp3"%current_script_dir, BASE_PATH+"files/basic")
if platform.architecture()[0] == '64bit': if platform.architecture()[0] == '64bit':
print "Installing 64-bit liquidsoap binary" print "Installing 64-bit liquidsoap binary"
shutil.copy("%s/../liquidsoap/liquidsoap64"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir) shutil.copy("%s/../liquidsoap/liquidsoap64"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)
@ -98,28 +94,21 @@ try:
sys.exit(1) sys.exit(1)
copy_dir("%s/.."%current_script_dir, BASE_PATH+"bin/") copy_dir("%s/.."%current_script_dir, BASE_PATH+"bin/")
copy_dir("%s/../../api_clients"%current_script_dir, BASE_PATH+"api_clients/")
print "Setting permissions" print "Setting permissions"
os.system("chmod -R 755 "+BASE_PATH) os.system("chmod -R 755 "+BASE_PATH)
os.system("chown -R pypo:pypo "+BASE_PATH) os.system("chown -R pypo:pypo "+BASE_PATH)
print "Installing daemontool script pypo-fetch" print "Installing pypo daemon"
create_path("/etc/service/pypo-fetch") create_path("/etc/service/pypo")
create_path("/etc/service/pypo-fetch/log") create_path("/etc/service/pypo/log")
shutil.copy("%s/pypo-daemontools-fetch.sh"%current_script_dir, "/etc/service/pypo-fetch/run") shutil.copy("%s/pypo-daemontools.sh"%current_script_dir, "/etc/service/pypo/run")
shutil.copy("%s/pypo-daemontools-logger.sh"%current_script_dir, "/etc/service/pypo-fetch/log/run") shutil.copy("%s/pypo-daemontools-logger.sh"%current_script_dir, "/etc/service/pypo/log/run")
os.system("chmod -R 755 /etc/service/pypo-fetch") os.system("chmod -R 755 /etc/service/pypo")
os.system("chown -R pypo:pypo /etc/service/pypo-fetch") os.system("chown -R pypo:pypo /etc/service/pypo")
print "Installing daemontool script pypo-push" print "Installing liquidsoap daemon"
create_path("/etc/service/pypo-push")
create_path("/etc/service/pypo-push/log")
shutil.copy("%s/pypo-daemontools-push.sh"%current_script_dir, "/etc/service/pypo-push/run")
shutil.copy("%s/pypo-daemontools-logger.sh"%current_script_dir, "/etc/service/pypo-push/log/run")
os.system("chmod -R 755 /etc/service/pypo-push")
os.system("chown -R pypo:pypo /etc/service/pypo-push")
print "Installing daemontool script pypo-liquidsoap"
create_path("/etc/service/pypo-liquidsoap") create_path("/etc/service/pypo-liquidsoap")
create_path("/etc/service/pypo-liquidsoap/log") create_path("/etc/service/pypo-liquidsoap/log")
shutil.copy("%s/pypo-daemontools-liquidsoap.sh"%current_script_dir, "/etc/service/pypo-liquidsoap/run") shutil.copy("%s/pypo-daemontools-liquidsoap.sh"%current_script_dir, "/etc/service/pypo-liquidsoap/run")
@ -134,16 +123,12 @@ try:
found = True found = True
p = Popen('svstat /etc/service/pypo-fetch', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) p = Popen('svstat /etc/service/pypo', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
output = p.stdout.read() output = p.stdout.read()
if (output.find("unable to open supervise/ok: file does not exist") >= 0): if (output.find("unable to open supervise/ok: file does not exist") >= 0):
found = False found = False
print output print output
p = Popen('svstat /etc/service/pypo-push', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
output = p.stdout.read()
print output
p = Popen('svstat /etc/service/pypo-liquidsoap', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) p = Popen('svstat /etc/service/pypo-liquidsoap', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
output = p.stdout.read() output = p.stdout.read()
print output print output
@ -152,6 +137,7 @@ try:
print "Pypo install has completed, but daemontools is not running, please make sure you have it installed and then reboot." print "Pypo install has completed, but daemontools is not running, please make sure you have it installed and then reboot."
except Exception, e: except Exception, e:
print "exception:" + str(e) print "exception:" + str(e)
sys.exit(1)

View File

@ -9,12 +9,9 @@ if os.geteuid() != 0:
sys.exit(1) sys.exit(1)
try: try:
print "Starting daemontool script pypo-fetch" print "Starting daemontool script pypo"
os.system("svc -u /etc/service/pypo-fetch") os.system("svc -u /etc/service/pypo")
print "Starting daemontool script pypo-push"
os.system("svc -u /etc/service/pypo-push")
print "Starting daemontool script pypo-liquidsoap" print "Starting daemontool script pypo-liquidsoap"
os.system("svc -u /etc/service/pypo-liquidsoap") os.system("svc -u /etc/service/pypo-liquidsoap")

View File

@ -0,0 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
if os.geteuid() != 0:
print "Please run this as root."
sys.exit(1)
try:
print "Stopping daemontool script pypo"
os.system("svc -dx /etc/service/pypo 1>/dev/null 2>&1")
if os.path.exists("/etc/service/pypo-fetch"):
os.system("svc -dx /etc/service/pypo-fetch 1>/dev/null 2>&1")
if os.path.exists("/etc/service/pypo-push"):
os.system("svc -dx /etc/service/pypo-push 1>/dev/null 2>&1")
print "Stopping daemontool script pypo-liquidsoap"
os.system("svc -dx /etc/service/pypo-liquidsoap 1>/dev/null 2>&1")
os.system("killall liquidsoap")
except Exception, e:
print "exception:" + str(e)

View File

@ -15,13 +15,13 @@ def remove_path(path):
os.system("rm -rf " + path) os.system("rm -rf " + path)
def remove_user(username): def remove_user(username):
os.system("killall -u %s 2>&1 1>/dev/null" % username) os.system("killall -u %s 1>/dev/null 2>&1" % username)
#allow all process to be completely closed before we attempt to delete user #allow all process to be completely closed before we attempt to delete user
print "Waiting for processes to close..." print "Waiting for processes to close..."
time.sleep(5) time.sleep(5)
os.system("deluser --remove-home " + username + " 1>/dev/null") os.system("deluser --remove-home " + username + " 1>/dev/null 2>&1")
def get_current_script_dir(): def get_current_script_dir():
current_script_dir = os.path.realpath(__file__) current_script_dir = os.path.realpath(__file__)
@ -37,16 +37,19 @@ try:
print "Removing pypo files" print "Removing pypo files"
remove_path(BASE_PATH) remove_path(BASE_PATH)
print "Removing daemontool script pypo-fetch" print "Removing daemontool script pypo"
remove_path("rm -rf /etc/service/pypo-fetch") remove_path("/etc/service/pypo")
print "Removing daemontool script pypo-push" if os.path.exists("/etc/service/pypo-fetch"):
remove_path("rm -rf /etc/service/pypo-push") remove_path("/etc/service/pypo-fetch")
if os.path.exists("/etc/service/pypo-push"):
remove_path("/etc/service/pypo-push")
print "Removing daemontool script pypo-liquidsoap" print "Removing daemontool script pypo-liquidsoap"
remove_path("rm -rf /etc/service/pypo-liquidsoap") remove_path("/etc/service/pypo-liquidsoap")
remove_user("pypo") remove_user("pypo")
print "Uninstall complete." print "Pypo uninstall complete."
except Exception, e: except Exception, e:
print "exception:" + str(e) print "exception:" + str(e)

View File

@ -0,0 +1,34 @@
[loggers]
keys=root,fetch,push
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_fetch]
level=DEBUG
handlers=consoleHandler
qualname=fetch
propagate=0
[logger_push]
level=DEBUG
handlers=consoleHandler
qualname=push
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
datefmt=

View File

@ -7,14 +7,13 @@ Python part of radio playout (pypo)
The main functions are "fetch" (./pypo_cli.py -f) and "push" (./pypo_cli.py -p) The main functions are "fetch" (./pypo_cli.py -f) and "push" (./pypo_cli.py -p)
""" """
# python defaults (debian default)
import time import time
#import calendar #import calendar
#import traceback #import traceback
from optparse import * from optparse import *
import sys import sys
import os
import signal
#import datetime #import datetime
import logging import logging
import logging.config import logging.config
@ -27,11 +26,11 @@ import logging.config
#import string #import string
#import operator #import operator
#import inspect #import inspect
from Queue import Queue
from pypopush import PypoPush from pypopush import PypoPush
from pypofetch import PypoFetch from pypofetch import PypoFetch
# additional modules (should be checked)
from configobj import ConfigObj from configobj import ConfigObj
# custom imports # custom imports
@ -53,7 +52,6 @@ parser.add_option("-v", "--compat", help="Check compatibility with server API ve
parser.add_option("-t", "--test", help="Do a test to make sure everything is working properly.", default=False, action="store_true", dest="test") parser.add_option("-t", "--test", help="Do a test to make sure everything is working properly.", default=False, action="store_true", dest="test")
parser.add_option("-f", "--fetch-scheduler", help="Fetch the schedule from server. This is a polling process that runs forever.", default=False, action="store_true", dest="fetch_scheduler") parser.add_option("-f", "--fetch-scheduler", help="Fetch the schedule from server. This is a polling process that runs forever.", default=False, action="store_true", dest="fetch_scheduler")
parser.add_option("-p", "--push-scheduler", help="Push the schedule to Liquidsoap. This is a polling process that runs forever.", default=False, action="store_true", dest="push_scheduler") parser.add_option("-p", "--push-scheduler", help="Push the schedule to Liquidsoap. This is a polling process that runs forever.", default=False, action="store_true", dest="push_scheduler")
parser.add_option("-b", "--cleanup", help="Cleanup", default=False, action="store_true", dest="cleanup") parser.add_option("-b", "--cleanup", help="Cleanup", default=False, action="store_true", dest="cleanup")
parser.add_option("-c", "--check", help="Check the cached schedule and exit", default=False, action="store_true", dest="check") parser.add_option("-c", "--check", help="Check the cached schedule and exit", default=False, action="store_true", dest="check")
@ -66,10 +64,6 @@ logging.config.fileConfig("logging.cfg")
# loading config file # loading config file
try: try:
config = ConfigObj('config.cfg') config = ConfigObj('config.cfg')
POLL_INTERVAL = float(config['poll_interval'])
PUSH_INTERVAL = float(config['push_interval'])
LS_HOST = config['ls_host']
LS_PORT = config['ls_port']
except Exception, e: except Exception, e:
print 'Error loading config file: ', e print 'Error loading config file: ', e
sys.exit() sys.exit()
@ -121,55 +115,44 @@ class Global:
for media in playlist['medias']: for media in playlist['medias']:
print media print media
def keyboardInterruptHandler(signum, frame):
print "\nKeyboard Interrupt\n"
sys.exit();
if __name__ == '__main__': if __name__ == '__main__':
print '###########################################' print '###########################################'
print '# *** pypo *** #' print '# *** pypo *** #'
print '# Liquidsoap + External Scheduler #' print '# Liquidsoap Scheduled Playout System #'
print '# Playout System #'
print '###########################################' print '###########################################'
signal.signal(signal.SIGINT, keyboardInterruptHandler)
# initialize # initialize
g = Global() g = Global()
g.selfcheck() g.selfcheck()
logger = logging.getLogger() logger = logging.getLogger()
loops = 0
if options.test: if options.test:
g.test_api() g.test_api()
sys.exit() sys.exit()
q = Queue()
if options.fetch_scheduler:
pf = PypoFetch()
while True:
try: pf.fetch('scheduler')
except Exception, e:
print e
sys.exit()
if (loops%2 == 0): pp = PypoPush(q)
logger.info("heartbeat") pp.daemon = True
loops += 1 pp.start()
time.sleep(POLL_INTERVAL)
if options.push_scheduler: pf = PypoFetch(q)
pp = PypoPush() pf.daemon = True
while True: pf.start()
try: pp.push('scheduler')
except Exception, e:
print 'PUSH ERROR!! WILL EXIT NOW:('
print e
sys.exit()
if (loops%60 == 0): while True: time.sleep(3600)
logger.info("heartbeat")
loops += 1
time.sleep(PUSH_INTERVAL)
#pp.join()
#pf.join()
"""
if options.check: if options.check:
try: g.check_schedule() try: g.check_schedule()
except Exception, e: except Exception, e:
@ -179,4 +162,4 @@ if __name__ == '__main__':
try: pf.cleanup('scheduler') try: pf.cleanup('scheduler')
except Exception, e: except Exception, e:
print e print e
sys.exit() """

View File

@ -9,146 +9,151 @@ import random
import string import string
import json import json
import telnetlib import telnetlib
import math
from threading import Thread
from subprocess import Popen, PIPE
# For RabbitMQ
from kombu.connection import BrokerConnection
from kombu.messaging import Exchange, Queue, Consumer, Producer
from api_clients import api_client from api_clients import api_client
from util import CueFile from util import CueFile
from configobj import ConfigObj from configobj import ConfigObj
# configure logging
logging.config.fileConfig("logging.cfg")
# loading config file # loading config file
try: try:
config = ConfigObj('config.cfg') config = ConfigObj('config.cfg')
POLL_INTERVAL = float(config['poll_interval'])
PUSH_INTERVAL = 0.5
#PUSH_INTERVAL = float(config['push_interval'])
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'])
except Exception, e: except Exception, e:
print 'Error loading config file: ', e print 'Error loading config file: ', e
sys.exit() sys.exit()
class PypoFetch: # Yuk - using a global, i know!
def __init__(self): SCHEDULE_PUSH_MSG = []
"""
Handle a message from RabbitMQ, put it into our yucky global var.
Hopefully there is a better way to do this.
"""
def handle_message(body, message):
logger = logging.getLogger('fetch')
global SCHEDULE_PUSH_MSG
logger.info("Received schedule from RabbitMQ: " + message.body)
SCHEDULE_PUSH_MSG = json.loads(message.body)
# ACK the message to take it off the queue
message.ack()
class PypoFetch(Thread):
def __init__(self, q):
Thread.__init__(self)
logger = logging.getLogger('fetch')
self.api_client = api_client.api_client_factory(config) self.api_client = api_client.api_client_factory(config)
self.cue_file = CueFile() self.cue_file = CueFile()
self.set_export_source('scheduler') self.set_export_source('scheduler')
self.queue = q
logger.info("Initializing RabbitMQ stuff")
schedule_exchange = Exchange("airtime-schedule", "direct", durable=True, auto_delete=True)
schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo")
self.connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], "/")
channel = self.connection.channel()
consumer = Consumer(channel, schedule_queue)
consumer.register_callback(handle_message)
consumer.consume()
logger.info("PypoFetch: init complete")
def set_export_source(self, export_source): def set_export_source(self, export_source):
self.export_source = export_source self.export_source = export_source
self.cache_dir = config["cache_dir"] + self.export_source + '/' self.cache_dir = config["cache_dir"] + self.export_source + '/'
self.schedule_file = self.cache_dir + 'schedule.pickle'
self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle" def check_matching_timezones(self, server_timezone):
logger = logging.getLogger('fetch')
process = Popen(["date", "+%z"], stdout=PIPE)
pypo_timezone = (process.communicate()[0]).strip(' \r\n\t')
if server_timezone != pypo_timezone:
logger.error("Server and pypo timezone offsets do not match. Audio playback may not start when expected!")
logger.error("Server timezone offset: %s", server_timezone)
logger.error("Pypo timezone offset: %s", pypo_timezone)
""" """
Fetching part of pypo Process the schedule
- Reads the scheduled entries of a given range (actual time +/- "prepare_ahead" / "cache_for") - Reads the scheduled entries of a given range (actual time +/- "prepare_ahead" / "cache_for")
- Saves a serialized file of the schedule - Saves a serialized file of the schedule
- playlists are prepared. (brought to liquidsoap format) and, if not mounted via nsf, files are copied - playlists are prepared. (brought to liquidsoap format) and, if not mounted via nsf, files are copied
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 cashed files - runs the cleanup routine, to get rid of unused cashed files
""" """
def fetch(self, export_source): def process_schedule(self, schedule_data, export_source):
""" logger = logging.getLogger('fetch')
wrapper script for fetching the whole schedule (in json) self.schedule = schedule_data["playlists"]
"""
logger = logging.getLogger()
try: os.mkdir(self.cache_dir) self.check_matching_timezones(schedule_data["server_timezone"])
except Exception, e: pass
# Push stream metadata to liquidsoap
# get schedule # TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
stream_metadata = schedule_data['stream_metadata']
try: try:
while self.get_schedule() != 1: tn = telnetlib.Telnet(LS_HOST, LS_PORT)
logger.warning("failed to read from export url") #encode in latin-1 due to telnet protocol not supporting utf-8
time.sleep(1) tn.write(('vars.stream_metadata_type %s\n' % stream_metadata['format']).encode('latin-1'))
tn.write(('vars.station_name %s\n' % stream_metadata['station_name']).encode('latin-1'))
tn.write('exit\n')
tn.read_all()
except Exception, e:
logger.error("Exception %s", e)
status = 0
# Download all the media and put playlists in liquidsoap format
try:
playlists = self.prepare_playlists()
except Exception, e: logger.error("%s", e) except Exception, e: logger.error("%s", e)
# prepare the playlists # Send the data to pypo-push
if config["cue_style"] == 'pre': scheduled_data = dict()
try: self.prepare_playlists_cue() scheduled_data['playlists'] = playlists
except Exception, e: logger.error("%s", e) scheduled_data['schedule'] = self.schedule
elif config["cue_style"] == 'otf': scheduled_data['stream_metadata'] = schedule_data["stream_metadata"]
try: self.prepare_playlists(self.export_source) self.queue.put(scheduled_data)
except Exception, e: logger.error("%s", e)
# cleanup # cleanup
try: self.cleanup(self.export_source) try: self.cleanup(self.export_source)
except Exception, e: logger.error("%s", e) except Exception, e: logger.error("%s", e)
def get_schedule(self):
logger = logging.getLogger()
status, response = self.api_client.get_schedule()
if status == 1:
logger.info("dump serialized schedule to %s", self.schedule_file)
schedule = response['playlists']
stream_metadata = response['stream_metadata']
try:
schedule_file = open(self.schedule_file, "w")
pickle.dump(schedule, schedule_file)
schedule_file.close()
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
#encode in latin-1 due to telnet protocol not supporting utf-8
tn.write(('vars.stream_metadata_type %s\n' % stream_metadata['format']).encode('latin-1'))
tn.write(('vars.station_name %s\n' % stream_metadata['station_name']).encode('latin-1'))
tn.write('exit\n')
logger.debug(tn.read_all())
except Exception, e:
logger.error("Exception %s", e)
status = 0
return status
#TODO this is a duplicate function!!!
def load_schedule(self):
logger = logging.getLogger()
schedule = None
# create the file if it doesnt exist
if (not os.path.exists(self.schedule_file)):
logger.debug('creating file ' + self.schedule_file)
open(self.schedule_file, 'w').close()
else:
# load the schedule from cache
#logger.debug('loading schedule file '+self.schedule_file)
try:
schedule_file = open(self.schedule_file, "r")
schedule = pickle.load(schedule_file)
schedule_file.close()
except Exception, e:
logger.error('%s', e)
return schedule
""" """
Alternative version of playout preparation. Every playlist entry is In this function every audio file is cut as necessary (cue_in/cue_out != 0)
pre-cued if neccessary (cue_in/cue_out != 0) and stored in the and stored in a playlist folder.
playlist folder. file is e.g. 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3
file is eg 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3
""" """
def prepare_playlists_cue(self): def prepare_playlists(self):
logger = logging.getLogger() logger = logging.getLogger('fetch')
# Load schedule from disk schedule = self.schedule
schedule = self.load_schedule() playlists = dict()
# Dont do anything if schedule is empty # Dont do anything if schedule is empty
if (not schedule): if not schedule:
logger.debug("Schedule is empty.") logger.debug("Schedule is empty.")
return return playlists
scheduleKeys = sorted(schedule.iterkeys()) scheduleKeys = sorted(schedule.iterkeys())
try: try:
for pkey in scheduleKeys: for pkey in scheduleKeys:
logger.info("found playlist at %s", pkey) logger.info("Playlist starting at %s", pkey)
playlist = schedule[pkey] playlist = schedule[pkey]
# create playlist directory # create playlist directory
@ -157,15 +162,15 @@ class PypoFetch:
except Exception, e: except Exception, e:
pass pass
logger.debug('*****************************************') #logger.debug('*****************************************')
logger.debug('pkey: ' + str(pkey)) #logger.debug('pkey: ' + str(pkey))
logger.debug('cached at : ' + self.cache_dir + str(pkey)) #logger.debug('cached at : ' + self.cache_dir + str(pkey))
logger.debug('subtype: ' + str(playlist['subtype'])) #logger.debug('subtype: ' + str(playlist['subtype']))
logger.debug('played: ' + str(playlist['played'])) #logger.debug('played: ' + str(playlist['played']))
logger.debug('schedule id: ' + str(playlist['schedule_id'])) #logger.debug('schedule id: ' + str(playlist['schedule_id']))
logger.debug('duration: ' + str(playlist['duration'])) #logger.debug('duration: ' + str(playlist['duration']))
logger.debug('source id: ' + str(playlist['x_ident'])) #logger.debug('source id: ' + str(playlist['x_ident']))
logger.debug('*****************************************') #logger.debug('*****************************************')
if int(playlist['played']) == 1: if int(playlist['played']) == 1:
logger.info("playlist %s already played / sent to liquidsoap, so will ignore it", pkey) logger.info("playlist %s already played / sent to liquidsoap, so will ignore it", pkey)
@ -173,34 +178,32 @@ class PypoFetch:
elif int(playlist['subtype']) > 0 and int(playlist['subtype']) < 5: elif int(playlist['subtype']) > 0 and int(playlist['subtype']) < 5:
ls_playlist = self.handle_media_file(playlist, pkey) ls_playlist = self.handle_media_file(playlist, pkey)
# write playlist file playlists[pkey] = ls_playlist
plfile = open(self.cache_dir + str(pkey) + '/list.lsp', "w")
plfile.write(json.dumps(ls_playlist))
plfile.close()
logger.info('ls playlist file written to %s', self.cache_dir + str(pkey) + '/list.lsp')
except Exception, e: except Exception, e:
logger.info("%s", e) logger.info("%s", e)
return playlists
"""
Download and cache the media files.
This handles both remote and local files.
Returns an updated ls_playlist string.
"""
def handle_media_file(self, playlist, pkey): def handle_media_file(self, playlist, pkey):
"""
This handles both remote and local files.
Returns an updated ls_playlist string.
"""
ls_playlist = [] ls_playlist = []
logger = logging.getLogger() logger = logging.getLogger('fetch')
for media in playlist['medias']: for media in playlist['medias']:
logger.debug("Processing track %s", media['uri']) logger.debug("Processing track %s", media['uri'])
fileExt = os.path.splitext(media['uri'])[1] fileExt = os.path.splitext(media['uri'])[1]
try: try:
if str(media['cue_in']) == '0' and str(media['cue_out']) == '0': if str(media['cue_in']) == '0' and str(media['cue_out']) == '0':
logger.debug('No cue in/out detected for this file') #logger.debug('No cue in/out detected for this file')
dst = "%s%s/%s%s" % (self.cache_dir, str(pkey), str(media['id']), str(fileExt)) dst = "%s%s/%s%s" % (self.cache_dir, str(pkey), str(media['id']), str(fileExt))
do_cue = False do_cue = False
else: else:
logger.debug('Cue in/out detected') #logger.debug('Cue in/out detected')
dst = "%s%s/%s_cue_%s-%s%s" % \ dst = "%s%s/%s_cue_%s-%s%s" % \
(self.cache_dir, str(pkey), str(media['id']), str(float(media['cue_in']) / 1000), str(float(media['cue_out']) / 1000), str(fileExt)) (self.cache_dir, str(pkey), str(media['id']), str(float(media['cue_in']) / 1000), str(float(media['cue_out']) / 1000), str(fileExt))
do_cue = True do_cue = True
@ -225,7 +228,7 @@ class PypoFetch:
% (str(media['export_source']), media['id'], 0, str(float(media['fade_in']) / 1000), \ % (str(media['export_source']), media['id'], 0, str(float(media['fade_in']) / 1000), \
str(float(media['fade_out']) / 1000), media['row_id'],dst) str(float(media['fade_out']) / 1000), media['row_id'],dst)
logger.debug(pl_entry) #logger.debug(pl_entry)
""" """
Tracks are only added to the playlist if they are accessible Tracks are only added to the playlist if they are accessible
@ -239,7 +242,7 @@ class PypoFetch:
entry['show_name'] = playlist['show_name'] entry['show_name'] = playlist['show_name']
ls_playlist.append(entry) ls_playlist.append(entry)
logger.debug("everything ok, adding %s to playlist", pl_entry) #logger.debug("everything ok, adding %s to playlist", pl_entry)
else: else:
print 'zero-file: ' + dst + ' from ' + media['uri'] print 'zero-file: ' + dst + ' from ' + media['uri']
logger.warning("zero-size file - skipping %s. will not add it to playlist", dst) logger.warning("zero-size file - skipping %s. will not add it to playlist", dst)
@ -251,11 +254,15 @@ class PypoFetch:
return ls_playlist return ls_playlist
"""
Download a file from a remote server and store it in the cache.
"""
def handle_remote_file(self, media, dst, do_cue): def handle_remote_file(self, media, dst, do_cue):
logger = logging.getLogger() logger = logging.getLogger('fetch')
if do_cue == False: if do_cue == False:
if os.path.isfile(dst): if os.path.isfile(dst):
logger.debug("file already in cache: %s", dst) pass
#logger.debug("file already in cache: %s", dst)
else: else:
logger.debug("try to download %s", media['uri']) logger.debug("try to download %s", media['uri'])
self.api_client.get_media(media['uri'], dst) self.api_client.get_media(media['uri'], dst)
@ -296,12 +303,12 @@ class PypoFetch:
logger.error("%s", e) logger.error("%s", e)
"""
Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
and deletes them.
"""
def cleanup(self, export_source): def cleanup(self, export_source):
""" logger = logging.getLogger('fetch')
Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
and deletes them.
"""
logger = logging.getLogger()
offset = 3600 * int(config["cache_for"]) offset = 3600 * int(config["cache_for"])
now = time.time() now = time.time()
@ -323,3 +330,41 @@ class PypoFetch:
print e print e
logger.error("%s", e) logger.error("%s", e)
"""
Main loop of the thread:
Wait for schedule updates from RabbitMQ, but in case there arent any,
poll the server to get the upcoming schedule.
"""
def run(self):
logger = logging.getLogger('fetch')
try: os.mkdir(self.cache_dir)
except Exception, e: pass
# Bootstrap: since we are just starting up, we need to grab the
# most recent schedule. After that we can just wait for updates.
status, schedule_data = self.api_client.get_schedule()
if status == 1:
self.process_schedule(schedule_data, "scheduler")
logger.info("Bootstrap complete: got initial copy of the schedule")
loops = 1
while True:
logger.info("Loop #"+str(loops))
try:
# Wait for messages from RabbitMQ. Timeout if we
# dont get any after POLL_INTERVAL.
self.connection.drain_events(timeout=POLL_INTERVAL)
# Hooray for globals!
schedule_data = SCHEDULE_PUSH_MSG
status = 1
except:
# We didnt get a message for a while, so poll the server
# to get an updated schedule.
status, schedule_data = self.api_client.get_schedule()
if status == 1:
self.process_schedule(schedule_data, "scheduler")
loops += 1

View File

@ -7,29 +7,38 @@ import pickle
import telnetlib import telnetlib
import calendar import calendar
import json import json
import math
from threading import Thread
from api_clients import api_client from api_clients import api_client
from util import CueFile from util import CueFile
from configobj import ConfigObj from configobj import ConfigObj
# configure logging
logging.config.fileConfig("logging.cfg")
# loading config file # loading config file
try: try:
config = ConfigObj('config.cfg') config = ConfigObj('config.cfg')
POLL_INTERVAL = float(config['poll_interval'])
PUSH_INTERVAL = 0.5
#PUSH_INTERVAL = float(config['push_interval'])
LS_HOST = config['ls_host'] LS_HOST = config['ls_host']
LS_PORT = config['ls_port'] LS_PORT = config['ls_port']
PUSH_INTERVAL = 2
except Exception, e: except Exception, e:
print 'Error loading config file: ', e logger.error('Error loading config file %s', e)
sys.exit() sys.exit()
class PypoPush: class PypoPush(Thread):
def __init__(self): def __init__(self, q):
Thread.__init__(self)
self.api_client = api_client.api_client_factory(config) self.api_client = api_client.api_client_factory(config)
self.cue_file = CueFile() self.cue_file = CueFile()
self.set_export_source('scheduler') self.set_export_source('scheduler')
self.queue = q
self.schedule = dict()
self.playlists = dict()
self.stream_metadata = dict()
""" """
push_ahead2 MUST be < push_ahead. The difference in these two values push_ahead2 MUST be < push_ahead. The difference in these two values
@ -42,51 +51,58 @@ class PypoPush:
def set_export_source(self, export_source): def set_export_source(self, export_source):
self.export_source = export_source self.export_source = export_source
self.cache_dir = config["cache_dir"] + self.export_source + '/' self.cache_dir = config["cache_dir"] + self.export_source + '/'
self.schedule_file = self.cache_dir + 'schedule.pickle'
self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle" self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle"
""" """
The Push Loop - the push loop periodically (minimal 1/2 of the playlist-grid) The Push Loop - the push loop periodically checks if there is a playlist
checks if there is a playlist that should be scheduled at the current time. that should be scheduled at the current time.
If yes, the temporary liquidsoap playlist gets replaced with the corresponding one, If yes, the current liquidsoap playlist gets replaced with the corresponding one,
then liquidsoap is asked (via telnet) to reload and immediately play it. then liquidsoap is asked (via telnet) to reload and immediately play it.
""" """
def push(self, export_source): def push(self, export_source):
logger = logging.getLogger() logger = logging.getLogger('push')
self.schedule = self.load_schedule() # get a new schedule from pypo-fetch
playedItems = self.load_schedule_tracker() if not self.queue.empty():
scheduled_data = self.queue.get()
logger.debug("Received data from pypo-fetch")
self.schedule = scheduled_data['schedule']
self.playlists = scheduled_data['playlists']
self.stream_metadata = scheduled_data['stream_metadata']
tcoming = time.localtime(time.time() + self.push_ahead) schedule = self.schedule
tcoming2 = time.localtime(time.time() + self.push_ahead2) playlists = self.playlists
str_tcoming_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming[0], tcoming[1], tcoming[2], tcoming[3], tcoming[4], tcoming[5])
str_tcoming2_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming2[0], tcoming2[1], tcoming2[2], tcoming2[3], tcoming2[4], tcoming2[5])
currently_on_air = False currently_on_air = False
if self.schedule == None: if schedule:
logger.warn('Unable to loop schedule - maybe write in progress?') playedItems = self.load_schedule_tracker()
logger.warn('Will try again in next loop.')
else: timenow = time.time()
for pkey in self.schedule: tcoming = time.localtime(timenow + self.push_ahead)
str_tcoming_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming[0], tcoming[1], tcoming[2], tcoming[3], tcoming[4], tcoming[5])
tcoming2 = time.localtime(timenow + self.push_ahead2)
str_tcoming2_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming2[0], tcoming2[1], tcoming2[2], tcoming2[3], tcoming2[4], tcoming2[5])
tnow = time.localtime(timenow)
str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
for pkey in schedule:
plstart = pkey[0:19] plstart = pkey[0:19]
start = self.schedule[pkey]['start'] start = schedule[pkey]['start']
end = self.schedule[pkey]['end'] end = schedule[pkey]['end']
playedFlag = (pkey in playedItems) and playedItems[pkey].get("played", 0) playedFlag = (pkey in playedItems) and playedItems[pkey].get("played", 0)
if plstart == str_tcoming_s or (plstart < str_tcoming_s and plstart > str_tcoming2_s and not playedFlag): if plstart == str_tcoming_s or (plstart < str_tcoming_s and plstart > str_tcoming2_s and not playedFlag):
logger.debug('Preparing to push playlist scheduled at: %s', pkey) logger.debug('Preparing to push playlist scheduled at: %s', pkey)
playlist = self.schedule[pkey] playlist = schedule[pkey]
ptype = playlist['subtype']
currently_on_air = True currently_on_air = True
# We have a match, replace the current playlist and # We have a match, replace the current playlist and
# force liquidsoap to refresh. # force liquidsoap to refresh.
if (self.push_liquidsoap(pkey, self.schedule, ptype) == 1): if (self.push_liquidsoap(pkey, schedule, playlists) == 1):
logger.debug("Pushed to liquidsoap, updating 'played' status.") logger.debug("Pushed to liquidsoap, updating 'played' status.")
# Marked the current playlist as 'played' in the schedule tracker # Marked the current playlist as 'played' in the schedule tracker
# so it is not called again in the next push loop. # so it is not called again in the next push loop.
@ -100,39 +116,28 @@ class PypoPush:
# Call API to update schedule states # Call API to update schedule states
logger.debug("Doing callback to server to update 'played' status.") logger.debug("Doing callback to server to update 'played' status.")
self.api_client.notify_scheduled_item_start_playing(pkey, self.schedule) self.api_client.notify_scheduled_item_start_playing(pkey, schedule)
if self.schedule != None: start = schedule[pkey]['start']
tnow = time.localtime(time.time()) end = schedule[pkey]['end']
str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
for pkey in self.schedule:
start = self.schedule[pkey]['start']
end = self.schedule[pkey]['end']
if start <= str_tnow_s and str_tnow_s < end: if start <= str_tnow_s and str_tnow_s < end:
currently_on_air = True currently_on_air = True
else:
pass
#logger.debug('Empty schedule')
if not currently_on_air: if not currently_on_air:
tn = telnetlib.Telnet(LS_HOST, LS_PORT) tn = telnetlib.Telnet(LS_HOST, LS_PORT)
tn.write('source.skip\n'.encode('latin-1')) tn.write('source.skip\n')
tn.write('exit\n') tn.write('exit\n')
tn.read_all() tn.read_all()
#logger.info('source.skip')
#logger.debug(tn.read_all())
def push_liquidsoap(self, pkey, schedule, ptype): def push_liquidsoap(self, pkey, schedule, playlists):
logger = logging.getLogger() logger = logging.getLogger('push')
src = self.cache_dir + str(pkey) + '/list.lsp'
try: try:
if True == os.access(src, os.R_OK): playlist = playlists[pkey]
logger.debug('OK - Can read playlist file')
pl_file = open(src, "r")
file_content = pl_file.read()
pl_file.close()
logger.debug('file content: %s' % (file_content))
playlist = json.loads(file_content)
#strptime returns struct_time in local time #strptime returns struct_time in local time
#mktime takes a time_struct and returns a floating point #mktime takes a time_struct and returns a floating point
@ -180,43 +185,24 @@ class PypoPush:
except Exception, e: except Exception, e:
logger.error('%s', e) logger.error('%s', e)
status = 0 status = 0
return status return status
def load_schedule(self):
logger = logging.getLogger()
schedule = None
# create the file if it doesnt exist
if (not os.path.exists(self.schedule_file)):
logger.debug('creating file ' + self.schedule_file)
open(self.schedule_file, 'w').close()
else:
# load the schedule from cache
#logger.debug('loading schedule file '+self.schedule_file)
try:
schedule_file = open(self.schedule_file, "r")
schedule = pickle.load(schedule_file)
schedule_file.close()
except Exception, e:
logger.error('%s', e)
return schedule
def load_schedule_tracker(self): def load_schedule_tracker(self):
logger = logging.getLogger() logger = logging.getLogger('push')
#logger.debug('load_schedule_tracker')
playedItems = dict() playedItems = dict()
# create the file if it doesnt exist # create the file if it doesnt exist
if (not os.path.exists(self.schedule_tracker_file)): if (not os.path.exists(self.schedule_tracker_file)):
logger.debug('creating file ' + self.schedule_tracker_file) try:
schedule_tracker = open(self.schedule_tracker_file, 'w') logger.debug('creating file ' + self.schedule_tracker_file)
pickle.dump(playedItems, schedule_tracker) schedule_tracker = open(self.schedule_tracker_file, 'w')
schedule_tracker.close() pickle.dump(playedItems, schedule_tracker)
schedule_tracker.close()
except Exception, e:
logger.error('Error creating schedule tracker file: %s', e)
else: else:
#logger.debug('schedule tracker file exists, opening: ' + self.schedule_tracker_file)
try: try:
schedule_tracker = open(self.schedule_tracker_file, "r") schedule_tracker = open(self.schedule_tracker_file, "r")
playedItems = pickle.load(schedule_tracker) playedItems = pickle.load(schedule_tracker)
@ -226,3 +212,18 @@ class PypoPush:
return playedItems return playedItems
def run(self):
loops = 0
heartbeat_period = math.floor(30/PUSH_INTERVAL)
logger = logging.getLogger('push')
while True:
if loops % heartbeat_period == 0:
logger.info("heartbeat")
loops = 0
try: self.push('scheduler')
except Exception, e:
logger.error('Pypo Push Error, exiting: %s', e)
sys.exit()
time.sleep(PUSH_INTERVAL)
loops += 1

Some files were not shown because too many files have changed in this diff Show More