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 = "