diff --git a/CREDITS b/CREDITS index 05fff9198..bb5d3dad4 100644 --- a/CREDITS +++ b/CREDITS @@ -2,11 +2,30 @@ CREDITS ======= +Version 1.7.0 +------------- +Naomi Aro (naomi.aro@sourcefabric.org) + Role: Software Developer + +Martin Konecny (martin.konecny@sourcefabric.org) + Role: Software Developer + +Ofir Gal (ofir.gal@sourcefabric.org) + Role: QA + +Daniel James + Role: Documentor & QA + +Paul Baranowski (paul.baranowski@sourcefabric.org) + Role: Project Manager + + Version 1.6.1 ------------- Version 1.6.1 fixes a problem with playout being interrupted after 48 hours. It also fixes some security issues. + Version 1.6.0 ------------- This version marks a major change to the project, completely replacing the @@ -27,11 +46,14 @@ Naomi Aro (naomi.aro@sourcefabric.org) - Conversion to Propel DB backend Martin Konecny (martin.konecny@sourcefabric.org) - Role:Software Developer + Role: Software Developer Highlights: - New User Interface - Scheduler/Backend +Ofir Gal (ofir.gal@sourcefabric.org) + Role: QA + Daniel James Role: Documentor & QA diff --git a/INSTALL b/INSTALL index 1eea4e8fe..88f7a5807 100644 --- a/INSTALL +++ b/INSTALL @@ -1,23 +1,22 @@ --------------------------------------------------------------------------------- +Airtime is the open radio software for scheduling and remote station management. +Home page: http://airtime.sourcefabric.org/ - Copyright (c) 2010-2011 Sourcefabric O.P.S. +Installation instructions are here: + http://wiki.sourcefabric.org/x/BQBF - This file is part of the Airtime project. - http://airtime.sourcefabric.org/ +Here is the manual: + http://new.flossmanuals.net/airtime/index + +To report bugs, visit our bug tracker at: + http://dev.sourcefabric.org/browse/CC + +Visit our community support forum here: + http://forum.sourcefabric.org/index.php/f/14/ - To report bugs, visit our bug tracker at: - http://dev.sourcefabric.org/browse/CC +For commercial support, see: + http://sourcefabric.org/en/services/about/347/Support.htm + or send an e-mail to contact@sourcefabric.org - Visit our community support forum here: - http://forum.sourcefabric.org/index.php/f/14/ - - For commercial support, see http://sourcefabric.org/en/services/about/347/Support.htm - or send an e-mail to contact@sourcefabric.org +If you are a developer and want to hack on Airtime, go here: + http://wiki.sourcefabric.org/display/CC --------------------------------------------------------------------------------- - -Please see this page for install instructions: -http://wiki.sourcefabric.org/display/CC/Installing+Airtime+%28v1.6%29 - -If you are a developer, see this page: -http://wiki.sourcefabric.org/display/CC diff --git a/LICENSE_3RD_PARTY b/LICENSE_3RD_PARTY index 892af37a1..06dcf9016 100644 --- a/LICENSE_3RD_PARTY +++ b/LICENSE_3RD_PARTY @@ -29,8 +29,19 @@ Linked code: - License: LGPLv3 * Soundcloud php api wrapper - - https://github.com/mptre/php-soundcloud/blob/master/Services/Soundcloud.php + - Web site: https://github.com/mptre/php-soundcloud/blob/master/Services/Soundcloud.php - License: MIT + - Compatible with the GPL: Yes. See http://www.gnu.org/licenses/license-list.html + + * Kombu + - Web site: http://pypi.python.org/pypi/kombu/ + - License: New BSD + - Compatible with GPLv3? Yes. + + * PHP-AMQPLIB + - Web site: https://github.com/tnc/php-amqplib + - License: LGPLv2.1 + - Compatible with GPLv3? Yes ---------------- Non-linked code: diff --git a/README b/README index 111c0e95c..0747ce34a 100644 --- a/README +++ b/README @@ -4,6 +4,8 @@ Airtime is an open source application that provides remote automation of a radio station. +Home page: http://airtime.sourcefabric.org/ + Major features: * Web-based remote station management. Authorized personnel can add program material, create playlists, and schedule programming all via diff --git a/application/configs/classmap-airtime-conf.php b/application/configs/classmap-airtime-conf.php index a4f799421..e529ed6a2 100644 --- a/application/configs/classmap-airtime-conf.php +++ b/application/configs/classmap-airtime-conf.php @@ -8,13 +8,6 @@ return array ( 'BaseCcAccessPeer' => 'airtime/om/BaseCcAccessPeer.php', 'BaseCcAccess' => 'airtime/om/BaseCcAccess.php', 'BaseCcAccessQuery' => 'airtime/om/BaseCcAccessQuery.php', - 'CcBackupTableMap' => 'airtime/map/CcBackupTableMap.php', - 'CcBackupPeer' => 'airtime/CcBackupPeer.php', - 'CcBackup' => 'airtime/CcBackup.php', - 'CcBackupQuery' => 'airtime/CcBackupQuery.php', - 'BaseCcBackupPeer' => 'airtime/om/BaseCcBackupPeer.php', - 'BaseCcBackup' => 'airtime/om/BaseCcBackup.php', - 'BaseCcBackupQuery' => 'airtime/om/BaseCcBackupQuery.php', 'CcFilesTableMap' => 'airtime/map/CcFilesTableMap.php', 'CcFilesPeer' => 'airtime/CcFilesPeer.php', 'CcFiles' => 'airtime/CcFiles.php', @@ -113,11 +106,4 @@ return array ( 'BaseCcSubjsPeer' => 'airtime/om/BaseCcSubjsPeer.php', 'BaseCcSubjs' => 'airtime/om/BaseCcSubjs.php', 'BaseCcSubjsQuery' => 'airtime/om/BaseCcSubjsQuery.php', - 'CcTransTableMap' => 'airtime/map/CcTransTableMap.php', - 'CcTransPeer' => 'airtime/CcTransPeer.php', - 'CcTrans' => 'airtime/CcTrans.php', - 'CcTransQuery' => 'airtime/CcTransQuery.php', - 'BaseCcTransPeer' => 'airtime/om/BaseCcTransPeer.php', - 'BaseCcTrans' => 'airtime/om/BaseCcTrans.php', - 'BaseCcTransQuery' => 'airtime/om/BaseCcTransQuery.php', ); \ No newline at end of file diff --git a/application/configs/conf.php b/application/configs/conf.php index 74c3b10ac..a7cc48c6f 100644 --- a/application/configs/conf.php +++ b/application/configs/conf.php @@ -1,98 +1,56 @@ 'localhost', - 'storageUrlPort' => 80, - // Name of the web server user - 'webServerUser' => 'www-data', + 'webServerUser' => $values['general']['webServerUser'], - 'rabbitmq' => array("host" => "127.0.0.1", - "port" => "5672", - "user" => "guest", - "password" => "guest", - "vhost" => "/"), + 'rabbitmq' => $values['rabbitmq'], - // *********************************************************************** - // STOP CUSTOMIZING HERE - // - // You don't need to touch anything below this point. - // *********************************************************************** - - 'baseFilesDir' => $baseFilesDir, + 'baseFilesDir' => $values['general']['baseFilesDir'], // main directory for storing binary media files - 'storageDir' => "$baseFilesDir/stor", + 'storageDir' => $values['general']['baseFilesDir']."/stor", // Database config - 'dsn' => $values['database'], + 'dsn' => array( + 'username' => $values['database']['dbuser'], + 'password' => $values['database']['dbpass'], + 'hostspec' => $values['database']['host'], + 'phptype' => 'pgsql', + 'database' => $values['database']['dbname']), // prefix for table names in the database 'tblNamePrefix' => 'cc_', /* ================================================ storage configuration */ - 'apiKey' => $values['api_key'], + 'apiKey' => array($values['general']['api_key']), 'apiPath' => '/api/', 'soundcloud-client-id' => '2CLCxcSXYzx7QhhPVHN4A', 'soundcloud-client-secret' => 'pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs', + 'soundcloud-connection-retries' => $values['soundcloud']['connection_retries'], + 'soundcloud-connection-wait' => $values['soundcloud']['time_between_retries'], + "rootDir" => __DIR__."/../..", 'pearPath' => dirname(__FILE__).'/../../library/pear', 'zendPath' => dirname(__FILE__).'/../../library/Zend', 'phingPath' => dirname(__FILE__).'/../../library/phing', - // name of admin group - //'AdminsGr' => 'Admins', - - // name of station preferences group -// 'StationPrefsGr'=> 'StationPrefs', - - // name of 'all users' group - //'AllGr' => 'All', - - /* ==================================== application-specific configuration */ -// 'objtypes' => array( -// 'Storage' => array(/*'Folder',*/ 'File' /*, 'Replica'*/), -// 'File' => array(), -// 'audioclip' => array(), -// 'playlist' => array(), -// ), -// 'allowedActions'=> array( -// 'File' => array('editPrivs', 'write', 'read'), -// 'audioclip' => array('editPrivs', 'write', 'read'), -// 'playlist' => array('editPrivs', 'write', 'read'), -// ), -// 'allActions' => array( -// 'editPrivs', 'write', 'read', 'subjects' -// ), - - /* =================================================== cron configuration */ - 'cronUserName' => 'www-data', -# 'lockfile' => dirname(__FILE__).'/cron/cron.lock', - 'lockfile' => dirname(__FILE__).'/stor/buffer/cron.lock', - 'cronfile' => dirname(__FILE__).'/cron/croncall.php', - 'paramdir' => dirname(__FILE__).'/cron/params', -// 'systemPrefId' => "0", // ID for system prefs in prefs table ); // Add database table names @@ -119,11 +77,6 @@ $CC_CONFIG['permSequence'] = $CC_CONFIG['permTable'].'_id'; $CC_CONFIG['subjSequence'] = $CC_CONFIG['subjTable'].'_id'; $CC_CONFIG['smembSequence'] = $CC_CONFIG['smembTable'].'_id'; -// System users/groups - they cannot be deleted -//$CC_CONFIG['sysSubjs'] = array( -// 'root', /*$CC_CONFIG['AdminsGr'],*/ /*$CC_CONFIG['AllGr'],*/ $CC_CONFIG['StationPrefsGr'] -//); - // Add libs to the PHP path $old_include_path = get_include_path(); set_include_path('.'.PATH_SEPARATOR.$CC_CONFIG['pearPath'] @@ -131,15 +84,6 @@ set_include_path('.'.PATH_SEPARATOR.$CC_CONFIG['pearPath'] .PATH_SEPARATOR.$old_include_path); function load_airtime_config(){ - $ini_array = parse_ini_file(dirname(__FILE__).'/../../build/airtime.conf', true); - - return array( - 'database' => array( - 'username' => $ini_array['database']['dbuser'], - 'password' => $ini_array['database']['dbpass'], - 'hostspec' => $ini_array['database']['host'], - 'phptype' => 'pgsql', - 'database' => $ini_array['database']['dbname']), - 'api_key' => array($ini_array['general']['api_key']) - ); + $ini_array = parse_ini_file('/etc/airtime/airtime.conf', true); + return $ini_array; } diff --git a/application/controllers/ApiController.php b/application/controllers/ApiController.php index 7f1cf524e..00d1b8e9e 100644 --- a/application/controllers/ApiController.php +++ b/application/controllers/ApiController.php @@ -103,27 +103,55 @@ class ApiController extends Zend_Controller_Action exit; } - public function liveInfoAction(){ - global $CC_CONFIG; + public function liveInfoAction() + { + if (Application_Model_Preference::GetAllow3rdPartyApi()){ + // disable the view and the layout + $this->view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); - // disable the view and the layout - $this->view->layout()->disableLayout(); - $this->_helper->viewRenderer->setNoRender(true); + $result = Schedule::GetPlayOrderRange(0, 1); - $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).')'; + } else { + header('HTTP/1.0 401 Unauthorized'); + print 'You are not allowed to access this resource. '; + exit; + } + } - $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 weekInfoAction() + { + if (Application_Model_Preference::GetAllow3rdPartyApi()){ + // disable the view and the layout + $this->view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + + $dow = array("sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"); + + $result = array(); + for ($i=0; $i<7; $i++){ + $result[$dow[$i]] = Show_DAL::GetShowsByDayOfWeek($i); + } + + header("Content-type: text/javascript"); + echo $_GET['callback'].'('.json_encode($result).')'; + } else { + header('HTTP/1.0 401 Unauthorized'); + print 'You are not allowed to access this resource. '; + exit; + } } public function scheduleAction() @@ -254,11 +282,27 @@ class ApiController extends Zend_Controller_Action if(Application_Model_Preference::GetDoSoundCloudUpload()) { - $show = new Show($show_inst->getShowId()); - $description = $show->getDescription(); + for($i=0; $i<$CC_CONFIG['soundcloud-connection-retries']; $i++) { - $soundcloud = new ATSoundcloud(); - $soundcloud->uploadTrack($file->getRealFilePath(), $file->getName(), $description); + $show = new Show($show_inst->getShowId()); + $description = $show->getDescription(); + $hosts = $show->getHosts(); + + try { + $soundcloud = new ATSoundcloud(); + $soundcloud_id = $soundcloud->uploadTrack($file->getRealFilePath(), $file->getName(), $description, $hosts); + $show_inst->setSoundCloudFileId($soundcloud_id); + break; + } + catch (Services_Soundcloud_Invalid_Http_Response_Code_Exception $e) { + $code = $e->getHttpCode(); + if(!in_array($code, array(0, 100))) { + break; + } + } + + sleep($CC_CONFIG['soundcloud-connection-wait']); + } } $this->view->id = $file->getId(); diff --git a/application/controllers/PlaylistController.php b/application/controllers/PlaylistController.php index e32eb2e59..1540f0259 100644 --- a/application/controllers/PlaylistController.php +++ b/application/controllers/PlaylistController.php @@ -28,7 +28,7 @@ class PlaylistController extends Zend_Controller_Action private function getPlaylist() { $pl_sess = $this->pl_sess; - + if(isset($pl_sess->id)) { $pl = Playlist::Recall($pl_sess->id); @@ -43,7 +43,7 @@ class PlaylistController extends Zend_Controller_Action private function changePlaylist($pl_id) { $pl_sess = $this->pl_sess; - + if(isset($pl_sess->id)) { $pl = Playlist::Recall($pl_sess->id); @@ -51,13 +51,13 @@ class PlaylistController extends Zend_Controller_Action $this->closePlaylist($pl); } } - + $userInfo = Zend_Auth::getInstance()->getStorage()->read(); $pl = Playlist::Recall($pl_id); if($pl === FALSE) { return FALSE; - } + } $pl->lock($userInfo->id); $pl_sess->id = $pl_id; } @@ -78,7 +78,7 @@ class PlaylistController extends Zend_Controller_Action $this->view->headScript()->appendFile('/js/airtime/library/spl.js','text/javascript'); $this->view->headLink()->appendStylesheet('/css/playlist_builder.css'); - $this->_helper->viewRenderer->setResponseSegment('spl'); + $this->_helper->viewRenderer->setResponseSegment('spl'); $this->view->pl = $this->getPlaylist(); } @@ -88,10 +88,10 @@ class PlaylistController extends Zend_Controller_Action $userInfo = Zend_Auth::getInstance()->getStorage()->read(); $pl = new Playlist(); - $pl_id = $pl->create("Untitled Playlist"); + $pl->create("Untitled Playlist"); $pl->setPLMetaData('dc:creator', $userInfo->login); - $this->changePlaylist($pl_id); + $this->changePlaylist($pl->getId()); $form = new Application_Form_PlaylistMetadata(); $this->view->fieldset = $form; $this->view->form = $this->view->render('playlist/new.phtml'); @@ -105,25 +105,25 @@ class PlaylistController extends Zend_Controller_Action $pl_id = $this->_getParam('id', null); //not a new playlist if(!is_null($pl_id)) { - $this->changePlaylist($pl_id); - + $this->changePlaylist($pl_id); + $pl = $this->getPlaylist(); $title = $pl->getPLMetaData(UI_MDATA_KEY_TITLE); $desc = $pl->getPLMetaData(UI_MDATA_KEY_DESCRIPTION); - $data = array( 'title' => $title, 'description' => $desc); - $form->populate($data); + $data = array( 'title' => $title, 'description' => $desc); + $form->populate($data); } - + if ($request->isPost()) { $title = $this->_getParam('title', null); $description = $this->_getParam('description', null); $pl = $this->getPlaylist(); - + if($title) $pl->setName($title); - + if(isset($description)) { $pl->setPLMetaData(UI_MDATA_KEY_DESCRIPTION, $description); } @@ -132,7 +132,7 @@ class PlaylistController extends Zend_Controller_Action $this->view->html = $this->view->render('playlist/index.phtml'); unset($this->view->pl); } - + $this->view->fieldset = $form; $this->view->form = $this->view->render('playlist/new.phtml'); } @@ -140,13 +140,13 @@ class PlaylistController extends Zend_Controller_Action public function editAction() { $pl_id = $this->_getParam('id', null); - + if(!is_null($pl_id)) { - $this->changePlaylist($pl_id); + $this->changePlaylist($pl_id); } - + $pl = $this->getPlaylist(); - + $this->view->pl = $pl; $this->view->html = $this->view->render('playlist/index.phtml'); unset($this->view->pl); @@ -158,7 +158,7 @@ class PlaylistController extends Zend_Controller_Action $pos = $this->_getParam('pos', null); if (!is_null($id)) { - + $pl = $this->getPlaylist(); $res = $pl->addAudioClip($id, $pos); @@ -181,7 +181,7 @@ class PlaylistController extends Zend_Controller_Action { $oldPos = $this->_getParam('oldPos'); $newPos = $this->_getParam('newPos'); - + $pl = $this->getPlaylist(); $pl->moveAudioClip($oldPos, $newPos); @@ -197,7 +197,7 @@ class PlaylistController extends Zend_Controller_Action public function deleteItemAction() { $positions = $this->_getParam('pos', array()); - + if (!is_array($positions)) $positions = array($positions); @@ -208,7 +208,7 @@ class PlaylistController extends Zend_Controller_Action $pl = $this->getPlaylist(); foreach ($positions as $pos) { - $pl->delAudioClip($pos); + $pl->delAudioClip($pos); } $this->view->pl = $pl; @@ -274,7 +274,7 @@ class PlaylistController extends Zend_Controller_Action { $id = $this->_getParam('id', null); $pl = Playlist::Recall($id); - + if ($pl !== FALSE) { Playlist::Delete($id); @@ -291,7 +291,7 @@ class PlaylistController extends Zend_Controller_Action public function deleteActiveAction() { - $pl = $this->getPlaylist(); + $pl = $this->getPlaylist(); Playlist::Delete($pl->getId()); $pl_sess = $this->pl_sess; @@ -304,7 +304,7 @@ class PlaylistController extends Zend_Controller_Action { $pl = $this->getPlaylist(); $this->closePlaylist($pl); - + $this->view->html = $this->view->render('playlist/index.phtml'); } diff --git a/application/controllers/PreferenceController.php b/application/controllers/PreferenceController.php index 58bd6416d..46d82054f 100644 --- a/application/controllers/PreferenceController.php +++ b/application/controllers/PreferenceController.php @@ -30,11 +30,13 @@ class PreferenceController extends Zend_Controller_Action Application_Model_Preference::SetHeadTitle($values["stationName"], $this->view); Application_Model_Preference::SetDefaultFade($values["stationDefaultFade"]); Application_Model_Preference::SetStreamLabelFormat($values["streamFormat"]); + Application_Model_Preference::SetAllow3rdPartyApi($values["thirdPartyApi"]); Application_Model_Preference::SetDoSoundCloudUpload($values["UseSoundCloud"]); Application_Model_Preference::SetSoundCloudUser($values["SoundCloudUser"]); - Application_Model_Preference::SetSoundCloudPassword($values["SoundCloudPassword"]); + Application_Model_Preference::SetSoundCloudPassword($values["SoundCloudPassword"]); + Application_Model_Preference::SetSoundCloudTags($values["SoundCloudTags"]); - $this->view->statusMsg = "Preferences Updated."; + $this->view->statusMsg = "
Preferences updated.
"; } $this->view->form = $form; diff --git a/application/controllers/ScheduleController.php b/application/controllers/ScheduleController.php index acbc7bc60..31ce6d3f2 100644 --- a/application/controllers/ScheduleController.php +++ b/application/controllers/ScheduleController.php @@ -176,11 +176,11 @@ class ScheduleController extends Zend_Controller_Action if (strtotime($show->getShowStart()) <= strtotime($today_timestamp) && - strtotime($today_timestamp) < strtotime($show->getShowEnd())) { + strtotime($today_timestamp) < strtotime($show->getShowEnd()) && + $user->isAdmin()) { $menu[] = array('action' => array('type' => 'fn', - //'url' => '/Schedule/cancel-current-show'.$params, - 'callback' => "window['confirmCancelShow']($id)"), - 'title' => 'Cancel Current Show'); + 'callback' => "window['confirmCancelShow']($id)"), + 'title' => 'Cancel Current Show'); } if (strtotime($today_timestamp) < strtotime($show->getShowStart())) { @@ -319,6 +319,17 @@ class ScheduleController extends Zend_Controller_Action $showInstanceId = $this->_getParam('id'); $show = new ShowInstance($showInstanceId); + $originalShowId = $show->isRebroadcast(); + if (!is_null($originalShowId)){ + $originalShow = new ShowInstance($originalShowId); + $originalShowName = $originalShow->getName(); + $originalShowStart = $originalShow->getShowStart(); + + $timestamp = strtotime($originalShowStart); + $this->view->additionalShowInfo = + "Rebroadcast of show \"$originalShowName\" from " + .date("l, F jS", $timestamp)." at ".date("G:i", $timestamp); + } $this->view->showContent = $show->getShowListContent(); $this->view->dialog = $this->view->render('schedule/show-content-dialog.phtml'); unset($this->view->showContent); @@ -397,8 +408,8 @@ class ScheduleController extends Zend_Controller_Action $userInfo = Zend_Auth::getInstance()->getStorage()->read(); $user = new User($userInfo->id); - if($user->isAdmin()) { - Show::addShow($data); + if ($user->isAdmin()) { + Show::create($data); } //send back a new form for the user. diff --git a/application/forms/EditAudioMD.php b/application/forms/EditAudioMD.php index 0e700e19e..e50067063 100644 --- a/application/forms/EditAudioMD.php +++ b/application/forms/EditAudioMD.php @@ -89,7 +89,28 @@ class Application_Form_EditAudioMD extends Zend_Form 'ignore' => true, 'class' => 'ui-button ui-state-default', 'label' => 'Submit', + 'decorators' => array( + 'ViewHelper' + ) )); + + // Add the submit button + $this->addElement('button', 'cancel', array( + 'ignore' => true, + 'class' => 'ui-button ui-state-default', + 'label' => 'Cancel', + 'onclick' => 'javascript:document.location="/Library"', + 'decorators' => array( + 'ViewHelper' + ) + )); + + $this->addDisplayGroup(array('submit', 'cancel'), 'submitButtons', array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'dd', 'class' => 'element')), + ), + )); } diff --git a/application/forms/Preferences.php b/application/forms/Preferences.php index 23cda3d3f..a319476a2 100644 --- a/application/forms/Preferences.php +++ b/application/forms/Preferences.php @@ -42,6 +42,13 @@ class Application_Form_Preferences extends Zend_Form $stream_format->setValue(Application_Model_Preference::GetStreamLabelFormat()); $this->addElement($stream_format); + $third_party_api = new Zend_Form_Element_Radio('thirdPartyApi'); + $third_party_api->setLabel('Allow Remote Websites To Access Show Schedule Info'); + $third_party_api->setMultiOptions(array("Disabled", + "Enabled")); + $third_party_api->setValue(Application_Model_Preference::GetAllow3rdPartyApi()); + $this->addElement($third_party_api); + $this->addElement('checkbox', 'UseSoundCloud', array( 'label' => 'Automatically Upload Recorded Shows To SoundCloud', @@ -59,7 +66,7 @@ class Application_Form_Preferences extends Zend_Form )); //SoundCloud Password - $this->addElement('text', 'SoundCloudPassword', array( + $this->addElement('password', 'SoundCloudPassword', array( 'class' => 'input_text', 'label' => 'SoundCloud Password:', 'required' => false, @@ -67,6 +74,14 @@ class Application_Form_Preferences extends Zend_Form '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( 'class' => 'ui-button ui-state-default', 'ignore' => true, diff --git a/application/models/Playlist.php b/application/models/Playlist.php index 732a142e9..826977be6 100644 --- a/application/models/Playlist.php +++ b/application/models/Playlist.php @@ -102,27 +102,9 @@ class Playlist { return $res; } - public static function Insert($p_values) + + public static function Delete($id) { - // Create the StoredPlaylist object - $storedPlaylist = new Playlist(); - $storedPlaylist->name = isset($p_values['filename']) ? $p_values['filename'] : date("H:i:s"); - $storedPlaylist->mtime = new DateTime("now"); - - $pl = new CcPlaylist(); - $pl->setDbName($storedPlaylist->name); - $pl->setDbState("incomplete"); - $pl->setDbMtime($storedPlaylist->mtime); - $pl->save(); - - $storedPlaylist->id = $pl->getDbId(); - $storedPlaylist->setState('ready'); - - return $storedPlaylist->id; - - } - - public static function Delete($id) { $pl = CcPlaylistQuery::create()->findPK($id); if($pl === NULL) return FALSE; @@ -207,7 +189,7 @@ class Playlist { return $this->name; } $pl = CcPlaylistQuery::create()->findPK($id); - if($pl === NULL) + if ($pl === NULL) return FALSE; return $pl->getDbName(); @@ -395,18 +377,27 @@ class Playlist { } /** - * Create instance of Playlist object and insert empty file + * Create instance of a Playlist object. * - * @param string $fname - * name of new file - * @return instance of Playlist object + * @param string $p_fname + * Name of the playlist + * @return Playlist */ - public function create($fname=NULL) + public function create($p_fname=NULL) { - $values = array("filename" => $fname); - $pl_id = Playlist::Insert($values); - $this->id = $pl_id; - return $this->id; + $this->name = !empty($p_fname) ? $p_fname : date("H:i:s"); + $this->mtime = new DateTime("now"); + + $pl = new CcPlaylist(); + $pl->setDbName($this->name); + $pl->setDbState("incomplete"); + $pl->setDbMtime($this->mtime); + $pl->save(); + + $this->id = $pl->getDbId(); + $this->setState('ready'); + + return $this; } /** diff --git a/application/models/Preference.php b/application/models/Preference.php index 8662f4424..b234a36b9 100644 --- a/application/models/Preference.php +++ b/application/models/Preference.php @@ -117,12 +117,34 @@ class Application_Model_Preference } public static function SetSoundCloudPassword($password) { - Application_Model_Preference::SetValue("soundcloud_password", $password); + if (strlen($password) > 0) + 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"); + } + + public static function SetAllow3rdPartyApi($bool) { + Application_Model_Preference::SetValue("third_party_api", $bool); + } + + public static function GetAllow3rdPartyApi() { + $val = Application_Model_Preference::GetValue("third_party_api"); + if (strlen($val) == 0){ + return "0"; + } else { + return $val; + } + } + } diff --git a/application/models/Schedule.php b/application/models/Schedule.php index d1d556e1f..07a23b81c 100644 --- a/application/models/Schedule.php +++ b/application/models/Schedule.php @@ -27,6 +27,8 @@ class ScheduleGroup { /** * Add a music clip or playlist to the schedule. * + * @param int $p_showInstance + * ID of the show. * @param $p_datetime * In the format YYYY-MM-DD HH:MM:SS.mmmmmm * @param $p_audioFileId @@ -40,7 +42,7 @@ class ScheduleGroup { * Return PEAR_Error if the item could not be added. * Error code 555 is a scheduling conflict. */ - public function add($show_instance, $p_datetime, $p_audioFileId = null, $p_playlistId = null, $p_options = null) { + public function add($p_showInstance, $p_datetime, $p_audioFileId = null, $p_playlistId = null, $p_options = null) { global $CC_CONFIG, $CC_DBC; if (!is_null($p_audioFileId)) { @@ -64,7 +66,7 @@ class ScheduleGroup { $sql = "INSERT INTO ".$CC_CONFIG["scheduleTable"] ." (instance_id, starts, ends, clip_length, group_id, file_id, cue_out)" - ." VALUES ($show_instance, TIMESTAMP '$p_datetime', " + ." VALUES ($p_showInstance, TIMESTAMP '$p_datetime', " ." (TIMESTAMP '$p_datetime' + INTERVAL '$length')," ." '$length'," ." {$this->groupId}, $p_audioFileId, '$length')"; @@ -103,7 +105,7 @@ class ScheduleGroup { $sql = "INSERT INTO ".$CC_CONFIG["scheduleTable"] ." (instance_id, playlist_id, starts, ends, group_id, file_id," ." clip_length, cue_in, cue_out, fade_in, fade_out)" - ." VALUES ($show_instance, $p_playlistId, TIMESTAMP '$itemStartTime', " + ." VALUES ($p_showInstance, $p_playlistId, TIMESTAMP '$itemStartTime', " ." (TIMESTAMP '$itemStartTime' + INTERVAL '$trackLength')," ." '{$this->groupId}', '{$row['file_id']}', '$trackLength', '{$row['cuein']}'," ." '{$row['cueout']}', '{$row['fadein']}','{$row['fadeout']}')"; @@ -466,6 +468,30 @@ class Schedule { { 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" + ." FROM $CC_CONFIG[scheduleTable] st" + ." LEFT JOIN $CC_CONFIG[filesTable] ft" + ." ON st.file_id = ft.id" + ." LEFT JOIN $CC_CONFIG[playListTable] pt" + ." ON st.playlist_id = pt.id" + ." LEFT JOIN $CC_CONFIG[showInstances] si" + ." ON st.instance_id = si.id" + ." LEFT JOIN $CC_CONFIG[showTable] show" + ." ON si.show_id = show.id" + ." WHERE st.starts < si.ends"; + +/* $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" ." FROM $CC_CONFIG[scheduleTable] st, $CC_CONFIG[filesTable] ft, $CC_CONFIG[playListTable] pt, $CC_CONFIG[showInstances] si, $CC_CONFIG[showTable] show" ." WHERE st.playlist_id = pt.id" @@ -473,6 +499,7 @@ class Schedule { ." AND st.instance_id = si.id" ." AND si.show_id = show.id" ." AND st.starts < si.ends"; +*/ if ($timePeriod < 0){ $sql .= " AND st.ends < TIMESTAMP '$timeStamp'" @@ -628,17 +655,24 @@ class Schedule { * @param string $p_toDateTime * In the format "YYYY-MM-DD-HH-mm-SS" */ - public static function GetScheduledPlaylists() + public static function GetScheduledPlaylists($p_fromDateTime = null, $p_toDateTime = null) { global $CC_CONFIG, $CC_DBC; - $t1 = new DateTime(); - $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"); - + if (is_null($p_fromDateTime)) { + $t1 = new DateTime(); + $range_start = $t1->format("Y-m-d H:i:s"); + } else { + $range_start = Schedule::PypoTimeToAirtimeTime($p_fromDateTime); + } + if (is_null($p_fromDateTime)) { + $t2 = new DateTime(); + $t2->add(new DateInterval("PT24H")); + $range_end = $t2->format("Y-m-d H:i:s"); + } else { + $range_end = Schedule::PypoTimeToAirtimeTime($p_toDateTime); + } + // Scheduler wants everything in a playlist $data = Schedule::GetItems($range_start, $range_end, true); $playlists = array(); diff --git a/application/models/Shows.php b/application/models/Shows.php index 8aa83beae..492885f40 100644 --- a/application/models/Shows.php +++ b/application/models/Shows.php @@ -58,6 +58,29 @@ class Show { $show->setDbBackgroundColor($backgroundColor); } + public function getId() + { + return $this->_showId; + } + + public function getHosts() + { + global $CC_DBC; + + $sql = "SELECT first_name, last_name + FROM cc_show_hosts LEFT JOIN cc_subjs ON cc_show_hosts.subjs_id = cc_subjs.id + WHERE show_id = {$this->_showId}"; + + $hosts = $CC_DBC->GetAll($sql); + + $res = array(); + foreach($hosts as $host) { + $res[] = $host['first_name']." ".$host['last_name']; + } + + return $res; + } + public function cancelShow($day_timestamp) { global $CC_DBC; @@ -75,8 +98,17 @@ class Show { RabbitMq::PushSchedule(); } - //end dates are non inclusive. - public static function addShow($data) + + /** + * Create a show. + * + * Note: end dates are non inclusive. + * + * @param array $data + * @return int + * Show ID + */ + public static function create($data) { $con = Propel::getConnection(CcShowPeer::DATABASE_NAME); @@ -88,11 +120,11 @@ class Show { $r = $con->query($sql); $startDow = $r->fetchColumn(0); - if($data['add_show_no_end']) { + if ($data['add_show_no_end']) { $endDate = NULL; $data['add_show_repeats'] = 1; } - else if($data['add_show_repeats']) { + else if ($data['add_show_repeats']) { $sql = "SELECT date '{$data['add_show_end_date']}' + INTERVAL '1 day' "; $r = $con->query($sql); $endDate = $r->fetchColumn(0); @@ -104,15 +136,15 @@ class Show { } //only want the day of the week from the start date. - if(!$data['add_show_repeats']) { + if (!$data['add_show_repeats']) { $data['add_show_day_check'] = array($startDow); } - 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); } //find repeat type or set to a non repeating show. - if($data['add_show_repeats']) { + if ($data['add_show_repeats']) { $repeat_type = $data["add_show_repeat_type"]; } else { @@ -129,7 +161,7 @@ class Show { $showId = $show->getDbId(); - if($data['add_show_record']){ + if ($data['add_show_record']){ $isRecorded = 1; } else { @@ -137,7 +169,7 @@ class Show { } //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->setDbFirstShow($data['add_show_start_date']); $showDay->setDbLastShow($endDate); @@ -150,7 +182,7 @@ class Show { } else { foreach ($data['add_show_day_check'] as $day) { - if($startDow !== $day){ + if ($startDow !== $day){ if ($startDow > $day) $daysAdd = 6 - $startDow + 1 + $day; @@ -165,7 +197,7 @@ class Show { $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->setDbFirstShow($start); $showDay->setDbLastShow($endDate); @@ -181,11 +213,11 @@ class Show { } //adding rows to cc_show_rebroadcast - if($data['add_show_record'] && $data['add_show_rebroadcast'] && $repeat_type != -1) { + 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_date_'.$i]) { + if ($data['add_show_rebroadcast_date_'.$i]) { $showRebroad = new CcShowRebroadcast(); $showRebroad->setDbDayOffset($data['add_show_rebroadcast_date_'.$i]); $showRebroad->setDbStartTime($data['add_show_rebroadcast_time_'.$i]); @@ -194,11 +226,11 @@ 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']}' "; $r = $con->query($sql); $offset_days = $r->fetchColumn(0); @@ -212,7 +244,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. foreach ($data['add_show_hosts'] as $host) { $showHost = new CcShowHosts(); @@ -222,10 +254,19 @@ class Show { } } - Show::populateShowUntilLastGeneratedDate($showId); + Show::populateShowUntil($showId); RabbitMq::PushSchedule(); + return $showId; } + /** + * @param string $start_timestamp + * In the format "YYYY-MM-DD HH:mm:ss" + * @param string $end_timestamp + * In the format "YYYY-MM-DD HH:mm:ss" + * @param unknown_type $excludeInstance + * @param boolean $onlyRecord + */ public static function getShows($start_timestamp, $end_timestamp, $excludeInstance=NULL, $onlyRecord=FALSE) { global $CC_DBC; @@ -283,7 +324,7 @@ class Show { $next_date = $first_show." ".$start_time; - if(strtotime($next_date) < strtotime($end_timestamp)) { + if (strtotime($next_date) < strtotime($end_timestamp)) { $start = $next_date; @@ -327,7 +368,8 @@ class Show { //for a show with repeat_type == 0,1,2 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; if(isset($next_pop_date)) { @@ -404,53 +446,86 @@ class Show { } } - //used to catch up a newly added show - private static function populateShowUntilLastGeneratedDate($show_id) { - global $CC_DBC; - $showsPopUntil = Application_Model_Preference::GetShowsPopulatedUntil(); - $sql = "SELECT * FROM cc_show_days WHERE show_id = {$show_id}"; + /** + * Generate repeating show instances for a single show up to the given date. + * If no date is given, use the one in the user's preferences, which is stored + * automatically by FullCalendar as the furthest date in the future the user + * has looked at. + * + * @param int $p_showId + * @param string $p_date + * In the format "YYYY-MM-DD HH:mm:ss" + */ + public static function populateShowUntil($p_showId, $p_date = NULL) + { + global $CC_DBC; + if (is_null($p_date)) { + $p_date = Application_Model_Preference::GetShowsPopulatedUntil(); + + if ($p_date == "") { + $today_timestamp = date("Y-m-d"); + Application_Model_Preference::SetShowsPopulatedUntil($today_timestamp); + } + } + + $sql = "SELECT * FROM cc_show_days WHERE show_id = {$p_showId}"; $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"], - $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"], $p_date); } } - public static function populateShowsUntil($pop_timestamp, $end_timestamp) { + /** + * Generate all the repeating shows in the given range. + * + * @param string $p_startTimestamp + * In the format "YYYY-MM-DD HH:mm:ss" + * @param string $p_endTimestamp + * In the format "YYYY-MM-DD HH:mm:ss" + */ + public static function populateAllShowsInRange($p_startTimestamp, $p_endTimestamp) + { global $CC_DBC; - if($pop_timestamp != "") { + if ($p_startTimestamp != "") { $sql = "SELECT * FROM cc_show_days - WHERE last_show IS NULL - OR first_show < '{$end_timestamp}' AND last_show > '{$pop_timestamp}'"; + WHERE last_show IS NULL + OR first_show < '{$p_endTimestamp}' AND last_show > '{$p_startTimestamp}'"; } else { $today_timestamp = date("Y-m-d"); - $sql = "SELECT * FROM cc_show_days - WHERE last_show IS NULL - OR first_show < '{$end_timestamp}' AND last_show > '{$today_timestamp}'"; + WHERE last_show IS NULL + OR first_show < '{$p_endTimestamp}' AND last_show > '{$today_timestamp}'"; } $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"], - $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"], $p_endTimestamp); } } - public static function getFullCalendarEvents($start, $end, $editable=false) { - + /** + * + * @param string $start + * In the format "YYYY-MM-DD HH:mm:ss" + * @param string $end + * In the format "YYYY-MM-DD HH:mm:ss" + * @param boolean $editable + */ + public static function getFullCalendarEvents($start, $end, $editable=false) + { $events = array(); $showsPopUntil = Application_Model_Preference::GetShowsPopulatedUntil(); //if fullcalendar is requesting shows past our previous populated until date, generate shows up until this point. - if($showsPopUntil == "" || strtotime($showsPopUntil) < strtotime($end)) { - - Show::populateShowsUntil($showsPopUntil, $end); + if ($showsPopUntil == "" || strtotime($showsPopUntil) < strtotime($end)) { + Show::populateAllShowsInRange($showsPopUntil, $end); Application_Model_Preference::SetShowsPopulatedUntil($end); } @@ -458,7 +533,7 @@ class Show { $today_timestamp = date("Y-m-d H:i:s"); foreach ($shows as $show) { - if($editable && strtotime($today_timestamp) < strtotime($show["starts"])) + if ($editable && strtotime($today_timestamp) < strtotime($show["starts"])) $events[] = Show::makeFullCalendarEvent($show, array("editable" => true)); else $events[] = Show::makeFullCalendarEvent($show); @@ -549,6 +624,19 @@ class ShowInstance { return $showInstance->getDbEnds(); } + public function setSoundCloudFileId($p_soundcloud_id) + { + $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); + $showInstance->setDbSoundCloudId($p_soundcloud_id) + ->save(); + } + + public function getSoundCloudFileId() + { + $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); + return $showInstance->getDbSoundCloudId(); + } + public function setShowStart($start) { $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); @@ -665,6 +753,10 @@ class ShowInstance { RabbitMq::PushSchedule(); } + /** + * Get the group ID for this show. + * + */ private function getLastGroupId() { global $CC_DBC; @@ -673,13 +765,18 @@ class ShowInstance { return $res; } + /** + * Add a playlist as the last item of the current show. + * + * @param int $plId + * Playlist ID. + */ public function addPlaylistToShow($plId) { $sched = new ScheduleGroup(); $lastGroupId = $this->getLastGroupId(); - if(is_null($lastGroupId)) { - + if (is_null($lastGroupId)) { $groupId = $sched->add($this->_instanceId, $this->getShowStart(), null, $plId); } else { @@ -688,14 +785,18 @@ class ShowInstance { RabbitMq::PushSchedule(); } - public function addFileToShow($file_id) + /** + * Add a media file as the last item in the show. + * + * @param int $file_id + */ + public function addFileToShow($file_id) { $sched = new ScheduleGroup(); $lastGroupId = $this->getLastGroupId(); - - if(is_null($lastGroupId)) { - $groupId = $sched->add($this->_instanceId, $this->getShowStart(), $file_id); + if (is_null($lastGroupId)) { + $groupId = $sched->add($this->_instanceId, $this->getShowStart(), $file_id); } else { $groupId = $sched->addFileAfter($this->_instanceId, $lastGroupId, $file_id); @@ -703,12 +804,18 @@ class ShowInstance { RabbitMq::PushSchedule(); } - public function scheduleShow($plIds) { - - foreach($plIds as $plId) { - $this->addPlaylistToShow($plId); - } - } + /** + * Add the given playlists to the show. + * + * @param array $plIds + * An array of playlist IDs. + */ + public function scheduleShow($plIds) + { + foreach ($plIds as $plId) { + $this->addPlaylistToShow($plId); + } + } public function removeGroupFromShow($group_id) { @@ -871,10 +978,6 @@ class Show_DAL { { global $CC_CONFIG, $CC_DBC; - $timestamp = explode(" ", $timeNow); - $date = $timestamp[0]; - $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" ." FROM $CC_CONFIG[showInstances] si, $CC_CONFIG[showTable] s" ." WHERE si.show_id = s.id" @@ -930,7 +1033,31 @@ class Show_DAL { ." OR (si.starts < TIMESTAMP '$timeNow' + INTERVAL '$end seconds' AND si.ends > TIMESTAMP '$timeNow' + INTERVAL '$end seconds'))" //checking for st.starts IS NULL so that the query also returns shows that do not have any items scheduled. ." AND (st.starts < si.ends OR st.starts IS NULL)" - ." ORDER BY st.starts"; + ." ORDER BY si.starts, st.starts"; + + return $CC_DBC->GetAll($sql); + } + + public static function GetShowsByDayOfWeek($day){ + //DOW FROM TIMESTAMP + //The day of the week (0 - 6; Sunday is 0) (for timestamp values only) + + //SELECT EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40'); + //Result: 5 + + global $CC_CONFIG, $CC_DBC; + $sql = "SELECT" + ." si.starts as show_starts," + ." si.ends as show_ends," + ." s.name as show_name," + ." s.url as url" + ." FROM $CC_CONFIG[showInstances] si" + ." LEFT JOIN $CC_CONFIG[showTable] s" + ." ON si.show_id = s.id" + ." WHERE EXTRACT(DOW FROM si.starts) = $day" + ." AND EXTRACT(WEEK FROM si.starts) = EXTRACT(WEEK FROM localtimestamp)"; + + //echo $sql; return $CC_DBC->GetAll($sql); } diff --git a/application/models/Soundcloud.php b/application/models/Soundcloud.php index 352bd8ea7..6f5ca58f2 100644 --- a/application/models/Soundcloud.php +++ b/application/models/Soundcloud.php @@ -32,26 +32,31 @@ class ATSoundcloud { { if($this->getToken()) { - $tags = join(" ", $tags); + 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 + 'track[description]' => $description, + 'track[downloadable]' => true, + ); - try { - $response = json_decode( - $this->_soundcloud->post('tracks', $track_data), - true - ); - } - catch (Services_Soundcloud_Invalid_Http_Response_Code_Exception $e) { - echo $e->getMessage(); - } - } + $response = json_decode( + $this->_soundcloud->post('tracks', $track_data), + true + ); + + return $response["id"]; + } } } diff --git a/application/models/StoredFile.php b/application/models/StoredFile.php index ecff91caa..957d552b0 100644 --- a/application/models/StoredFile.php +++ b/application/models/StoredFile.php @@ -1481,9 +1481,7 @@ class StoredFile { public function getFileUrl() { global $CC_CONFIG; - return "http://".$CC_CONFIG["storageUrlHost"] - .$CC_CONFIG["apiPath"]."get-media/file/" - .$this->gunid.".".$this->getFileExtension(); + return $this->gunid.".".$this->getFileExtension(); } /** diff --git a/application/models/airtime/map/CcShowDaysTableMap.php b/application/models/airtime/map/CcShowDaysTableMap.php index 333934c44..2e932e7eb 100644 --- a/application/models/airtime/map/CcShowDaysTableMap.php +++ b/application/models/airtime/map/CcShowDaysTableMap.php @@ -47,7 +47,7 @@ class CcShowDaysTableMap extends TableMap { $this->addColumn('REPEAT_TYPE', 'DbRepeatType', 'TINYINT', true, null, null); $this->addColumn('NEXT_POP_DATE', 'DbNextPopDate', 'DATE', false, null, null); $this->addForeignKey('SHOW_ID', 'DbShowId', 'INTEGER', 'cc_show', 'ID', true, null, null); - $this->addColumn('RECORD', 'DbRecord', 'TINYINT', false, null, null); + $this->addColumn('RECORD', 'DbRecord', 'TINYINT', false, null, 0); // validators } // initialize() diff --git a/application/models/airtime/map/CcShowInstancesTableMap.php b/application/models/airtime/map/CcShowInstancesTableMap.php index b826c19ce..c69459bc0 100644 --- a/application/models/airtime/map/CcShowInstancesTableMap.php +++ b/application/models/airtime/map/CcShowInstancesTableMap.php @@ -46,6 +46,7 @@ class CcShowInstancesTableMap extends TableMap { $this->addColumn('REBROADCAST', 'DbRebroadcast', 'TINYINT', false, null, 0); $this->addForeignKey('INSTANCE_ID', 'DbOriginalShow', 'INTEGER', 'cc_show_instances', 'ID', false, null, null); $this->addForeignKey('FILE_ID', 'DbRecordedFile', 'INTEGER', 'cc_files', 'ID', false, null, null); + $this->addColumn('SOUNDCLOUD_ID', 'DbSoundCloudId', 'INTEGER', false, null, null); // validators } // initialize() diff --git a/application/models/airtime/om/BaseCcShowDays.php b/application/models/airtime/om/BaseCcShowDays.php index 248abdb94..3a2d48f37 100644 --- a/application/models/airtime/om/BaseCcShowDays.php +++ b/application/models/airtime/om/BaseCcShowDays.php @@ -80,6 +80,7 @@ abstract class BaseCcShowDays extends BaseObject implements Persistent /** * The value for the record field. + * Note: this column has a database default value of: 0 * @var int */ protected $record; @@ -103,6 +104,27 @@ abstract class BaseCcShowDays extends BaseObject implements Persistent */ protected $alreadyInValidation = false; + /** + * Applies default values to this object. + * This method should be called from the object's constructor (or + * equivalent initialization method). + * @see __construct() + */ + public function applyDefaultValues() + { + $this->record = 0; + } + + /** + * Initializes internal state of BaseCcShowDays object. + * @see applyDefaults() + */ + public function __construct() + { + parent::__construct(); + $this->applyDefaultValues(); + } + /** * Get the [id] column value. * @@ -607,7 +629,7 @@ abstract class BaseCcShowDays extends BaseObject implements Persistent $v = (int) $v; } - if ($this->record !== $v) { + if ($this->record !== $v || $this->isNew()) { $this->record = $v; $this->modifiedColumns[] = CcShowDaysPeer::RECORD; } @@ -625,6 +647,10 @@ abstract class BaseCcShowDays extends BaseObject implements Persistent */ public function hasOnlyDefaultValues() { + if ($this->record !== 0) { + return false; + } + // otherwise, everything was equal, so return TRUE return true; } // hasOnlyDefaultValues() @@ -1357,6 +1383,7 @@ abstract class BaseCcShowDays extends BaseObject implements Persistent $this->alreadyInSave = false; $this->alreadyInValidation = false; $this->clearAllReferences(); + $this->applyDefaultValues(); $this->resetModified(); $this->setNew(true); $this->setDeleted(false); diff --git a/application/models/airtime/om/BaseCcShowInstances.php b/application/models/airtime/om/BaseCcShowInstances.php index 9a0578dd4..6d608ce19 100644 --- a/application/models/airtime/om/BaseCcShowInstances.php +++ b/application/models/airtime/om/BaseCcShowInstances.php @@ -74,6 +74,12 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent */ protected $file_id; + /** + * The value for the soundcloud_id field. + * @var int + */ + protected $soundcloud_id; + /** * @var CcShow */ @@ -261,6 +267,16 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent return $this->file_id; } + /** + * Get the [soundcloud_id] column value. + * + * @return int + */ + public function getDbSoundCloudId() + { + return $this->soundcloud_id; + } + /** * Set the value of [id] column. * @@ -491,6 +507,26 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent return $this; } // setDbRecordedFile() + /** + * Set the value of [soundcloud_id] column. + * + * @param int $v new value + * @return CcShowInstances The current object (for fluent API support) + */ + public function setDbSoundCloudId($v) + { + if ($v !== null) { + $v = (int) $v; + } + + if ($this->soundcloud_id !== $v) { + $this->soundcloud_id = $v; + $this->modifiedColumns[] = CcShowInstancesPeer::SOUNDCLOUD_ID; + } + + return $this; + } // setDbSoundCloudId() + /** * Indicates whether the columns in this object are only set to default values. * @@ -539,6 +575,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $this->rebroadcast = ($row[$startcol + 5] !== null) ? (int) $row[$startcol + 5] : null; $this->instance_id = ($row[$startcol + 6] !== null) ? (int) $row[$startcol + 6] : null; $this->file_id = ($row[$startcol + 7] !== null) ? (int) $row[$startcol + 7] : null; + $this->soundcloud_id = ($row[$startcol + 8] !== null) ? (int) $row[$startcol + 8] : null; $this->resetModified(); $this->setNew(false); @@ -547,7 +584,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $this->ensureConsistency(); } - return $startcol + 8; // 8 = CcShowInstancesPeer::NUM_COLUMNS - CcShowInstancesPeer::NUM_LAZY_LOAD_COLUMNS). + return $startcol + 9; // 9 = CcShowInstancesPeer::NUM_COLUMNS - CcShowInstancesPeer::NUM_LAZY_LOAD_COLUMNS). } catch (Exception $e) { throw new PropelException("Error populating CcShowInstances object", $e); @@ -968,6 +1005,9 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent case 7: return $this->getDbRecordedFile(); break; + case 8: + return $this->getDbSoundCloudId(); + break; default: return null; break; @@ -1000,6 +1040,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $keys[5] => $this->getDbRebroadcast(), $keys[6] => $this->getDbOriginalShow(), $keys[7] => $this->getDbRecordedFile(), + $keys[8] => $this->getDbSoundCloudId(), ); if ($includeForeignObjects) { if (null !== $this->aCcShow) { @@ -1066,6 +1107,9 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent case 7: $this->setDbRecordedFile($value); break; + case 8: + $this->setDbSoundCloudId($value); + break; } // switch() } @@ -1098,6 +1142,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent if (array_key_exists($keys[5], $arr)) $this->setDbRebroadcast($arr[$keys[5]]); if (array_key_exists($keys[6], $arr)) $this->setDbOriginalShow($arr[$keys[6]]); if (array_key_exists($keys[7], $arr)) $this->setDbRecordedFile($arr[$keys[7]]); + if (array_key_exists($keys[8], $arr)) $this->setDbSoundCloudId($arr[$keys[8]]); } /** @@ -1117,6 +1162,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent if ($this->isColumnModified(CcShowInstancesPeer::REBROADCAST)) $criteria->add(CcShowInstancesPeer::REBROADCAST, $this->rebroadcast); if ($this->isColumnModified(CcShowInstancesPeer::INSTANCE_ID)) $criteria->add(CcShowInstancesPeer::INSTANCE_ID, $this->instance_id); if ($this->isColumnModified(CcShowInstancesPeer::FILE_ID)) $criteria->add(CcShowInstancesPeer::FILE_ID, $this->file_id); + if ($this->isColumnModified(CcShowInstancesPeer::SOUNDCLOUD_ID)) $criteria->add(CcShowInstancesPeer::SOUNDCLOUD_ID, $this->soundcloud_id); return $criteria; } @@ -1185,6 +1231,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $copyObj->setDbRebroadcast($this->rebroadcast); $copyObj->setDbOriginalShow($this->instance_id); $copyObj->setDbRecordedFile($this->file_id); + $copyObj->setDbSoundCloudId($this->soundcloud_id); if ($deepCopy) { // important: temporarily setNew(false) because this affects the behavior of @@ -1676,6 +1723,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $this->rebroadcast = null; $this->instance_id = null; $this->file_id = null; + $this->soundcloud_id = null; $this->alreadyInSave = false; $this->alreadyInValidation = false; $this->clearAllReferences(); diff --git a/application/models/airtime/om/BaseCcShowInstancesPeer.php b/application/models/airtime/om/BaseCcShowInstancesPeer.php index dcabfbd67..78a7e8b83 100644 --- a/application/models/airtime/om/BaseCcShowInstancesPeer.php +++ b/application/models/airtime/om/BaseCcShowInstancesPeer.php @@ -26,7 +26,7 @@ abstract class BaseCcShowInstancesPeer { const TM_CLASS = 'CcShowInstancesTableMap'; /** The total number of columns. */ - const NUM_COLUMNS = 8; + const NUM_COLUMNS = 9; /** The number of lazy-loaded columns. */ const NUM_LAZY_LOAD_COLUMNS = 0; @@ -55,6 +55,9 @@ abstract class BaseCcShowInstancesPeer { /** the column name for the FILE_ID field */ const FILE_ID = 'cc_show_instances.FILE_ID'; + /** the column name for the SOUNDCLOUD_ID field */ + const SOUNDCLOUD_ID = 'cc_show_instances.SOUNDCLOUD_ID'; + /** * An identiy map to hold any loaded instances of CcShowInstances objects. * This must be public so that other peer classes can access this when hydrating from JOIN @@ -71,12 +74,12 @@ abstract class BaseCcShowInstancesPeer { * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' */ private static $fieldNames = array ( - BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbShowId', 'DbRecord', 'DbRebroadcast', 'DbOriginalShow', 'DbRecordedFile', ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbShowId', 'dbRecord', 'dbRebroadcast', 'dbOriginalShow', 'dbRecordedFile', ), - BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::SHOW_ID, self::RECORD, self::REBROADCAST, self::INSTANCE_ID, self::FILE_ID, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'SHOW_ID', 'RECORD', 'REBROADCAST', 'INSTANCE_ID', 'FILE_ID', ), - BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'show_id', 'record', 'rebroadcast', 'instance_id', 'file_id', ), - BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, ) + BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbShowId', 'DbRecord', 'DbRebroadcast', 'DbOriginalShow', 'DbRecordedFile', 'DbSoundCloudId', ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbShowId', 'dbRecord', 'dbRebroadcast', 'dbOriginalShow', 'dbRecordedFile', 'dbSoundCloudId', ), + BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::SHOW_ID, self::RECORD, self::REBROADCAST, self::INSTANCE_ID, self::FILE_ID, self::SOUNDCLOUD_ID, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'SHOW_ID', 'RECORD', 'REBROADCAST', 'INSTANCE_ID', 'FILE_ID', 'SOUNDCLOUD_ID', ), + BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'show_id', 'record', 'rebroadcast', 'instance_id', 'file_id', 'soundcloud_id', ), + BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, ) ); /** @@ -86,12 +89,12 @@ abstract class BaseCcShowInstancesPeer { * e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0 */ private static $fieldKeys = array ( - BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbShowId' => 3, 'DbRecord' => 4, 'DbRebroadcast' => 5, 'DbOriginalShow' => 6, 'DbRecordedFile' => 7, ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbShowId' => 3, 'dbRecord' => 4, 'dbRebroadcast' => 5, 'dbOriginalShow' => 6, 'dbRecordedFile' => 7, ), - BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::SHOW_ID => 3, self::RECORD => 4, self::REBROADCAST => 5, self::INSTANCE_ID => 6, self::FILE_ID => 7, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'SHOW_ID' => 3, 'RECORD' => 4, 'REBROADCAST' => 5, 'INSTANCE_ID' => 6, 'FILE_ID' => 7, ), - BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'show_id' => 3, 'record' => 4, 'rebroadcast' => 5, 'instance_id' => 6, 'file_id' => 7, ), - BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, ) + BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbShowId' => 3, 'DbRecord' => 4, 'DbRebroadcast' => 5, 'DbOriginalShow' => 6, 'DbRecordedFile' => 7, 'DbSoundCloudId' => 8, ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbShowId' => 3, 'dbRecord' => 4, 'dbRebroadcast' => 5, 'dbOriginalShow' => 6, 'dbRecordedFile' => 7, 'dbSoundCloudId' => 8, ), + BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::SHOW_ID => 3, self::RECORD => 4, self::REBROADCAST => 5, self::INSTANCE_ID => 6, self::FILE_ID => 7, self::SOUNDCLOUD_ID => 8, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'SHOW_ID' => 3, 'RECORD' => 4, 'REBROADCAST' => 5, 'INSTANCE_ID' => 6, 'FILE_ID' => 7, 'SOUNDCLOUD_ID' => 8, ), + BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'show_id' => 3, 'record' => 4, 'rebroadcast' => 5, 'instance_id' => 6, 'file_id' => 7, 'soundcloud_id' => 8, ), + BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, ) ); /** @@ -171,6 +174,7 @@ abstract class BaseCcShowInstancesPeer { $criteria->addSelectColumn(CcShowInstancesPeer::REBROADCAST); $criteria->addSelectColumn(CcShowInstancesPeer::INSTANCE_ID); $criteria->addSelectColumn(CcShowInstancesPeer::FILE_ID); + $criteria->addSelectColumn(CcShowInstancesPeer::SOUNDCLOUD_ID); } else { $criteria->addSelectColumn($alias . '.ID'); $criteria->addSelectColumn($alias . '.STARTS'); @@ -180,6 +184,7 @@ abstract class BaseCcShowInstancesPeer { $criteria->addSelectColumn($alias . '.REBROADCAST'); $criteria->addSelectColumn($alias . '.INSTANCE_ID'); $criteria->addSelectColumn($alias . '.FILE_ID'); + $criteria->addSelectColumn($alias . '.SOUNDCLOUD_ID'); } } diff --git a/application/models/airtime/om/BaseCcShowInstancesQuery.php b/application/models/airtime/om/BaseCcShowInstancesQuery.php index 846f9d9dd..a45f511cc 100644 --- a/application/models/airtime/om/BaseCcShowInstancesQuery.php +++ b/application/models/airtime/om/BaseCcShowInstancesQuery.php @@ -14,6 +14,7 @@ * @method CcShowInstancesQuery orderByDbRebroadcast($order = Criteria::ASC) Order by the rebroadcast column * @method CcShowInstancesQuery orderByDbOriginalShow($order = Criteria::ASC) Order by the instance_id column * @method CcShowInstancesQuery orderByDbRecordedFile($order = Criteria::ASC) Order by the file_id column + * @method CcShowInstancesQuery orderByDbSoundCloudId($order = Criteria::ASC) Order by the soundcloud_id column * * @method CcShowInstancesQuery groupByDbId() Group by the id column * @method CcShowInstancesQuery groupByDbStarts() Group by the starts column @@ -23,6 +24,7 @@ * @method CcShowInstancesQuery groupByDbRebroadcast() Group by the rebroadcast column * @method CcShowInstancesQuery groupByDbOriginalShow() Group by the instance_id column * @method CcShowInstancesQuery groupByDbRecordedFile() Group by the file_id column + * @method CcShowInstancesQuery groupByDbSoundCloudId() Group by the soundcloud_id column * * @method CcShowInstancesQuery leftJoin($relation) Adds a LEFT JOIN clause to the query * @method CcShowInstancesQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query @@ -59,6 +61,7 @@ * @method CcShowInstances findOneByDbRebroadcast(int $rebroadcast) Return the first CcShowInstances filtered by the rebroadcast column * @method CcShowInstances findOneByDbOriginalShow(int $instance_id) Return the first CcShowInstances filtered by the instance_id column * @method CcShowInstances findOneByDbRecordedFile(int $file_id) Return the first CcShowInstances filtered by the file_id column + * @method CcShowInstances findOneByDbSoundCloudId(int $soundcloud_id) Return the first CcShowInstances filtered by the soundcloud_id column * * @method array findByDbId(int $id) Return CcShowInstances objects filtered by the id column * @method array findByDbStarts(string $starts) Return CcShowInstances objects filtered by the starts column @@ -68,6 +71,7 @@ * @method array findByDbRebroadcast(int $rebroadcast) Return CcShowInstances objects filtered by the rebroadcast column * @method array findByDbOriginalShow(int $instance_id) Return CcShowInstances objects filtered by the instance_id column * @method array findByDbRecordedFile(int $file_id) Return CcShowInstances objects filtered by the file_id column + * @method array findByDbSoundCloudId(int $soundcloud_id) Return CcShowInstances objects filtered by the soundcloud_id column * * @package propel.generator.airtime.om */ @@ -411,6 +415,37 @@ abstract class BaseCcShowInstancesQuery extends ModelCriteria return $this->addUsingAlias(CcShowInstancesPeer::FILE_ID, $dbRecordedFile, $comparison); } + /** + * Filter the query on the soundcloud_id column + * + * @param int|array $dbSoundCloudId The value to use as filter. + * Accepts an associative array('min' => $minValue, 'max' => $maxValue) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return CcShowInstancesQuery The current query, for fluid interface + */ + public function filterByDbSoundCloudId($dbSoundCloudId = null, $comparison = null) + { + if (is_array($dbSoundCloudId)) { + $useMinMax = false; + if (isset($dbSoundCloudId['min'])) { + $this->addUsingAlias(CcShowInstancesPeer::SOUNDCLOUD_ID, $dbSoundCloudId['min'], Criteria::GREATER_EQUAL); + $useMinMax = true; + } + if (isset($dbSoundCloudId['max'])) { + $this->addUsingAlias(CcShowInstancesPeer::SOUNDCLOUD_ID, $dbSoundCloudId['max'], Criteria::LESS_EQUAL); + $useMinMax = true; + } + if ($useMinMax) { + return $this; + } + if (null === $comparison) { + $comparison = Criteria::IN; + } + } + return $this->addUsingAlias(CcShowInstancesPeer::SOUNDCLOUD_ID, $dbSoundCloudId, $comparison); + } + /** * Filter the query by a related CcShow object * diff --git a/application/models/tests/populator.php b/application/models/tests/populator.php new file mode 100644 index 000000000..98992b650 --- /dev/null +++ b/application/models/tests/populator.php @@ -0,0 +1,98 @@ +create("Calendar Load test playlist ".uniqid()); + +// Add a file +$values = array("filepath" => __DIR__."/test10001.mp3"); +$storedFile = StoredFile::Insert($values, false); +$result = $playlist->addAudioClip($storedFile->getId()); + +// Add a file +$values = array("filepath" => __DIR__."/test10002.mp3"); +$storedFile2 = StoredFile::Insert($values, false); + +$result = $playlist->addAudioClip($storedFile2->getId()); +$result = $playlist->addAudioClip($storedFile2->getId()); + +echo "Created playlist ".$playlist->getName()." with ID ".$playlist->getId()."\n"; + +// Create the shows + +$data = array(); + +$currentDate = date("Y\\-m\\-d"); + +$year = date("Y"); +$month = date("m"); +$day = date("d"); + +$nextDay = $currentDate; + +#echo $currentDate; +$currentHour = date("H"); +$setHour = $currentHour + 1; + +$showNumber = 1; +for ($days=1; $days<100; $days=$days+1) +{ + // Adding shows until the end of the day + while ($setHour < 24) + { + echo 'Adding show: '.$nextDay. ' '.$setHour.":00\n"; + $data['add_show_name'] = 'automated show '.$showNumber; + $data['add_show_start_date'] = $nextDay; + $data['add_show_start_time'] = $setHour.':00'; + $showNumber = $showNumber + 1; + $data['add_show_duration'] = '1:00'; + $data['add_show_no_end'] = 0; + $data['add_show_repeats'] = 0; + $data['add_show_description'] = 'automated show'; + $data['add_show_url'] = 'http://www.OfirGal.com'; + $data['add_show_color'] = ""; + $data['add_show_background_color'] = ""; + $data['add_show_record'] = 0; + $data['add_show_hosts'] =""; + $showId = Show::create($data); + Show::populateShowUntil($showId, "2012-01-01 00:00:00"); + + // populating the show with a playlist + $show = new ShowInstance($showId); + $show->scheduleShow(array($playlist->getId())); + + $setHour = $setHour + 1; + } + // set the next day + $setHour = 0; + if ($day<30) { + $day = $day + 1; + } else { + $day = 1; + if ($month<12) + { + $month = $month + 1; + } else { + $month = 1; + $year = $year + 1; + } + } + $nextDay = $year."-".$month."-".$day; +} diff --git a/application/views/scripts/schedule/add-show-form.phtml b/application/views/scripts/schedule/add-show-form.phtml index 9d8613d7d..bd34fb341 100644 --- a/application/views/scripts/schedule/add-show-form.phtml +++ b/application/views/scripts/schedule/add-show-form.phtml @@ -1,7 +1,7 @@
Close - @@ -30,5 +30,11 @@
style; ?>
+
+ +
diff --git a/application/views/scripts/schedule/show-content-dialog.phtml b/application/views/scripts/schedule/show-content-dialog.phtml index 59dab4fbe..4e5aac630 100644 --- a/application/views/scripts/schedule/show-content-dialog.phtml +++ b/application/views/scripts/schedule/show-content-dialog.phtml @@ -1,4 +1,5 @@
+
additionalShowInfo; ?>
diff --git a/build/airtime.conf b/build/airtime.conf index e72855457..4cb119e33 100644 --- a/build/airtime.conf +++ b/build/airtime.conf @@ -4,6 +4,18 @@ dbname = airtime dbuser = airtime dbpass = airtime +[rabbitmq] +host = 127.0.0.1 +port = 5672 +user = guest +password = guest +vhost = / + [general] api_key = AAA +webServerUser = www-data +baseFilesDir = x +[soundcloud] +connection_retries = 3 +time_between_retries = 60 diff --git a/build/schema.xml b/build/schema.xml index f00ad14fb..d08f9387f 100644 --- a/build/schema.xml +++ b/build/schema.xml @@ -24,13 +24,6 @@
Start
- - - - - - -
@@ -138,6 +131,7 @@ + @@ -158,7 +152,7 @@ - + @@ -295,45 +289,4 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/build/sql/schema.sql b/build/sql/schema.sql index 71ff970ef..6bdc5351d 100644 --- a/build/sql/schema.sql +++ b/build/sql/schema.sql @@ -30,27 +30,6 @@ CREATE INDEX "cc_access_parent_idx" ON "cc_access" ("parent"); CREATE INDEX "cc_access_token_idx" ON "cc_access" ("token"); ------------------------------------------------------------------------------ --- cc_backup ------------------------------------------------------------------------------ - -DROP TABLE "cc_backup" CASCADE; - - -CREATE TABLE "cc_backup" -( - "token" VARCHAR(64) NOT NULL, - "sessionid" VARCHAR(64) NOT NULL, - "status" VARCHAR(32) NOT NULL, - "fromtime" TIMESTAMP NOT NULL, - "totime" TIMESTAMP NOT NULL, - PRIMARY KEY ("token") -); - -COMMENT ON TABLE "cc_backup" IS ''; - - -SET search_path TO public; ----------------------------------------------------------------------------- -- cc_files ----------------------------------------------------------------------------- @@ -190,6 +169,7 @@ CREATE TABLE "cc_show_instances" "rebroadcast" INT2 default 0, "instance_id" INTEGER, "file_id" INTEGER, + "soundcloud_id" INTEGER, PRIMARY KEY ("id") ); @@ -215,7 +195,7 @@ CREATE TABLE "cc_show_days" "repeat_type" INT2 NOT NULL, "next_pop_date" DATE, "show_id" INTEGER NOT NULL, - "record" INT2, + "record" INT2 default 0, PRIMARY KEY ("id") ); @@ -440,53 +420,6 @@ COMMENT ON TABLE "cc_subjs" IS ''; SET search_path TO public; ------------------------------------------------------------------------------ --- cc_trans ------------------------------------------------------------------------------ - -DROP TABLE "cc_trans" CASCADE; - - -CREATE TABLE "cc_trans" -( - "id" serial NOT NULL, - "trtok" CHAR(16) NOT NULL, - "direction" VARCHAR(128) NOT NULL, - "state" VARCHAR(128) NOT NULL, - "trtype" VARCHAR(128) NOT NULL, - "lock" CHAR(1) default 'N' NOT NULL, - "target" VARCHAR(255), - "rtrtok" CHAR(16), - "mdtrtok" CHAR(16), - "gunid" CHAR(32), - "pdtoken" INT8, - "url" VARCHAR(255), - "localfile" VARCHAR(255), - "fname" VARCHAR(255), - "title" VARCHAR(255), - "expectedsum" CHAR(32), - "realsum" CHAR(32), - "expectedsize" INTEGER, - "realsize" INTEGER, - "uid" INTEGER, - "errmsg" VARCHAR(255), - "jobpid" INTEGER, - "start" TIMESTAMP, - "ts" TIMESTAMP, - PRIMARY KEY ("id"), - CONSTRAINT "cc_trans_id_idx" UNIQUE ("id"), - CONSTRAINT "cc_trans_token_idx" UNIQUE ("pdtoken"), - CONSTRAINT "cc_trans_trtok_idx" UNIQUE ("trtok") -); - -COMMENT ON TABLE "cc_trans" IS ''; - - -SET search_path TO public; -CREATE INDEX "cc_trans_gunid_idx" ON "cc_trans" ("gunid"); - -CREATE INDEX "cc_trans_state_idx" ON "cc_trans" ("state"); - ALTER TABLE "cc_access" ADD CONSTRAINT "cc_access_owner_fkey" FOREIGN KEY ("owner") REFERENCES "cc_subjs" ("id"); ALTER TABLE "cc_files" ADD CONSTRAINT "cc_files_editedby_fkey" FOREIGN KEY ("editedby") REFERENCES "cc_subjs" ("id"); diff --git a/dev_tools/pl.sh b/dev_tools/pl.sh index 7c6f8b650..4e1c70134 100755 --- a/dev_tools/pl.sh +++ b/dev_tools/pl.sh @@ -1,3 +1,5 @@ #!/bin/sh - +echo +echo "This will tail the pypo-liquidsoap log file." +echo "Type in password for pypo user (default password is 'pypo'):" su -l pypo -c "tail -F /etc/service/pypo-liquidsoap/log/main/current" diff --git a/dev_tools/pr.sh b/dev_tools/pr.sh index f5e741658..aadab42ec 100755 --- a/dev_tools/pr.sh +++ b/dev_tools/pr.sh @@ -1,3 +1,7 @@ #!/bin/sh +echo +echo "This will tail the recorder log file." +echo "Type in password for pypo user (default password is 'pypo'):" + su -l pypo -c "tail -F /etc/service/recorder/log/main/current" diff --git a/dev_tools/pypoless.sh b/dev_tools/pypoless.sh index 542702527..f9db223af 100755 --- a/dev_tools/pypoless.sh +++ b/dev_tools/pypoless.sh @@ -1,3 +1,7 @@ #!/bin/sh +echo +echo "This will output the pypo log file." +echo "Type in password for pypo user (default password is 'pypo'):" + su -l pypo -c "less /etc/service/pypo/log/main/current" diff --git a/dev_tools/pypotail.sh b/dev_tools/pypotail.sh index e91cd3917..0f2168e44 100755 --- a/dev_tools/pypotail.sh +++ b/dev_tools/pypotail.sh @@ -1,3 +1,7 @@ #!/bin/sh +echo +echo "This will tail the pypo log file." +echo "Type in password for pypo user (default password is 'pypo'):" + su -l pypo -c "tail -F /etc/service/pypo/log/main/current" diff --git a/install/DoctrineMigrations/Version20110308003959.php b/install/DoctrineMigrations/Version20110308003959.php deleted file mode 100644 index 41f7199b5..000000000 --- a/install/DoctrineMigrations/Version20110308003959.php +++ /dev/null @@ -1,21 +0,0 @@ -getTable("cc_show_instances"); - $table->addColumn("record", "boolean", array( 'notnull' => 0, 'default' => 0)); - } - - public function down(Schema $schema) - { - $table = $schema->getTable("cc_show_instances"); - $table->dropColumn("record"); - } -} diff --git a/install/DoctrineMigrations/Version20110331111708.php b/install/DoctrineMigrations/Version20110331111708.php new file mode 100644 index 000000000..d355566a9 --- /dev/null +++ b/install/DoctrineMigrations/Version20110331111708.php @@ -0,0 +1,96 @@ +getTable('cc_show_instances'); + + $show_instances_table->addColumn('record', 'smallint', array('notnull' => 0, 'default' => 0)); + $show_instances_table->addColumn('rebroadcast', 'smallint', array('notnull' => 0, 'default' => 0)); + $show_instances_table->addColumn('instance_id', 'integer', array('notnull' => 0)); + $show_instances_table->addColumn('file_id', 'integer', array('notnull' => 0)); + $show_instances_table->addColumn('soundcloud_id', 'integer', array('notnull' => 0)); + + $show_instances_table->addNamedForeignKeyConstraint('cc_original_show_instance_fkey', $show_instances_table, array('instance_id'), array('id'), array('onDelete' => 'CASCADE')); + + $files_table = $schema->getTable('cc_files'); + $show_instances_table->addNamedForeignKeyConstraint('cc_recorded_file_fkey', $files_table, array('file_id'), array('id'), array('onDelete' => 'CASCADE')); + //end cc_show_instances modifications + + //start cc_show_days modifications + $show_days_table = $schema->getTable('cc_show_days'); + + $show_days_table->addColumn('record', 'smallint', array( 'notnull' => 0, 'default' => 0)); + //end cc_show_days modifications + + //start cc_show modifications + $show_table = $schema->getTable('cc_show'); + + $show_table->addColumn('url', 'string', array('notnull' => 0, 'length' => 255)); + //end cc_show modifications + + //start cc_schedule modifications + $schedule_table = $schema->getTable('cc_schedule'); + + $playlist_id_col = $schedule_table->getColumn('playlist_id'); + $playlist_id_col->setNotnull(false); + //end cc_schedule modifications + + //create cc_show_rebroadcast table + $cc_show_rebroadcast_table = $schema->createTable('cc_show_rebroadcast'); + + $cc_show_rebroadcast_table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => true)); + $cc_show_rebroadcast_table->addColumn('day_offset', 'string', array('length' => 255)); + $cc_show_rebroadcast_table->addColumn('start_time', 'datetime', array('notnull' => 1)); + $cc_show_rebroadcast_table->addColumn('show_id', 'integer', array('notnull' => 1)); + + $cc_show_rebroadcast_table->setPrimaryKey(array('id')); + //end create cc_show_rebroadcast table + } + + public function down(Schema $schema) + { + //start cc_show_instances modifications + $show_instances_table = $schema->getTable('cc_show_instances'); + + $show_instances_table->dropColumn('record'); + $show_instances_table->dropColumn('rebroadcast'); + $show_instances_table->dropColumn('instance_id'); + $show_instances_table->dropColumn('file_id'); + $show_instances_table->dropColumn('soundcloud_id'); + //end cc_show_instances modifications + + //start cc_show_days modifications + $show_days_table = $schema->getTable('cc_show_days'); + + $show_days_table->dropColumn('record'); + //end cc_show_days modifications + + //start cc_show modifications + $show_table = $schema->getTable('cc_show'); + + $show_table->dropColumn('url'); + //end cc_show modifications + + //start cc_schedule modifications + $schedule_table = $schema->getTable('cc_schedule'); + + $playlist_id_col = $schedule_table->getColumn('playlist_id'); + $playlist_id_col->setNotnull(true); + //end cc_schedule modifications + + //drop cc_show_rebroadcast table + $schema->dropTable('cc_show_rebroadcast'); + //end drop cc_show_rebroadcast table + } +} diff --git a/install/airtime-install.php b/install/airtime-install.php index 1d350f1e1..8b203f6df 100644 --- a/install/airtime-install.php +++ b/install/airtime-install.php @@ -5,56 +5,47 @@ * @license http://www.gnu.org/licenses/gpl.txt */ -// Do not allow remote execution -$arr = array_diff_assoc($_SERVER, $_ENV); -if (isset($arr["DOCUMENT_ROOT"]) && ($arr["DOCUMENT_ROOT"] != "") ) { - header("HTTP/1.1 400"); - header("Content-type: text/plain; charset=UTF-8"); - echo "400 Not executable\r\n"; - exit(1); -} - -require_once(dirname(__FILE__).'/../application/configs/conf.php'); -require_once(dirname(__FILE__).'/installInit.php'); - echo "******************************** Install Begin *********************************".PHP_EOL; -AirtimeInstall::ExitIfNotRoot(); -AirtimeInstall::CreateApiKey(); -AirtimeInstall::UpdateIniValue('../build/build.properties', 'project.home', realpath(__dir__.'/../')); +require_once(dirname(__FILE__).'/include/AirtimeIni.php'); +AirtimeIni::ExitIfNotRoot(); +AirtimeIni::CreateIniFile(); +AirtimeIni::UpdateIniFiles(); + +require_once(dirname(__FILE__).'/../application/configs/conf.php'); +require_once(dirname(__FILE__).'/include/AirtimeInstall.php'); + +echo PHP_EOL."*** Installing Airtime ".AIRTIME_VERSION." ***".PHP_EOL; echo PHP_EOL."*** Database Installation ***".PHP_EOL; -echo "* Creating Airtime Database User".PHP_EOL; +echo "* Creating Airtime database user".PHP_EOL; AirtimeInstall::CreateDatabaseUser(); -echo "* Creating Airtime Database".PHP_EOL; +echo "* Creating Airtime database".PHP_EOL; AirtimeInstall::CreateDatabase(); AirtimeInstall::DbConnect(true); -echo "* Install Postgresql Scripting Language".PHP_EOL; +echo "* Installing Postgresql scripting language".PHP_EOL; AirtimeInstall::InstallPostgresScriptingLanguage(); -echo "* Creating Database Tables".PHP_EOL; +echo "* Creating database tables".PHP_EOL; AirtimeInstall::CreateDatabaseTables(); -echo "* Storage Directory Setup".PHP_EOL; +echo "* Storage directory setup".PHP_EOL; AirtimeInstall::SetupStorageDirectory($CC_CONFIG); -echo "* Setting Dir Permissions".PHP_EOL; +echo "* Giving Apache permission to access the storage directory".PHP_EOL; 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"); -echo "* Python eggs Setup".PHP_EOL; -AirtimeInstall::SetUpPythonEggs(); - echo PHP_EOL."*** Pypo Installation ***".PHP_EOL; system("python ".__DIR__."/../python_apps/pypo/install/pypo-install.py"); diff --git a/install/airtime-uninstall.php b/install/airtime-uninstall.php index 5b9a58c3a..7accac35f 100644 --- a/install/airtime-uninstall.php +++ b/install/airtime-uninstall.php @@ -5,23 +5,18 @@ * @license http://www.gnu.org/licenses/gpl.txt */ -// Do not allow remote execution. -$arr = array_diff_assoc($_SERVER, $_ENV); -if (isset($arr["DOCUMENT_ROOT"]) && ($arr["DOCUMENT_ROOT"] != "") ) { - header("HTTP/1.1 400"); - header("Content-type: text/plain; charset=UTF-8"); - echo "400 Not executable".PHP_EOL; - exit; -} +require_once(dirname(__FILE__).'/include/AirtimeIni.php'); +// Need to check that we are superuser before running this. +AirtimeIni::ExitIfNotRoot(); require_once(dirname(__FILE__).'/../application/configs/conf.php'); -require_once(dirname(__FILE__).'/installInit.php'); +require_once(dirname(__FILE__).'/include/AirtimeInstall.php'); -// Need to check that we are superuser before running this. -AirtimeInstall::ExitIfNotRoot(); AirtimeInstall::RemoveSymlinks(); +echo "Uninstalling Airtime ".AIRTIME_VERSION.PHP_EOL; + echo "******************************* Uninstall Begin ********************************".PHP_EOL; //------------------------------------------------------------------------ // Delete the database @@ -80,7 +75,7 @@ if ($results == 0) { // Delete files //------------------------------------------------------------------------ AirtimeInstall::DeleteFilesRecursive($CC_CONFIG['storageDir']); - +AirtimeIni::RemoveIniFiles(); $command = "python ".__DIR__."/../python_apps/pypo/install/pypo-uninstall.py"; system($command); diff --git a/install/airtime-upgrade.php b/install/airtime-upgrade.php index 7d6fd3bdb..a84617321 100644 --- a/install/airtime-upgrade.php +++ b/install/airtime-upgrade.php @@ -6,27 +6,23 @@ * @license http://www.gnu.org/licenses/gpl.txt */ -// Do not allow remote execution -$arr = array_diff_assoc($_SERVER, $_ENV); -if (isset($arr["DOCUMENT_ROOT"]) && ($arr["DOCUMENT_ROOT"] != "") ) { - header("HTTP/1.1 400"); - header("Content-type: text/plain; charset=UTF-8"); - echo "400 Not executable\r\n"; - exit(1); -} +require_once(dirname(__FILE__).'/include/AirtimeIni.php'); +require_once(dirname(__FILE__).'/include/AirtimeInstall.php'); -require_once(dirname(__FILE__).'/installInit.php'); - -AirtimeInstall::ExitIfNotRoot(); +AirtimeIni::ExitIfNotRoot(); echo "******************************** Update Begin *********************************".PHP_EOL; -AirtimeInstall::UpdateIniValue('../build/build.properties', 'project.home', realpath(__dir__.'/../')); +AirtimeIni::CreateIniFile(); +AirtimeIni::UpdateIniFiles(); echo PHP_EOL."*** Updating Database Tables ***".PHP_EOL; AirtimeInstall::MigrateTables(__DIR__); echo PHP_EOL."*** Updating Pypo ***".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 "******************************* Update Complete *******************************".PHP_EOL; diff --git a/install/include/AirtimeIni.php b/install/include/AirtimeIni.php new file mode 100644 index 000000000..33e7f772e --- /dev/null +++ b/install/include/AirtimeIni.php @@ -0,0 +1,169 @@ + strlen($p_property)) + if ($p_property == substr($lines[$i], 0, strlen($p_property))){ + $lines[$i] = "$p_property = $p_value\n"; + } + } + + $fp=fopen($p_filename, 'w'); + for($i=0; $i<$n; $i++){ + fwrite($fp, $lines[$i]); + } + fclose($fp); + } + + /** + * After the configuration files have been copied to /etc/airtime, + * this function will update them to values unique to this + * particular installation. + */ + static function UpdateIniFiles() + { + $api_key = AirtimeIni::GenerateRandomString(); + AirtimeIni::UpdateIniValue('/etc/airtime/airtime.conf', 'api_key', $api_key); + AirtimeIni::UpdateIniValue('/etc/airtime/airtime.conf', 'baseFilesDir', realpath(__DIR__.'/../../').'/files'); + AirtimeIni::UpdateIniValue('/etc/airtime/pypo.cfg', 'api_key', "'$api_key'"); + AirtimeIni::UpdateIniValue('/etc/airtime/recorder.cfg', 'api_key', "'$api_key'"); + AirtimeIni::UpdateIniValue(__DIR__.'/../../build/build.properties', 'project.home', realpath(__dir__.'/../../')); + } +} diff --git a/install/installInit.php b/install/include/AirtimeInstall.php similarity index 68% rename from install/installInit.php rename to install/include/AirtimeInstall.php index 1efaa4833..6f9ce0ef5 100644 --- a/install/installInit.php +++ b/install/include/AirtimeInstall.php @@ -1,11 +1,7 @@ strlen($property)) - if ($property == substr($lines[$i], 0, strlen($property))){ - $lines[$i] = "$property = $value\n"; - } - } - - $fp=fopen($filename, 'w'); - for($i=0; $i<$n; $i++){ - fwrite($fp, $lines[$i]); - } - fclose($fp); - } - public static function SetupStorageDirectory($CC_CONFIG) { global $CC_CONFIG, $CC_DBC; @@ -145,9 +95,15 @@ class AirtimeInstall { @exec($command, $output, $results); if ($results == 0) { - echo "* User {$CC_CONFIG['dsn']['username']} created.".PHP_EOL; + echo "* Database user '{$CC_CONFIG['dsn']['username']}' created.".PHP_EOL; } else { - echo "* Could not create user {$CC_CONFIG['dsn']['username']}: $output".PHP_EOL; + if (count($output) > 0) { + echo "* Could not create user '{$CC_CONFIG['dsn']['username']}': ".PHP_EOL; + echo implode(PHP_EOL, $output); + } + else { + echo "* Database user '{$CC_CONFIG['dsn']['username']}' already exists.".PHP_EOL; + } } } @@ -160,7 +116,13 @@ class AirtimeInstall { if ($results == 0) { echo "* Database '{$CC_CONFIG['dsn']['database']}' created.".PHP_EOL; } else { - echo "* Could not create database '{$CC_CONFIG['dsn']['database']}': $output".PHP_EOL; + if (count($output) > 0) { + echo "* Could not create database '{$CC_CONFIG['dsn']['database']}': ".PHP_EOL; + echo implode(PHP_EOL, $output); + } + else { + echo "* Database '{$CC_CONFIG['dsn']['database']}' already exists.".PHP_EOL; + } } } @@ -181,7 +143,7 @@ class AirtimeInstall { public static function CreateDatabaseTables() { // Put Propel sql files in Database - $command = __DIR__."/../library/propel/generator/bin/propel-gen ../build/ insert-sql 2>propel-error.log"; + $command = __DIR__."/../../library/propel/generator/bin/propel-gen ../build/ insert-sql 2>propel-error.log"; @exec($command, $output, $results); } @@ -191,13 +153,6 @@ class AirtimeInstall { system($command); } - public static function SetUpPythonEggs() - { - //install poster streaming upload - $command = "sudo easy_install poster"; - @exec($command); - } - public static function DeleteFilesRecursive($p_path) { $command = "rm -rf $p_path"; @@ -207,10 +162,10 @@ class AirtimeInstall { public static function CreateSymlinks(){ AirtimeInstall::RemoveSymlinks(); - $dir = realpath(__DIR__."/../utils/airtime-import"); + $dir = realpath(__DIR__."/../../utils/airtime-import"); exec("ln -s $dir /usr/bin/airtime-import"); - $dir = realpath(__DIR__."/../utils/airtime-clean-storage"); + $dir = realpath(__DIR__."/../../utils/airtime-clean-storage"); exec("ln -s $dir /usr/bin/airtime-clean-storage"); } @@ -218,6 +173,4 @@ class AirtimeInstall { exec("rm -f /usr/bin/airtime-import"); exec("rm -f /usr/bin/airtime-clean-storage"); } - - } diff --git a/plugins/jquery.showinfo.js b/plugins/jquery.showinfo.js index c31381a10..c6eba4959 100644 --- a/plugins/jquery.showinfo.js +++ b/plugins/jquery.showinfo.js @@ -4,8 +4,10 @@ var defaults = { updatePeriod: 20, //seconds sourceDomain: "http://localhost/", //where to get show status from + text: {onAirToday:"On air today"} }; var options = $.extend(defaults, options); + options.sourceDomain = addEndingBackslash(options.sourceDomain); return this.each(function() { var obj = $(this); @@ -29,7 +31,7 @@ } tableString = ""; - tableString += "

On air today

"; + tableString += "

"+options.text.onAirToday+"

"; tableString += ""+ ""; @@ -40,7 +42,7 @@ tableString += "" + "" + - "" + + "" + ""; } @@ -54,11 +56,14 @@ updateWidget(); } + function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){ + } + 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); + }, error:airtimeScheduleJsonpError}); + setTimeout(getServerData, options.updatePeriod*1000); } }); }; @@ -71,9 +76,11 @@ var defaults = { updatePeriod: 5, //seconds sourceDomain: "http://localhost/", //where to get show status from - audioStreamSource: "" //where to get audio stream from + audioStreamSource: "http://localhost:8000/airtime.mp3", //where to get audio stream from + text: {listenLive:"Listen WADR Live", onAirNow:"On Air Now", offline:"Offline", current:"Current", next:"Next"} }; var options = $.extend(defaults, options); + options.sourceDomain = addEndingBackslash(options.sourceDomain); return this.each(function() { var obj = $(this); @@ -84,7 +91,7 @@ var currentShow = sd.getCurrentShow(); var nextShows = sd.getNextShows(); - var showStatus = "Offline"; + var showStatus = options.text.offline; var currentShowName = ""; var timeElapsed = ""; var timeRemaining = ""; @@ -93,7 +100,7 @@ var nextShowRange = ""; if (currentShow.length > 0){ - showStatus = "On Air Now"; + showStatus = options.text.onAirNow; currentShowName = currentShow[0].getName(); timeElapsed = sd.getShowTimeElapsed(currentShow[0]); @@ -106,14 +113,14 @@ } obj.empty(); - obj.append("Listen WADR Live"); + obj.append(""+options.text.listenLive+""); obj.append("

"+showStatus+" >>

"); obj.append(""); //refresh the UI to update the elapsed/remaining time @@ -125,16 +132,121 @@ updateWidget(); } + function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){ + } + 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); + }, error:airtimeScheduleJsonpError}); + setTimeout(getServerData, options.updatePeriod*1000); } }); }; })(jQuery); +(function($){ + $.fn.airtimeWeekSchedule = function(options) { + + var defaults = { + sourceDomain: "http://localhost/", //where to get show status from + updatePeriod: 600, + dowText: {monday:"Monday", tuesday:"Tuesday", wednesday:"Wednesday", thursday:"Thursday", friday:"Friday", saturday:"Saturday", sunday:"Sunday"}, + miscText: {time:"Time", programName:"Program Name", details:"Details", readMore:"Read More"} + }; + var options = $.extend(defaults, options); + options.sourceDomain = addEndingBackslash(options.sourceDomain); + + return this.each(function() { + var obj = $(this); + obj.empty(); + + obj.attr("class", "ui-tabs"); + + var dow = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]; + + var html = ''; + + for (var i=0; i' + } + obj.append(html); + getServerData(); + + function updateWidget(data){ + for (var i=0; i'+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''; + var daySchedule = data[dow[i]]; + for (var j=0; j'+ + ''+ + ''+ + ''+ + ''; + } + html += + ''+ + '
"+shows[i].getRange()+""+shows[i].getName()+" Listen"+shows[i].getName()+"
'+options.miscText.time+''+options.miscText.programName+''+options.miscText.details+'
'+getTime(daySchedule[j].show_starts)+ " - " + getTime(daySchedule[j].show_ends)+''+ + '

'+daySchedule[j].show_name+'

'+ + '
'+ + ''+ + '
'; + + $("#"+dow[i]).empty(); + $("#"+dow[i]).append(html); + } + } + + function processData(data){ + updateWidget(data); + } + + function airtimeScheduleJsonpError(jqXHR, textStatus, errorThrown){ + } + + function getServerData(){ + $.ajax({ url: options.sourceDomain + "api/week-info/", dataType:"jsonp", success:function(data){ + processData(data); + }, error:airtimeScheduleJsonpError}); + setTimeout(getServerData, options.updatePeriod*1000); + } + }); + }; +})(jQuery); + +function addEndingBackslash(str){ + if (str.charAt(str.length-1) != '/') + return str+'/'; + else return str; +} + /* ScheduleData class BEGIN */ function ScheduleData(data){ this.data = data; @@ -263,5 +375,5 @@ function convertDateToPosixTime(s){ } else sec = time[2]; - return Date.UTC(year, month, day, hour, minute, sec, msec); + return Date.UTC(year, month-1, day, hour, minute, sec, msec); } diff --git a/public/css/images/icon_finishedplaying_m.png b/public/css/images/icon_finishedplaying_m.png new file mode 100644 index 000000000..5b221d29d Binary files /dev/null and b/public/css/images/icon_finishedplaying_m.png differ diff --git a/public/css/images/icon_nowplaying_m.png b/public/css/images/icon_nowplaying_m.png new file mode 100644 index 000000000..844f80193 Binary files /dev/null and b/public/css/images/icon_nowplaying_m.png differ diff --git a/public/css/images/icon_soundcloud.png b/public/css/images/icon_soundcloud.png new file mode 100644 index 000000000..03c943309 Binary files /dev/null and b/public/css/images/icon_soundcloud.png differ diff --git a/public/css/images/icon_soundcloud_m.png b/public/css/images/icon_soundcloud_m.png new file mode 100644 index 000000000..553f4df98 Binary files /dev/null and b/public/css/images/icon_soundcloud_m.png differ diff --git a/public/css/redmond/jquery-ui-1.8.8.custom.css b/public/css/redmond/jquery-ui-1.8.8.custom.css index 1179f337c..aaef2c929 100644 --- a/public/css/redmond/jquery-ui-1.8.8.custom.css +++ b/public/css/redmond/jquery-ui-1.8.8.custom.css @@ -1513,4 +1513,4 @@ padding: 0; .ui-datepicker { display:none; -} \ No newline at end of file +} diff --git a/public/css/styles.css b/public/css/styles.css index 7d0dc8690..3f11e491e 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -1211,6 +1211,10 @@ ul.errors { margin:0; } +.formrow-repeat ul.errors { + width:278px; +} + ul.errors li { color:#902d2d; font-size:11px; @@ -1440,7 +1444,7 @@ div.success{ list-style-type:none; margin:0 0 7px 0; padding:0; - height:26px; + /*height:26px;*/ display:block; } .formrow-repeat li .ui-button-icon-only { @@ -1501,7 +1505,10 @@ div.success{ .small-icon.rebroadcast { background:url(images/icon_rebroadcast.png) no-repeat 0 0; } - +.small-icon.soundcloud { + background:url(images/icon_soundcloud.png) no-repeat 0 0; + width:21px; +} .medium-icon { display:block; width:25px; @@ -1511,8 +1518,23 @@ div.success{ } .medium-icon.recording { background:url(images/icon_record_m.png) no-repeat 0 0; + width:20px; } .medium-icon.rebroadcast { background:url(images/icon_rebroadcast_m.png) no-repeat 0 0; } - +.medium-icon.soundcloud { + background:url(images/icon_soundcloud_m.png) no-repeat 0 0; + width:21px; +} +.medium-icon.nowplaying, .medium-icon.finishedplaying { + background:url(images/icon_nowplaying_m.png) no-repeat 0 0; + width:12px; + height:9px; + float:left; + margin-left:6px; + margin-right:0; +} +.medium-icon.finishedplaying { + background:url(images/icon_finishedplaying_m.png) no-repeat 0 0; +} diff --git a/public/js/airtime/schedule/add-show.js b/public/js/airtime/schedule/add-show.js index 329391086..480530e7e 100644 --- a/public/js/airtime/schedule/add-show.js +++ b/public/js/airtime/schedule/add-show.js @@ -181,8 +181,15 @@ function setAddShowEvents() { makeAddShowButton(); }); - form.find("#add-show-submit") + form.find(".add-show-submit") .click(function(event){ + var addShowButton = $(this); + if (!addShowButton.hasClass("disabled")){ + addShowButton.addClass("disabled"); + } else { + return; + } + event.preventDefault(); var data = $("form").serializeArray(); @@ -203,6 +210,7 @@ function setAddShowEvents() { var end_date = $("#add_show_end_date").val(); $.post("/Schedule/add-show", {format: "json", data: data, hosts: hosts, days: days}, function(json){ + addShowButton.removeClass("disabled"); if(json.form) { $("#add-show-form") .empty() diff --git a/public/js/playlist/helperfunctions.js b/public/js/playlist/helperfunctions.js index a9a57467c..cdd2eb931 100644 --- a/public/js/playlist/helperfunctions.js +++ b/public/js/playlist/helperfunctions.js @@ -134,7 +134,7 @@ function convertDateToPosixTime(s){ } else sec = time[2]; - return Date.UTC(year, month, day, hour, minute, sec, msec); + return Date.UTC(year, month-1, day, hour, minute, sec, msec); } function getFileExt(filename){ diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index cd2a6e7d7..9ff02ee74 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -117,7 +117,7 @@ class AirTimeApiClient(ApiClientInterface): def __get_airtime_version(self, verbose = True): logger = logging.getLogger() - url = self.config["base_url"] + self.config["api_base"] + self.config["version_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["version_url"]) logger.debug("Trying to contact %s", url) url = url.replace("%%api_key%%", self.config["api_key"]) @@ -198,7 +198,8 @@ class AirTimeApiClient(ApiClientInterface): logger = logging.getLogger() # 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"] + export_url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["export_url"]) logger.info("Fetching schedule from %s", export_url) export_url = export_url.replace('%%api_key%%', self.config["api_key"]) @@ -215,12 +216,15 @@ class AirTimeApiClient(ApiClientInterface): return status, response - def get_media(self, src, dst): + def get_media(self, uri, dst): logger = logging.getLogger() try: + src = "http://%s:%s/%s/%s" % \ + (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["get_media_url"]) + src = src.replace("%%file%%", uri) logger.info("try to download from %s to %s", src, dst) - src = src + "/api_key/" + self.config["api_key"] + src = src.replace("%%api_key%%", self.config["api_key"]) # check if file exists already before downloading again filename, headers = urllib.urlretrieve(src, dst) except Exception, e: @@ -234,7 +238,9 @@ class AirTimeApiClient(ApiClientInterface): logger = logging.getLogger() playlist = schedule[pkey] schedule_id = playlist["schedule_id"] - url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] + #url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_item_url"]) + url = url.replace("%%schedule_id%%", str(schedule_id)) logger.debug(url) url = url.replace("%%api_key%%", self.config["api_key"]) @@ -261,7 +267,8 @@ class AirTimeApiClient(ApiClientInterface): response = '' try: schedule_id = data - url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] + #url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_start_playing_url"]) url = url.replace("%%media_id%%", str(media_id)) url = url.replace("%%schedule_id%%", str(schedule_id)) logger.debug(url) @@ -288,9 +295,10 @@ class AirTimeApiClient(ApiClientInterface): def get_shows_to_record(self): logger = logging.getLogger() - response = '' + response = None try: - url = self.config["base_url"] + self.config["api_base"] + self.config["show_schedule_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["show_schedule_url"]) + #url = self.config["base_url"] + self.config["api_base"] + self.config["show_schedule_url"] logger.debug(url) url = url.replace("%%api_key%%", self.config["api_key"]) @@ -301,23 +309,40 @@ class AirTimeApiClient(ApiClientInterface): except Exception, e: logger.error("Exception: %s", e) - return response[u'shows'] + return response 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() + retries = int(self.config["upload_retries"]) + retries_wait = int(self.config["upload_wait"]) - logger.info("uploaded show result %s", response) - - except Exception, e: - logger.error("Exception: %s", e) + #url = self.config["base_url"] + self.config["api_base"] + self.config["upload_file_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["upload_file_url"]) + + logger.debug(url) + url = url.replace("%%api_key%%", self.config["api_key"]) + + for i in range(0, retries): + logger.debug("Upload attempt: %s", i+1) + + try: + request = urllib2.Request(url, data, headers) + response = urllib2.urlopen(request).read().strip() + + logger.info("uploaded show result %s", response) + break + + except urllib2.HTTPError, e: + logger.error("Http error code: %s", e.code) + except urllib2.URLError, e: + logger.error("Server is down: %s", e.args) + except Exception, e: + logger.error("Exception: %s", e) + + #wait some time before next retry + time.sleep(retries_wait) return response @@ -368,7 +393,9 @@ class ObpApiClient(): logger = logging.getLogger() # lookup OBP version - url = self.config["base_url"] + self.config["api_base"]+ self.config["version_url"] + #url = self.config["base_url"] + self.config["api_base"]+ self.config["version_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["version_url"]) + try: logger.debug("Trying to contact %s", url) @@ -427,7 +454,8 @@ class ObpApiClient(): range['end'] = end # 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"] + export_url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["export_url"]) # Insert the start and end times into the URL export_url = export_url.replace('%%api_key%%', self.config["api_key"]) @@ -465,7 +493,8 @@ class ObpApiClient(): def notify_scheduled_item_start_playing(self, pkey, schedule): #def update_scheduled_item(self, item_id, value): logger = logging.getLogger() - url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] + #url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_item_url"]) url = url.replace("%%item_id%%", str(schedule[pkey]["id"])) url = url.replace("%%played%%", "1") @@ -495,7 +524,8 @@ class ObpApiClient(): playlist_id = data["playlist_id"] transmission_id = data["transmission_id"] - url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] + #url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_start_playing_url"]) url = url.replace("%%playlist_type%%", str(playlist_type)) url = url.replace("%%export_source%%", str(export_source)) url = url.replace("%%media_id%%", str(media_id)) @@ -521,7 +551,8 @@ class ObpApiClient(): def generate_range_dp(self): logger = logging.getLogger() - url = self.config["base_url"] + self.config["api_base"] + self.config["generate_range_url"] + #url = self.config["base_url"] + self.config["api_base"] + self.config["generate_range_url"] + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["generate_range_url"]) try: response = urllib.urlopen(url, self.api_auth) diff --git a/python_apps/pypo/pypo-api-validator.py b/python_apps/pypo/pypo-api-validator.py index f5c85e317..0fcce4fef 100755 --- a/python_apps/pypo/pypo-api-validator.py +++ b/python_apps/pypo/pypo-api-validator.py @@ -31,7 +31,7 @@ import unittest logging.config.fileConfig("logging-api-validator.cfg") try: - config = ConfigObj('config.cfg') + config = ConfigObj('/etc/airtime/pypo.cfg') except Exception, e: print 'Error loading config file: ', e sys.exit() diff --git a/python_apps/pypo/pypo-cli.py b/python_apps/pypo/pypo-cli.py index 208049d7b..87b24b8b4 100755 --- a/python_apps/pypo/pypo-cli.py +++ b/python_apps/pypo/pypo-cli.py @@ -63,7 +63,7 @@ logging.config.fileConfig("logging.cfg") # loading config file try: - config = ConfigObj('config.cfg') + config = ConfigObj('/etc/airtime/pypo.cfg') except Exception, e: print 'Error loading config file: ', e sys.exit() diff --git a/python_apps/pypo/pypo-notify.py b/python_apps/pypo/pypo-notify.py index 5e46e088f..d59932dcb 100755 --- a/python_apps/pypo/pypo-notify.py +++ b/python_apps/pypo/pypo-notify.py @@ -58,7 +58,7 @@ logging.config.fileConfig("logging.cfg") # loading config file try: - config = ConfigObj('config.cfg') + config = ConfigObj('/etc/airtime/pypo.cfg') except Exception, e: print 'error: ', e diff --git a/python_apps/pypo/config.cfg b/python_apps/pypo/pypo.cfg similarity index 95% rename from python_apps/pypo/config.cfg rename to python_apps/pypo/pypo.cfg index 2c0fce6f0..b1f9ed229 100644 --- a/python_apps/pypo/config.cfg +++ b/python_apps/pypo/pypo.cfg @@ -18,7 +18,8 @@ file_dir = '/opt/pypo/files/' tmp_dir = '/opt/pypo/tmp/' # Hostname -base_url = 'http://localhost/' +base_url = 'localhost' +base_port = 80 ############################################ # Liquidsoap settings # @@ -77,7 +78,7 @@ cue_style = 'pre' api_key = 'AAA' # Path to the base of the API -api_base = 'api/' +api_base = 'api' # URL to get the version number of the server API version_url = 'version/api_key/%%api_key%%' @@ -85,7 +86,9 @@ version_url = 'version/api_key/%%api_key%%' # Schedule export path. # %%from%% - 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%%' +export_url = 'schedule/api_key/%%api_key%%' + +get_media_url = 'get-media/file/%%file%%/api_key/%%api_key%%' # Update whether a schedule group has begun playing. update_item_url = 'notify-schedule-group-play/api_key/%%api_key%%/schedule_id/%%schedule_id%%' diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 2958c6bd4..aada900ff 100644 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -27,7 +27,7 @@ logging.config.fileConfig("logging.cfg") # loading config file try: - config = ConfigObj('config.cfg') + config = ConfigObj('/etc/airtime/pypo.cfg') LS_HOST = config['ls_host'] LS_PORT = config['ls_port'] POLL_INTERVAL = int(config['poll_interval']) @@ -84,9 +84,12 @@ class PypoFetch(Thread): 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) + logger.error("ERROR: Airtime server and pypo timezone offsets do not match. Audio playback will not start when expected!!!") + logger.error(" * Server timezone offset: %s", server_timezone) + logger.error(" * Pypo timezone offset: %s", pypo_timezone) + logger.error(" * To fix this, you need to set the 'date.timezone' value in your php.ini file and restart apache.") + logger.error(" * See this page for more info (v1.7): http://wiki.sourcefabric.org/x/BQBF") + logger.error(" * and also the 'FAQ and Support' page underneath it.") """ Process the schedule @@ -208,12 +211,8 @@ class PypoFetch(Thread): (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 - # check if it is a remote file, if yes download - if media['uri'][0:4] == 'http': - self.handle_remote_file(media, dst, do_cue) - else: - logger.debug("invalid media uri: %s", media['uri']) - + # download media file + self.handle_remote_file(media, dst, do_cue) if True == os.access(dst, os.R_OK): # check filesize (avoid zero-byte files) diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py index 25bfddbf1..db3d95393 100644 --- a/python_apps/pypo/pypopush.py +++ b/python_apps/pypo/pypopush.py @@ -20,7 +20,7 @@ logging.config.fileConfig("logging.cfg") # loading config file try: - config = ConfigObj('config.cfg') + config = ConfigObj('/etc/airtime/pypo.cfg') LS_HOST = config['ls_host'] LS_PORT = config['ls_port'] PUSH_INTERVAL = 2 diff --git a/python_apps/pypo/scripts/ls_config.liq b/python_apps/pypo/scripts/liquidsoap.cfg similarity index 60% rename from python_apps/pypo/scripts/ls_config.liq rename to python_apps/pypo/scripts/liquidsoap.cfg index 20b1f4215..a7f310f1a 100644 --- a/python_apps/pypo/scripts/ls_config.liq +++ b/python_apps/pypo/scripts/liquidsoap.cfg @@ -1,9 +1,6 @@ ########################################### # liquidsoap config file # ########################################### -# This config assumes that there are -# two instances of LS running -# the "scheduler" & the "fallback" instance ########################################### @@ -13,13 +10,6 @@ log_file = "/var/log/pypo/