From d28c6620562ade0a52a94427cfabb90265508eee Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 11:18:38 -0400 Subject: [PATCH 01/21] CC-2555: Now Playing page takes roughly 1.4 seconds to load on localhost -fixed --- .../application/controllers/NowplayingController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/airtime_mvc/application/controllers/NowplayingController.php b/airtime_mvc/application/controllers/NowplayingController.php index 7c4815ae5..257b354a2 100644 --- a/airtime_mvc/application/controllers/NowplayingController.php +++ b/airtime_mvc/application/controllers/NowplayingController.php @@ -24,10 +24,10 @@ class NowplayingController extends Zend_Controller_Action $refer_sses = new Zend_Session_Namespace('referrer'); $userInfo = Zend_Auth::getInstance()->getStorage()->read(); $user = new User($userInfo->id); - - $form = new Application_Form_RegisterAirtime(); - + if ($request->isPost()) { + $form = new Application_Form_RegisterAirtime(); + $values = $request->getPost(); if ($values["Publicise"] != 1){ Application_Model_Preference::SetSupportFeedback($values["SupportFeedback"]); @@ -64,6 +64,10 @@ class NowplayingController extends Zend_Controller_Action //popup if previous page was login if($refer_sses->referrer == 'login' && Application_Model_Nowplaying::ShouldShowPopUp() && !Application_Model_Preference::GetSupportFeedback() && $user->isAdmin()){ + + $form = new Application_Form_RegisterAirtime(); + + $logo = Application_Model_Preference::GetStationLogo(); if($logo){ $this->view->logoImg = $logo; From 8ba8944964d6fe74488400d419ae47ef2f04642c Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 11:52:33 -0400 Subject: [PATCH 02/21] CC-2279: Upgrade script for converting stor directory to new format -added conf.php file --- install/upgrades/airtime-1.9.0/conf.php | 83 +++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 install/upgrades/airtime-1.9.0/conf.php diff --git a/install/upgrades/airtime-1.9.0/conf.php b/install/upgrades/airtime-1.9.0/conf.php new file mode 100644 index 000000000..0653af60b --- /dev/null +++ b/install/upgrades/airtime-1.9.0/conf.php @@ -0,0 +1,83 @@ + 'cc_', + + /* ================================================ storage configuration */ + + 'soundcloud-client-id' => '2CLCxcSXYzx7QhhPVHN4A', + 'soundcloud-client-secret' => 'pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs', + + "rootDir" => __DIR__."/../..", + 'pearPath' => dirname(__FILE__).'/../../../airtime_mvc/library/pear', + 'zendPath' => dirname(__FILE__).'/../../../airtime_mvc/library/Zend', + 'phingPath' => dirname(__FILE__).'/../../../airtime_mvc/library/phing' +); + +$CC_CONFIG = Config::loadConfig($CC_CONFIG); + +// Add database table names +$CC_CONFIG['playListTable'] = $CC_CONFIG['tblNamePrefix'].'playlist'; +$CC_CONFIG['playListContentsTable'] = $CC_CONFIG['tblNamePrefix'].'playlistcontents'; +$CC_CONFIG['filesTable'] = $CC_CONFIG['tblNamePrefix'].'files'; +$CC_CONFIG['accessTable'] = $CC_CONFIG['tblNamePrefix'].'access'; +$CC_CONFIG['permTable'] = $CC_CONFIG['tblNamePrefix'].'perms'; +$CC_CONFIG['sessTable'] = $CC_CONFIG['tblNamePrefix'].'sess'; +$CC_CONFIG['subjTable'] = $CC_CONFIG['tblNamePrefix'].'subjs'; +$CC_CONFIG['smembTable'] = $CC_CONFIG['tblNamePrefix'].'smemb'; +$CC_CONFIG['prefTable'] = $CC_CONFIG['tblNamePrefix'].'pref'; +$CC_CONFIG['scheduleTable'] = $CC_CONFIG['tblNamePrefix'].'schedule'; +$CC_CONFIG['playListTimeView'] = $CC_CONFIG['tblNamePrefix'].'playlisttimes'; +$CC_CONFIG['showSchedule'] = $CC_CONFIG['tblNamePrefix'].'show_schedule'; +$CC_CONFIG['showDays'] = $CC_CONFIG['tblNamePrefix'].'show_days'; +$CC_CONFIG['showTable'] = $CC_CONFIG['tblNamePrefix'].'show'; +$CC_CONFIG['showInstances'] = $CC_CONFIG['tblNamePrefix'].'show_instances'; + +$CC_CONFIG['playListSequence'] = $CC_CONFIG['playListTable'].'_id'; +$CC_CONFIG['filesSequence'] = $CC_CONFIG['filesTable'].'_id'; +$CC_CONFIG['prefSequence'] = $CC_CONFIG['prefTable'].'_id'; +$CC_CONFIG['permSequence'] = $CC_CONFIG['permTable'].'_id'; +$CC_CONFIG['subjSequence'] = $CC_CONFIG['subjTable'].'_id'; +$CC_CONFIG['smembSequence'] = $CC_CONFIG['smembTable'].'_id'; + +// Add libs to the PHP path +$old_include_path = get_include_path(); +set_include_path('.'.PATH_SEPARATOR.$CC_CONFIG['pearPath'] + .PATH_SEPARATOR.$CC_CONFIG['zendPath'] + .PATH_SEPARATOR.$old_include_path); + +class Config { + public static function loadConfig($CC_CONFIG) { + $values = parse_ini_file('/etc/airtime/airtime.conf', true); + + // Name of the web server user + $CC_CONFIG['webServerUser'] = $values['general']['web_server_user']; + $CC_CONFIG['rabbitmq'] = $values['rabbitmq']; + + $CC_CONFIG['baseUrl'] = $values['general']['base_url']; + $CC_CONFIG['basePort'] = $values['general']['base_port']; + + // Database config + $CC_CONFIG['dsn']['username'] = $values['database']['dbuser']; + $CC_CONFIG['dsn']['password'] = $values['database']['dbpass']; + $CC_CONFIG['dsn']['hostspec'] = $values['database']['host']; + $CC_CONFIG['dsn']['phptype'] = 'pgsql'; + $CC_CONFIG['dsn']['database'] = $values['database']['dbname']; + + $CC_CONFIG['apiKey'] = array($values['general']['api_key']); + + $CC_CONFIG['soundcloud-connection-retries'] = $values['soundcloud']['connection_retries']; + $CC_CONFIG['soundcloud-connection-wait'] = $values['soundcloud']['time_between_retries']; + + return $CC_CONFIG; + } +} From 542547ecf0334e1cc24739e5134230c228d23d85 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 11:53:15 -0400 Subject: [PATCH 03/21] CC-2559: Remove files from cc_schedule that are no longer watched -added foreign key relationship --- .../DoctrineMigrations/Version20110711161043.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/install/DoctrineMigrations/Version20110711161043.php b/install/DoctrineMigrations/Version20110711161043.php index d10c76c00..c21f097fb 100644 --- a/install/DoctrineMigrations/Version20110711161043.php +++ b/install/DoctrineMigrations/Version20110711161043.php @@ -3,8 +3,9 @@ namespace DoctrineMigrations; /* -update cc_files table to include to "directory" column as well as add foreign key relation to -cc_music_dirs table. +1) update cc_files table to include to "directory" column +2) create a foreign key relationship from cc_files to cc_music_dirs +3) create a foreign key relationship from cc_schedule to cc_files */ use Doctrine\DBAL\Migrations\AbstractMigration, @@ -14,21 +15,22 @@ class Version20110711161043 extends AbstractMigration { public function up(Schema $schema) { - - //CREATE the default value of "/srv/airtime/stor", this can be updated later in the upgrade script. + /* 1) update cc_files table to include to "directory" column */ $this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('stor', '/srv/airtime/stor');"); $this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('upgrade', '');"); $cc_music_dirs = $schema->getTable('cc_music_dirs'); - //start cc_files modifications + /* 2) create a foreign key relationship from cc_files to cc_music_dirs */ $cc_files = $schema->getTable('cc_files'); $cc_files->addColumn('directory', 'integer', array('default'=> 2)); $cc_files->addNamedForeignKeyConstraint('cc_music_dirs_folder_fkey', $cc_music_dirs, array('directory'), array('id'), array('onDelete' => 'CASCADE')); - //end cc_files modifications - + + /* 3) create a foreign key relationship from cc_schedule to cc_files */ + $cc_schedule = $schema->getTable('cc_schedule'); + $cc_schedule->addNamedForeignKeyConstraint('cc_files_folder_fkey', $cc_files, array('file_id'), array('id'), array('onDelete' => 'CASCADE')); } public function down(Schema $schema) From ef0e0dcfa97aeef228e61dedb09850a1074746a4 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 12:11:59 -0400 Subject: [PATCH 04/21] CC-2548: Update from 1.9-beta2 to beta4, doesn't update the version tag in About -fixed --- install/include/airtime-upgrade.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/install/include/airtime-upgrade.php b/install/include/airtime-upgrade.php index ce7ac7077..eb0dd2aa0 100644 --- a/install/include/airtime-upgrade.php +++ b/install/include/airtime-upgrade.php @@ -8,7 +8,9 @@ //Pear classes. set_include_path(__DIR__.'/../../airtime_mvc/library/pear' . PATH_SEPARATOR . get_include_path()); + require_once('DB.php'); +require_once(__DIR__.'/../../airtime_mvc/application/configs/constants.php'); require_once(dirname(__FILE__).'/AirtimeIni.php'); if(exec("whoami") != "root"){ @@ -87,7 +89,9 @@ if (strcmp($version, "1.9.0") < 0){ //set the new version in the database. $sql = "DELETE FROM cc_pref WHERE keystr = 'system_version'"; $CC_DBC->query($sql); -$sql = "INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '1.9.0-devel')"; + +$newVersion = AIRTIME_VERSION; +$sql = "INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '$newVersion')"; $CC_DBC->query($sql); From f044f6df8305b85ed0716d914434bc9deab4f672 Mon Sep 17 00:00:00 2001 From: Paul Baranowski Date: Mon, 18 Jul 2011 13:30:40 -0400 Subject: [PATCH 05/21] Fixed grammar, typos, and spelling in airtime-import. --- utils/airtime-import/airtime-import.py | 56 +++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/utils/airtime-import/airtime-import.py b/utils/airtime-import/airtime-import.py index c5b29036c..6abde083b 100644 --- a/utils/airtime-import/airtime-import.py +++ b/utils/airtime-import/airtime-import.py @@ -47,10 +47,10 @@ def copy_or_move_files_to(paths, dest, flag): if( 'mp3' in ext or 'ogg' in ext ): destfile = dest+os.path.basename(path) if(flag == 'copy'): - print "Copying %(src)s to %(dest)s....." % {'src':path, 'dest':destfile} + print "Copying %(src)s to %(dest)s..." % {'src':path, 'dest':destfile} shutil.copy2(path, destfile) elif(flag == 'move'): - print "Moving %(src)s to %(dest)s....." % {'src':path, 'dest':destfile} + print "Moving %(src)s to %(dest)s..." % {'src':path, 'dest':destfile} shutil.move(path, destfile) else: print "Cannot find file or path: %s" % path @@ -95,7 +95,7 @@ def printHelp(): ======================== There are two ways to import audio files into Airtime: -1) Copy or move files into the storage folder +1) Use airtime-import to copy or move files into the storage folder. Copied or moved files will be placed into the folder: %s @@ -103,12 +103,12 @@ There are two ways to import audio files into Airtime: Files will be automatically organized into the structure "Artist/Album/TrackNumber-TrackName-Bitrate.file_extension". -2) Add a folder to the Airtime library("watch" a folder) +2) Use airtime-import to add a folder to the Airtime library ("watch" a folder). All the files in the watched folder will be imported to Airtime and the folder will be monitored to automatically detect any changes. Hence any changes done in the folder(add, delete, edit a file) will trigger - updates in Airtime libarary. + updates in Airtime library. """ % storage_dir parser.print_help() print "" @@ -117,7 +117,7 @@ def CopyAction(option, opt, value, parser): errorIfMultipleOption(parser.rargs) stor = helper_get_stor_dir() if(stor is None): - exit("Unable to connect to the server.") + exit("Unable to connect to the Airtime server.") dest = stor+"organize/" copy_or_move_files_to(parser.rargs, dest, 'copy') @@ -125,16 +125,16 @@ def MoveAction(option, opt, value, parser): errorIfMultipleOption(parser.rargs) stor = helper_get_stor_dir() if(stor is None): - exit("Unable to connect to the server.") + exit("Unable to connect to the Airtime server.") dest = stor+"organize/" copy_or_move_files_to(parser.rargs, dest, 'move') def WatchAddAction(option, opt, value, parser): errorIfMultipleOption(parser.rargs) if(len(parser.rargs) > 1): - raise OptionValueError("Too many arguments. This option need exactly one argument.") + raise OptionValueError("Too many arguments. This option requires exactly one argument.") elif(len(parser.rargs) == 0 ): - raise OptionValueError("No argument found. This option need exactly one argument.") + raise OptionValueError("No argument found. This option requires exactly one argument.") path = parser.rargs[0] if(os.path.isdir(path)): res = api_client.add_watched_dir(path) @@ -144,17 +144,17 @@ def WatchAddAction(option, opt, value, parser): if(res['msg']['code'] == 0): print "%s added to watched folder list successfully" % path else: - print "Adding a watched folder failed. : %s" % res['msg']['error'] + print "Adding a watched folder failed: %s" % res['msg']['error'] else: print "Given path is not a directory: %s" % path def WatchListAction(option, opt, value, parser): errorIfMultipleOption(parser.rargs) if(len(parser.rargs) > 0): - raise OptionValueError("This option doesn't take any argument.") + raise OptionValueError("This option doesn't take any arguments.") res = api_client.list_all_watched_dirs() if(res is None): - exit("Unable to connect to the server.") + exit("Unable to connect to the Airtime server.") dirs = res["dirs"].items() # there will be always 1 which is storage folder if(len(dirs) == 1): @@ -167,21 +167,21 @@ def WatchListAction(option, opt, value, parser): def WatchRemoveAction(option, opt, value, parser): errorIfMultipleOption(parser.rargs) if(len(parser.rargs) > 1): - raise OptionValueError("Too many arguments. This option need exactly one argument.") + raise OptionValueError("Too many arguments. This option requires exactly one argument.") elif(len(parser.rargs) == 0 ): - raise OptionValueError("No argument found. This option need exactly one argument.") + raise OptionValueError("No argument found. This option requires exactly one argument.") path = parser.rargs[0] if(os.path.isdir(path)): res = api_client.remove_watched_dir(path) if(res is None): - exit("Unable to connect to the server.") + exit("Unable to connect to the Airtime server.") # sucess if(res['msg']['code'] == 0): - print "%s removed from watched folder list successfully" % path + print "%s removed from watch folder list successfully." % path else: - print "Removing a watched folder failed. : %s" % res['msg']['error'] + print "Removing the watch folder failed: %s" % res['msg']['error'] else: - print "Given path is not a directory: %s" % path + print "The given path is not a directory: %s" % path def StorageSetAction(option, opt, value, parser): bypass = False @@ -196,41 +196,41 @@ def StorageSetAction(option, opt, value, parser): if(not bypass): errorIfMultipleOption(parser.rargs, "Only [-f] and [--force] option is allowed with this option.") possibleInput = {'y','Y','n','N'} - confirm = raw_input("Are you sure you want to change the storage direcory? (y/N)") + confirm = raw_input("Are you sure you want to change the storage direcory? (y/N) ") confirm = confirm or 'N' while(confirm not in possibleInput): - print "Not an acceptable input: %s" % confirm - confirm = raw_input("Are you sure you want to change the storage direcory? (y/N)") + print "Not an acceptable input: %s\n" % confirm + confirm = raw_input("Are you sure you want to change the storage direcory? (y/N) ") confirm = confirm or 'N' if(confirm == 'n' or confirm =='N'): sys.exit(1) if(len(parser.rargs) > 1): - raise OptionValueError("Too many arguments. This option need exactly one argument.") + raise OptionValueError("Too many arguments. This option requires exactly one argument.") elif(len(parser.rargs) == 0 ): - raise OptionValueError("No argument found. This option need exactly one argument.") + raise OptionValueError("No argument found. This option requires exactly one argument.") path = parser.rargs[0] if(os.path.isdir(path)): res = api_client.set_storage_dir(path) if(res is None): - exit("Unable to connect to the server.") + exit("Unable to connect to the Airtime server.") # sucess if(res['msg']['code'] == 0): print "Successfully set storage folder to %s" % path else: - print "Setting storage folder to failed.: %s" % res['msg']['error'] + print "Setting storage folder failed: %s" % res['msg']['error'] else: - print "Given path is not a directory: %s" % path + print "The given path is not a directory: %s" % path def StorageGetAction(option, opt, value, parser): errorIfMultipleOption(parser.rargs) if(len(parser.rargs) > 0): - raise OptionValueError("This option doesn't take any argument.") + raise OptionValueError("This option does not take any arguments.") print helper_get_stor_dir() usage = """[-c|--copy FILE/DIR [FILE/DIR...]] [-m|--move FILE/DIR [FILE/DIR...]] - [--watch-add DIR] [--watch-list] [--watch-remve DIR] + [--watch-add DIR] [--watch-list] [--watch-remove DIR] [--storage-dir-set DIR] [--storage-dir-get]""" parser = OptionParser(usage=usage, add_help_option=False) From c7279f74ca43d568e6a2d5f49e13c9362739b64c Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Mon, 18 Jul 2011 19:48:20 +0200 Subject: [PATCH 06/21] CC-2558 : Only delete files from disk that are in stor directory and not scheduled etc. --- .../controllers/LibraryController.php | 61 +++++++++++-------- .../controllers/PreferenceController.php | 6 +- airtime_mvc/application/models/MusicDir.php | 26 ++++++-- airtime_mvc/application/models/StoredFile.php | 13 +--- .../public/js/airtime/library/library.js | 44 ++++++------- .../airtimefilemonitor/airtimeprocessevent.py | 3 + 6 files changed, 87 insertions(+), 66 deletions(-) diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index 4090c8481..ce2f36c86 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -63,12 +63,15 @@ class LibraryController extends Zend_Controller_Action $paramsPop = str_replace('#id#', $id, $params); $paramsPop = str_replace('#type#', $type, $paramsPop); + $userInfo = Zend_Auth::getInstance()->getStorage()->read(); + $user = new User($userInfo->id); + $pl_sess = $this->pl_sess; if($type === "au") { if(isset($pl_sess->id)) { - $menu[] = array('action' => array('type' => 'ajax', 'url' => '/Playlist/add-item'.$params, 'callback' => 'window["setSPLContent"]'), + $menu[] = array('action' => array('type' => 'ajax', 'url' => '/Playlist/add-item'.$params, 'callback' => 'window["setSPLContent"]'), 'title' => 'Add to Playlist'); } @@ -85,9 +88,12 @@ class LibraryController extends Zend_Controller_Action $menu[] = array('action' => array('type' => 'gourl', 'url' => $url), 'title' => 'Download'); - $menu[] = array('action' => array('type' => 'fn', - 'callback' => "window['confirmDeleteAudioClip']('$paramsPop')"), - 'title' => 'Delete'); + + if ($user->isAdmin()) { + $menu[] = array('action' => array('type' => 'fn', + 'callback' => "window['confirmDeleteAudioClip']('$paramsPop')"), + 'title' => 'Delete'); + } } else if($type === "pl") { @@ -121,32 +127,37 @@ class LibraryController extends Zend_Controller_Action public function deleteAction() { $id = $this->_getParam('id'); + $userInfo = Zend_Auth::getInstance()->getStorage()->read(); + $user = new User($userInfo->id); - if (!is_null($id)) { - $file = StoredFile::Recall($id); + if ($user->isAdmin()) { - if (PEAR::isError($file)) { - $this->view->message = $file->getMessage(); - return; - } - else if(is_null($file)) { - $this->view->message = "file doesn't exist"; - return; + if (!is_null($id)) { + $file = StoredFile::Recall($id); + + if (PEAR::isError($file)) { + $this->view->message = $file->getMessage(); + return; + } + else if(is_null($file)) { + $this->view->message = "file doesn't exist"; + return; + } + + $res = $file->delete(); + + if (PEAR::isError($res)) { + $this->view->message = $res->getMessage(); + return; + } + else { + $data = array("filepath" => $file->getFilePath(), "delete" => $res); + RabbitMq::SendMessageToMediaMonitor("file_delete", $data); + } } - $res = $file->delete(); - - if (PEAR::isError($res)) { - $this->view->message = $res->getMessage(); - return; - } - else { - $data = array("filepath" => $file->getFilePath(), "delete" => $res); - RabbitMq::SendMessageToMediaMonitor("file_delete", $data); - } + $this->view->id = $id; } - - $this->view->id = $id; } public function contentsAction() diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 5452401ae..f4c106fe7 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -123,9 +123,10 @@ class PreferenceController extends Zend_Controller_Action $chosen = $this->getRequest()->getParam("dir"); $element = $this->getRequest()->getParam("element"); $watched_dirs_form = new Application_Form_WatchedDirPreferences(); - $watched_dirs_form->populate(array('storageFolder' => $chosen)); + $res = MusicDir::setStorDir($chosen); if($res['code'] != 0){ + $watched_dirs_form->populate(array('storageFolder' => $chosen)); $watched_dirs_form->getElement($element)->setErrors(array($res['error'])); } @@ -137,9 +138,10 @@ class PreferenceController extends Zend_Controller_Action $chosen = $this->getRequest()->getParam("dir"); $element = $this->getRequest()->getParam("element"); $watched_dirs_form = new Application_Form_WatchedDirPreferences(); - $watched_dirs_form->populate(array('watchedFolder' => $chosen)); + $res = MusicDir::addWatchedDir($chosen); if($res['code'] != 0){ + $watched_dirs_form->populate(array('watchedFolder' => $chosen)); $watched_dirs_form->getElement($element)->setErrors(array($res['error'])); } diff --git a/airtime_mvc/application/models/MusicDir.php b/airtime_mvc/application/models/MusicDir.php index 2c66c5ffd..1ae298b16 100644 --- a/airtime_mvc/application/models/MusicDir.php +++ b/airtime_mvc/application/models/MusicDir.php @@ -40,7 +40,23 @@ class MusicDir { public function remove() { + global $CC_DBC; + + $music_dir_id = $this->getId(); + + $sql = "SELECT DISTINCT s.instance_id from cc_music_dirs as md LEFT JOIN cc_files as f on f.directory = md.id + RIGHT JOIN cc_schedule as s on s.file_id = f.id WHERE md.id = $music_dir_id"; + + $show_instances = $CC_DBC->GetAll($sql); + $this->_dir->delete(); + + foreach ($show_instances as $show_instance_row) { + $temp_show = new ShowInstance($show_instance_row["instance_id"]); + $temp_show->updateScheduledTime(); + } + + RabbitMq::PushSchedule(); } public static function addDir($p_path, $p_type) @@ -60,7 +76,7 @@ class MusicDir { //echo $e->getMessage(); return array("code"=>1, "error"=>"'$p_path' is already set as the current storage dir or in the watched folders list"); } - + } public static function addWatchedDir($p_path) @@ -123,8 +139,8 @@ class MusicDir { $mus_dir = new MusicDir($dir); return $mus_dir; - } - + } + public static function setStorDir($p_dir) { if(!is_dir($p_dir)){ @@ -162,7 +178,7 @@ class MusicDir { return null; } - + public static function removeWatchedDir($p_dir){ $p_dir = realpath($p_dir)."/"; $dir = MusicDir::getDirByPath($p_dir); @@ -180,7 +196,7 @@ class MusicDir { public static function splitFilePath($p_filepath) { $mus_dir = self::getWatchedDirFromFilepath($p_filepath); - + if(is_null($mus_dir)) { return null; } diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index f162a4a06..aa711094d 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -341,18 +341,7 @@ class StoredFile { return PEAR::raiseError('Cannot delete a file that is scheduled in the future.'); } - $storageDir = MusicDir::getStorDir()->getDirectory(); - $dirCompare = substr($this->getFilePath(), 0, strlen($storageDir)); - - //return PEAR::raiseError("({$storageDir} , {$dirCompare})"); - - // Only delete the file from filesystem if it has been copied to the storage directory - if ($dirCompare === $storageDir) { - return true; - } - else { - return false; - } + return true; } /** diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 11f1610b5..820373752 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -1,12 +1,12 @@ //used by jjmenu -function getId() { +function getId() { var tr_id = $(this.triggerElement).attr("id"); tr_id = tr_id.split("_"); return tr_id[1]; } -function getType() { +function getType() { var tr_id = $(this.triggerElement).attr("id"); tr_id = tr_id.split("_"); @@ -25,8 +25,8 @@ function deleteItem(type, id) { } function deleteAudioClip(json) { - if(json.message) { - alert(json.message); + if(json.message) { + alert(json.message); return; } @@ -35,13 +35,13 @@ function deleteAudioClip(json) { //callbacks called by jjmenu function confirmDeleteAudioClip(params){ - if(confirm('Are you sure you want to delete?')){ + if(confirm('The file will be deleted from disk, are you sure you want to delete?')){ var url = '/Library/delete' + params; $.ajax({ url: url, success: deleteAudioClip }); - } + } } //callbacks called by jjmenu @@ -52,12 +52,12 @@ function confirmDeletePlaylist(params){ url: url, success: deletePlaylist }); - } + } } function deletePlaylist(json) { - if(json.message) { - alert(json.message); + if(json.message) { + alert(json.message); return; } @@ -69,15 +69,15 @@ function deletePlaylist(json) { function addLibraryItemEvents() { $('#library_display tr[id ^= "au"]') - .draggable({ + .draggable({ helper: 'clone', cursor: 'pointer' }); $('#library_display tbody tr') - .jjmenu("click", - [{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}], - {id: getId, type: getType}, + .jjmenu("click", + [{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}], + {id: getId, type: getType}, {xposition: "mouse", yposition: "mouse"}); } @@ -96,7 +96,7 @@ function dtRowCallback( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { } $(nRow).attr("id", type+'_'+id); - + // insert id on lenth field $('td:eq(4)', nRow).attr("id", "length"); @@ -111,14 +111,14 @@ function dtRowCallback( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { button: 'Close' // Show a close link in the title } }, - + position: { - + adjust: { screen: true // Keep the tooltip on-screen at all times } }, - + style: { border: { width: 0, @@ -146,16 +146,16 @@ $(document).ready(function() { "sAjaxSource": "/Library/contents/format/json", "fnServerData": function ( sSource, aoData, fnCallback ) { $.ajax( { - "dataType": 'json', - "type": "POST", - "url": sSource, - "data": aoData, + "dataType": 'json', + "type": "POST", + "url": sSource, + "data": aoData, "success": fnCallback } ); }, "fnRowCallback": dtRowCallback, "fnDrawCallback": dtDrawCallback, - "aoColumns": [ + "aoColumns": [ /* Id */ { "sName": "id", "bSearchable": false, "bVisible": false }, /* Title */ { "sName": "track_title" }, /* Creator */ { "sName": "artist_name" }, diff --git a/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py b/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py index 322cd7f0e..704b1abc6 100644 --- a/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py +++ b/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py @@ -57,6 +57,9 @@ class AirtimeProcessEvent(ProcessEvent): #file created is a tmp file which will be modified and then moved back to the original filename. #Easy Tag creates this when changing metadata of ogg files. self.temp_files[pathname] = None + #file is being overwritten/replaced in GUI. + elif "goutputstream" in pathname: + self.temp_files[pathname] = None elif self.mmc.is_audio_file(pathname): if self.mmc.is_parent_directory(pathname, self.config.organize_directory): #file was created in /srv/airtime/stor/organize. Need to process and move From 7cf8934856d278f7611f72ce71338967d314a2c0 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Jul 2011 15:09:27 -0400 Subject: [PATCH 07/21] CC-2546: Airtime-import does not confirm - fix on error. wrong syntax was used --- utils/airtime-import/airtime-import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/airtime-import/airtime-import.py b/utils/airtime-import/airtime-import.py index c5b29036c..7ea4ae7fc 100644 --- a/utils/airtime-import/airtime-import.py +++ b/utils/airtime-import/airtime-import.py @@ -195,7 +195,7 @@ def StorageSetAction(option, opt, value, parser): parser.rargs.remove('--force') if(not bypass): errorIfMultipleOption(parser.rargs, "Only [-f] and [--force] option is allowed with this option.") - possibleInput = {'y','Y','n','N'} + possibleInput = ['y','Y','n','N'] confirm = raw_input("Are you sure you want to change the storage direcory? (y/N)") confirm = confirm or 'N' while(confirm not in possibleInput): From 0e82f2a6062faa73055c2bec6610933d25cdd29e Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 15:27:22 -0400 Subject: [PATCH 08/21] CC-2560: Make sure new watched dirs do not contain existing watched dirs (and stor dir) and vice-versa. -fixed --- airtime_mvc/application/models/MusicDir.php | 71 +++++++++++++++++++-- python_apps/media-monitor/MediaMonitor.py | 2 + 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/airtime_mvc/application/models/MusicDir.php b/airtime_mvc/application/models/MusicDir.php index 1ae298b16..2af522169 100644 --- a/airtime_mvc/application/models/MusicDir.php +++ b/airtime_mvc/application/models/MusicDir.php @@ -1,5 +1,7 @@ strlen($p_dir2)){ + return false; + } + + return substr($p_dir2, 0, strlen($p_dir1)) == $p_dir1; + } + + /** + * Checks whether the path provided is a valid path. A valid path + * is defined as not being nested within an existing watched directory, + * or vice-versa. Throws a NestedDirectoryException if invalid. + * + * @param string $p_path + * The path we want to validate + * @return void + */ + public static function isPathValid($p_path){ + $dirs = self::getWatchedDirs(); + $dirs[] = self::getStorDir(); + + foreach ($dirs as $dirObj){ + $dir = $dirObj->getDirectory(); + $diff = strlen($dir) - strlen($p_path); + if ($diff == 0){ + if ($dir == $p_path){ + throw new NestedDirectoryException("'$p_path' is already watched."); + } + } else if ($diff > 0){ + if (self::isAncestorDir($p_path, $dir)){ + throw new NestedDirectoryException("'$p_path' contains nested watched directory: '$dir'"); + } + } else { /* diff < 0*/ + if (self::isAncestorDir($dir, $p_path)){ + throw new NestedDirectoryException("'$p_path' is nested within existing watched directory: '$dir'"); + } + } + } + } public static function addDir($p_path, $p_type) { @@ -67,13 +121,20 @@ class MusicDir { $dir = new CcMusicDirs(); $dir->setType($p_type); $p_path = realpath($p_path)."/"; - $temp = $dir->setDirectory($p_path); - try{ + + + try { + /* isPathValid() checks if path is a substring or a superstring of an + * existing dir and if not, throws NestedDirectoryException */ + self::isPathValid($p_path); + $dir->setDirectory($p_path); + $dir->save(); return array("code"=>0); - } - catch(Exception $e){ - //echo $e->getMessage(); + } catch (NestedDirectoryException $nde){ + $msg = $nde->getMessage(); + return array("code"=>1, "error"=>"$msg"); + } catch(Exception $e){ return array("code"=>1, "error"=>"'$p_path' is already set as the current storage dir or in the watched folders list"); } diff --git a/python_apps/media-monitor/MediaMonitor.py b/python_apps/media-monitor/MediaMonitor.py index a4e89c725..f7f67516b 100644 --- a/python_apps/media-monitor/MediaMonitor.py +++ b/python_apps/media-monitor/MediaMonitor.py @@ -30,6 +30,8 @@ except Exception, e: logger = logging.getLogger() +logger.info("\n\n*** Media Monitor bootup ***\n\n") + try: config = AirtimeMediaConfig(logger) api_client = api_client.api_client_factory(config.cfg) From aa1039413a4db622e4d0785ddbac31862846ca57 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 16:35:12 -0400 Subject: [PATCH 09/21] CC-2279: Upgrade script for converting stor directory to new format -set mm api_key after upgrading --- install/upgrades/airtime-1.9.0/airtime-upgrade.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/upgrades/airtime-1.9.0/airtime-upgrade.php b/install/upgrades/airtime-1.9.0/airtime-upgrade.php index ebd63ba76..0fb9a70e7 100644 --- a/install/upgrades/airtime-1.9.0/airtime-upgrade.php +++ b/install/upgrades/airtime-1.9.0/airtime-upgrade.php @@ -332,7 +332,7 @@ class AirtimeIni{ const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg"; const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg"; const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg"; - + /** * This function updates an INI style config file. * @@ -565,6 +565,8 @@ function installMediaMonitor($values){ if (!copy(__DIR__."/../../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){ echo "Could not copy media-monitor.cfg to /etc/airtime/. Exiting."; } + + AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_MEDIAMONITOR, "api_key", $values["general"]["api_key"]); echo "Reorganizing files in stor directory"; $mediaMonitorUpgradePath = realpath(__DIR__."/../../../python_apps/media-monitor/media-monitor-upgrade.py"); From 232470c66b91850db05de8151633b7990296a77b Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 16:50:42 -0400 Subject: [PATCH 10/21] CC-2279: Upgrade script for converting stor directory to new format -make every function a static function and put into a class --- .../airtime-1.9.0/airtime-upgrade.php | 217 +++++++++--------- 1 file changed, 111 insertions(+), 106 deletions(-) diff --git a/install/upgrades/airtime-1.9.0/airtime-upgrade.php b/install/upgrades/airtime-1.9.0/airtime-upgrade.php index 0fb9a70e7..ae6b7b88b 100644 --- a/install/upgrades/airtime-1.9.0/airtime-upgrade.php +++ b/install/upgrades/airtime-1.9.0/airtime-upgrade.php @@ -321,7 +321,7 @@ class AirtimeInstall{ INSERT INTO cc_country (isocode, name) VALUES ('ZWE', 'Zimbabwe ');"; echo "* Inserting data into country table".PHP_EOL; - execSqlQuery($sql); + Airtime190Upgrade::execSqlQuery($sql); } } @@ -420,7 +420,7 @@ class AirtimeIni{ } } - function upgradeConfigFiles(){ + public static function upgradeConfigFiles(){ $configFiles = array(AirtimeIni::CONF_FILE_AIRTIME, AirtimeIni::CONF_FILE_PYPO, @@ -443,7 +443,7 @@ class AirtimeIni{ * This function creates the /etc/airtime configuration folder * and copies the default config files to it. */ - function CreateIniFiles() + public static function CreateIniFiles() { if (!file_exists("/etc/airtime/")){ if (!mkdir("/etc/airtime/", 0755, true)){ @@ -474,117 +474,122 @@ class AirtimeIni{ } } -function InstallAirtimePhpServerCode($phpDir) -{ +class Airtime190Upgrade{ - $AIRTIME_SRC = realpath(__DIR__.'/../../../airtime_mvc'); + public static function InstallAirtimePhpServerCode($phpDir) + { - echo "* Installing PHP code to ".$phpDir.PHP_EOL; - exec("mkdir -p ".$phpDir); - exec("cp -R ".$AIRTIME_SRC."/* ".$phpDir); -} + $AIRTIME_SRC = realpath(__DIR__.'/../../../airtime_mvc'); -function CopyUtils() -{ - $utilsSrc = __DIR__."/../../../utils"; - - echo "* Installing binaries to ".CONF_DIR_BINARIES.PHP_EOL; - exec("mkdir -p ".CONF_DIR_BINARIES); - exec("cp -R ".$utilsSrc." ".CONF_DIR_BINARIES); -} - -/* Removes pypo, media-monitor, show-recorder and utils from system. These will - * be reinstalled by the main airtime-upgrade script. - */ -function UninstallBinaries() -{ - echo "* Removing Airtime binaries from ".CONF_DIR_BINARIES.PHP_EOL; - exec('rm -rf "'.CONF_DIR_BINARIES.'"'); -} - - -function removeOldAirtimeImport(){ - exec('rm -f "/usr/bin/airtime-import"'); - exec('rm -f "/usr/lib/airtime/utils/airtime-import.php"'); - exec('rm -rf "/usr/lib/airtime/utils/airtime-import"'); -} - -function updateAirtimeImportSymLink(){ - $dir = "/usr/lib/airtime/utils/airtime-import/airtime-import"; - exec("ln -s $dir /usr/bin/airtime-import"); -} - -function execSqlQuery($sql){ - global $CC_DBC; - - $result = $CC_DBC->query($sql); - if (PEAR::isError($result)) { - echo "* Failed sql query: $sql".PHP_EOL; - echo "* Message {$result->getMessage()}".PHP_EOL; + echo "* Installing PHP code to ".$phpDir.PHP_EOL; + exec("mkdir -p ".$phpDir); + exec("cp -R ".$AIRTIME_SRC."/* ".$phpDir); } - - return $result; -} -function connectToDatabase(){ - global $CC_DBC, $CC_CONFIG; + public static function CopyUtils() + { + $utilsSrc = __DIR__."/../../../utils"; - $values = parse_ini_file('/etc/airtime/airtime.conf', true); - - // Database config - $CC_CONFIG['dsn']['username'] = $values['database']['dbuser']; - $CC_CONFIG['dsn']['password'] = $values['database']['dbpass']; - $CC_CONFIG['dsn']['hostspec'] = $values['database']['host']; - $CC_CONFIG['dsn']['phptype'] = 'pgsql'; - $CC_CONFIG['dsn']['database'] = $values['database']['dbname']; - - $CC_DBC = DB::connect($CC_CONFIG['dsn'], FALSE); -} - -/* Old database had a "fullpath" column that stored the absolute path of each track. We have to - * change it so that the "fullpath" column has path relative to the "directory" column. - */ -function installMediaMonitor($values){ - - /* Handle Database Changes. */ - $stor_dir = realpath($values['general']['base_files_dir']."/stor")."/"; - echo "* Inserting stor directory location $stor_dir into music_dirs table".PHP_EOL; - $sql = "UPDATE cc_music_dirs SET directory='$stor_dir' WHERE type='stor'"; - echo $sql.PHP_EOL; - execSqlQuery($sql); - - $sql = "SELECT id FROM cc_music_dirs WHERE type='stor'"; - echo $sql.PHP_EOL; - $rows = execSqlQuery($sql); - - echo "Creating media-monitor log file"; - mkdir("/var/log/airtime/media-monitor/", 755, true); - touch("/var/log/airtime/media-monitor/media-monitor.log"); - - /* create media monitor config: */ - if (!copy(__DIR__."/../../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){ - echo "Could not copy media-monitor.cfg to /etc/airtime/. Exiting."; + echo "* Installing binaries to ".CONF_DIR_BINARIES.PHP_EOL; + exec("mkdir -p ".CONF_DIR_BINARIES); + exec("cp -R ".$utilsSrc." ".CONF_DIR_BINARIES); } - - AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_MEDIAMONITOR, "api_key", $values["general"]["api_key"]); - echo "Reorganizing files in stor directory"; - $mediaMonitorUpgradePath = realpath(__DIR__."/../../../python_apps/media-monitor/media-monitor-upgrade.py"); - exec("su -c \"python $mediaMonitorUpgradePath\"", $output); - print_r($output); + /* Removes pypo, media-monitor, show-recorder and utils from system. These will + * be reinstalled by the main airtime-upgrade script. + */ + public static function UninstallBinaries() + { + echo "* Removing Airtime binaries from ".CONF_DIR_BINARIES.PHP_EOL; + exec('rm -rf "'.CONF_DIR_BINARIES.'"'); + } - $oldAndNewFileNames = json_decode($output[0]); - foreach ($oldAndNewFileNames as $pair){ - $relPathNew = pg_escape_string(substr($pair[1], strlen($stor_dir))); - $absPathOld = pg_escape_string($pair[0]); - $sql = "UPDATE cc_files SET filepath = '$relPathNew', directory=1 WHERE filepath = '$absPathOld'"; + public static function removeOldAirtimeImport(){ + exec('rm -f "/usr/bin/airtime-import"'); + exec('rm -f "/usr/lib/airtime/utils/airtime-import.php"'); + exec('rm -rf "/usr/lib/airtime/utils/airtime-import"'); + } + + public static function updateAirtimeImportSymLink(){ + $dir = "/usr/lib/airtime/utils/airtime-import/airtime-import"; + exec("ln -s $dir /usr/bin/airtime-import"); + } + + public static function execSqlQuery($sql){ + global $CC_DBC; + + $result = $CC_DBC->query($sql); + if (PEAR::isError($result)) { + echo "* Failed sql query: $sql".PHP_EOL; + echo "* Message {$result->getMessage()}".PHP_EOL; + } + + return $result; + } + + public static function connectToDatabase(){ + global $CC_DBC, $CC_CONFIG; + + $values = parse_ini_file('/etc/airtime/airtime.conf', true); + + // Database config + $CC_CONFIG['dsn']['username'] = $values['database']['dbuser']; + $CC_CONFIG['dsn']['password'] = $values['database']['dbpass']; + $CC_CONFIG['dsn']['hostspec'] = $values['database']['host']; + $CC_CONFIG['dsn']['phptype'] = 'pgsql'; + $CC_CONFIG['dsn']['database'] = $values['database']['dbname']; + + $CC_DBC = DB::connect($CC_CONFIG['dsn'], FALSE); + } + + /* Old database had a "fullpath" column that stored the absolute path of each track. We have to + * change it so that the "fullpath" column has path relative to the "directory" column. + */ + public static function installMediaMonitor($values){ + + /* Handle Database Changes. */ + $stor_dir = realpath($values['general']['base_files_dir']."/stor")."/"; + echo "* Inserting stor directory location $stor_dir into music_dirs table".PHP_EOL; + $sql = "UPDATE cc_music_dirs SET directory='$stor_dir' WHERE type='stor'"; echo $sql.PHP_EOL; - execSqlQuery($sql); + Airtime190Upgrade::execSqlQuery($sql); + + $sql = "SELECT id FROM cc_music_dirs WHERE type='stor'"; + echo $sql.PHP_EOL; + $rows = Airtime190Upgrade::execSqlQuery($sql); + + echo "Creating media-monitor log file"; + mkdir("/var/log/airtime/media-monitor/", 755, true); + touch("/var/log/airtime/media-monitor/media-monitor.log"); + + /* create media monitor config: */ + if (!copy(__DIR__."/../../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){ + echo "Could not copy media-monitor.cfg to /etc/airtime/. Exiting."; + } + + AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_MEDIAMONITOR, "api_key", $values["general"]["api_key"]); + + echo "Reorganizing files in stor directory"; + $mediaMonitorUpgradePath = realpath(__DIR__."/../../../python_apps/media-monitor/media-monitor-upgrade.py"); + exec("su -c \"python $mediaMonitorUpgradePath\"", $output); + print_r($output); + + $oldAndNewFileNames = json_decode($output[0]); + + foreach ($oldAndNewFileNames as $pair){ + $relPathNew = pg_escape_string(substr($pair[1], strlen($stor_dir))); + $absPathOld = pg_escape_string($pair[0]); + $sql = "UPDATE cc_files SET filepath = '$relPathNew', directory=1 WHERE filepath = '$absPathOld'"; + echo $sql.PHP_EOL; + Airtime190Upgrade::execSqlQuery($sql); + } } } + + AirtimeInstall::CreateZendPhpLogFile(); /* In version 1.9.0 we have have switched from daemontools to more traditional @@ -624,19 +629,19 @@ foreach ($pathnames as $pn){ /* update Airtime Server PHP files */ $values = parse_ini_file(AirtimeIni::CONF_FILE_AIRTIME, true); $phpDir = $values['general']['airtime_dir']; -InstallAirtimePhpServerCode($phpDir); +Airtime190Upgrade::InstallAirtimePhpServerCode($phpDir); /* update utils (/usr/lib/airtime) folder */ -UninstallBinaries(); -CopyUtils(); +Airtime190Upgrade::UninstallBinaries(); +Airtime190Upgrade::CopyUtils(); /* James made a new airtime-import script, lets remove the old airtime-import php script, *install the new airtime-import.py script and update the /usr/bin/symlink. */ -removeOldAirtimeImport(); -updateAirtimeImportSymLink(); +Airtime190Upgrade::removeOldAirtimeImport(); +Airtime190Upgrade::updateAirtimeImportSymLink(); -connectToDatabase(); +Airtime190Upgrade::connectToDatabase(); if(AirtimeInstall::DbTableExists('doctrine_migration_versions') === false) { $migrations = array('20110312121200', '20110331111708', '20110402164819', '20110406182005'); @@ -654,7 +659,7 @@ AirtimeInstall::InsertCountryDataIntoDatabase(); /* create cron file for phone home stat */ AirtimeInstall::CreateCronFile(); -installMediaMonitor($values); +Airtime190Upgrade::installMediaMonitor($values); AirtimeIni::upgradeConfigFiles(); From 8ca2eec3e05e4ee0b528892d9e3c5442681aa761 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 17:09:03 -0400 Subject: [PATCH 11/21] CC-2561: Broken image links when selecting directory from server. -fixed --- airtime_mvc/public/js/airtime/preferences/musicdirs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/public/js/airtime/preferences/musicdirs.js b/airtime_mvc/public/js/airtime/preferences/musicdirs.js index 0803cfd86..a71e7aa31 100644 --- a/airtime_mvc/public/js/airtime/preferences/musicdirs.js +++ b/airtime_mvc/public/js/airtime/preferences/musicdirs.js @@ -33,7 +33,7 @@ function setWatchedDirEvents() { //knownPaths: [{text:'Desktop', image:'desktop.png', path:'/home'}], knownPaths: [], imageUrl: 'img/icons/', - systemImageUrl: 'img/browser/', + systemImageUrl: '/css/img/', handlerUrl: '/Preference/server-browse/format/json', title: 'Choose Folder to Watch', basePath: '', From bd183125da21d3f481ca3bd93628ca57aa773dd5 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 18 Jul 2011 17:18:21 -0400 Subject: [PATCH 12/21] CC-2279: Upgrade script for converting stor directory to new format -move media-monitor python upgrade script to upgrade dir --- .../airtimefilemonitor/mediaconfig.py | 24 ++ .../airtimefilemonitor/mediamonitorcommon.py | 243 ++++++++++++++++++ install/upgrades/airtime-1.9.0/logging.cfg | 22 ++ .../airtime-1.9.0}/media-monitor-upgrade.py | 0 4 files changed, 289 insertions(+) create mode 100644 install/upgrades/airtime-1.9.0/airtimefilemonitor/mediaconfig.py create mode 100644 install/upgrades/airtime-1.9.0/airtimefilemonitor/mediamonitorcommon.py create mode 100644 install/upgrades/airtime-1.9.0/logging.cfg rename {python_apps/media-monitor => install/upgrades/airtime-1.9.0}/media-monitor-upgrade.py (100%) diff --git a/install/upgrades/airtime-1.9.0/airtimefilemonitor/mediaconfig.py b/install/upgrades/airtime-1.9.0/airtimefilemonitor/mediaconfig.py new file mode 100644 index 000000000..2b1197e24 --- /dev/null +++ b/install/upgrades/airtime-1.9.0/airtimefilemonitor/mediaconfig.py @@ -0,0 +1,24 @@ +import sys + +from configobj import ConfigObj + +class AirtimeMediaConfig: + + MODE_CREATE = "create" + MODE_MODIFY = "modify" + MODE_MOVED = "moved" + MODE_DELETE = "delete" + + def __init__(self, logger): + + # loading config file + try: + config = ConfigObj('/etc/airtime/media-monitor.cfg') + self.cfg = config + except Exception, e: + logger.info('Error loading config: ', e) + sys.exit() + + self.storage_directory = None + + diff --git a/install/upgrades/airtime-1.9.0/airtimefilemonitor/mediamonitorcommon.py b/install/upgrades/airtime-1.9.0/airtimefilemonitor/mediamonitorcommon.py new file mode 100644 index 000000000..d3125c5b5 --- /dev/null +++ b/install/upgrades/airtime-1.9.0/airtimefilemonitor/mediamonitorcommon.py @@ -0,0 +1,243 @@ +import os +import grp +import pwd +import logging + +from subprocess import Popen, PIPE +from airtimemetadata import AirtimeMetadata + +class MediaMonitorCommon: + + timestamp_file = "/var/tmp/airtime/last_index" + + def __init__(self, airtime_config): + self.supported_file_formats = ['mp3', 'ogg'] + self.logger = logging.getLogger() + self.config = airtime_config + self.md_manager = AirtimeMetadata() + + def is_parent_directory(self, filepath, directory): + filepath = os.path.normpath(filepath) + directory = os.path.normpath(directory) + return (directory == filepath[0:len(directory)]) + + def is_temp_file(self, filename): + info = filename.split(".") + + if(info[-2] in self.supported_file_formats): + return True + else: + return False + + def is_audio_file(self, filename): + info = filename.split(".") + + if(info[-1] in self.supported_file_formats): + return True + else: + return False + + #check if file is readable by "nobody" + def has_correct_permissions(self, filepath): + #drop root permissions and become "nobody" + os.seteuid(65534) + + try: + open(filepath) + readable = True + except IOError: + self.logger.warn("File does not have correct permissions: '%s'", filepath) + readable = False + except Exception, e: + self.logger.error("Unexpected exception thrown: %s", e) + readable = False + finally: + #reset effective user to root + os.seteuid(0) + + return readable + + def set_needed_file_permissions(self, item, is_dir): + try: + omask = os.umask(0) + + uid = pwd.getpwnam('www-data')[2] + gid = grp.getgrnam('www-data')[2] + + os.chown(item, uid, gid) + + if is_dir is True: + os.chmod(item, 02777) + else: + os.chmod(item, 0666) + + except Exception, e: + self.logger.error("Failed to change file's owner/group/permissions. %s", e) + finally: + os.umask(omask) + + + #checks if path is a directory, and if it doesnt exist, then creates it. + #Otherwise prints error to log file. + def ensure_is_dir(self, directory): + try: + omask = os.umask(0) + if not os.path.exists(directory): + os.makedirs(directory, 02777) + elif not os.path.isdir(directory): + #path exists but it is a file not a directory! + self.logger.error("path %s exists, but it is not a directory!!!") + finally: + os.umask(omask) + + #moves file from source to dest but also recursively removes the + #the source file's parent directories if they are now empty. + def move_file(self, source, dest): + + try: + omask = os.umask(0) + os.rename(source, dest) + except Exception, e: + self.logger.error("failed to move file. %s", e) + finally: + os.umask(omask) + + dir = os.path.dirname(source) + self.cleanup_empty_dirs(dir) + + #keep moving up the file hierarchy and deleting parent + #directories until we hit a non-empty directory, or we + #hit the organize dir. + def cleanup_empty_dirs(self, dir): + if os.path.normpath(dir) != self.config.organize_directory: + if len(os.listdir(dir)) == 0: + os.rmdir(dir) + + pdir = os.path.dirname(dir) + self.cleanup_empty_dirs(pdir) + + + #checks if path exists already in stor. If the path exists and the md5s are the + #same just overwrite. + def create_unique_filename(self, filepath, old_filepath): + + try: + if(os.path.exists(filepath)): + self.logger.info("Path %s exists", filepath) + + self.logger.info("Checking if md5s are the same.") + md5_fp = self.md_manager.get_md5(filepath) + md5_ofp = self.md_manager.get_md5(old_filepath) + + if(md5_fp == md5_ofp): + self.logger.info("Md5s are the same, moving to same filepath.") + return filepath + + self.logger.info("Md5s aren't the same, appending to filepath.") + file_dir = os.path.dirname(filepath) + filename = os.path.basename(filepath).split(".")[0] + #will be in the format .ext + file_ext = os.path.splitext(filepath)[1] + i = 1; + while(True): + new_filepath = '%s/%s(%s)%s' % (file_dir, filename, i, file_ext) + self.logger.error("Trying %s", new_filepath) + + if(os.path.exists(new_filepath)): + i = i+1; + else: + filepath = new_filepath + break + + except Exception, e: + self.logger.error("Exception %s", e) + + return filepath + + #create path in /srv/airtime/stor/imported/[song-metadata] + def create_file_path(self, original_path, orig_md): + + storage_directory = self.config.storage_directory + + is_recorded_show = False + + try: + #will be in the format .ext + file_ext = os.path.splitext(original_path)[1] + file_ext = file_ext.encode('utf-8') + + path_md = ['MDATA_KEY_TITLE', 'MDATA_KEY_CREATOR', 'MDATA_KEY_SOURCE', 'MDATA_KEY_TRACKNUMBER', 'MDATA_KEY_BITRATE'] + + md = {} + for m in path_md: + if m not in orig_md: + md[m] = u'unknown'.encode('utf-8') + else: + #get rid of any "/" which will interfere with the filepath. + if isinstance(orig_md[m], basestring): + md[m] = orig_md[m].replace("/", "-") + else: + md[m] = orig_md[m] + + if 'MDATA_KEY_TRACKNUMBER' in orig_md: + #make sure all track numbers are at least 2 digits long in the filepath. + md['MDATA_KEY_TRACKNUMBER'] = "%02d" % (int(md['MDATA_KEY_TRACKNUMBER'])) + + #format bitrate as 128kbps + md['MDATA_KEY_BITRATE'] = str(md['MDATA_KEY_BITRATE']/1000)+"kbps" + + filepath = None + #file is recorded by Airtime + #/srv/airtime/stor/recorded/year/month/year-month-day-time-showname-bitrate.ext + if(md['MDATA_KEY_CREATOR'] == "AIRTIMERECORDERSOURCEFABRIC".encode('utf-8')): + #yyyy-mm-dd-hh-MM-ss + y = orig_md['MDATA_KEY_YEAR'].split("-") + filepath = '%s/%s/%s/%s/%s-%s-%s%s' % (storage_directory, "recorded".encode('utf-8'), y[0], y[1], orig_md['MDATA_KEY_YEAR'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) + elif(md['MDATA_KEY_TRACKNUMBER'] == u'unknown'.encode('utf-8')): + filepath = '%s/%s/%s/%s/%s-%s%s' % (storage_directory, "imported".encode('utf-8'), md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) + else: + filepath = '%s/%s/%s/%s/%s-%s-%s%s' % (storage_directory, "imported".encode('utf-8'), md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TRACKNUMBER'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) + + filepath = self.create_unique_filename(filepath, original_path) + self.logger.info('Unique filepath: %s', filepath) + self.ensure_is_dir(os.path.dirname(filepath)) + + except Exception, e: + self.logger.error('Exception: %s', e) + + return filepath + + def execCommandAndReturnStdOut(self, command): + p = Popen(command, shell=True, stdout=PIPE) + stdout = p.communicate()[0] + if p.returncode != 0: + self.logger.warn("command \n%s\n return with a non-zero return value", command) + return stdout + + def scan_dir_for_new_files(self, dir): + command = 'find "%s" -type f -iname "*.ogg" -o -iname "*.mp3" -readable' % dir.replace('"', '\\"') + self.logger.debug(command) + stdout = self.execCommandAndReturnStdOut(command) + stdout = unicode(stdout, "utf_8") + + return stdout.splitlines() + + def touch_index_file(self): + open(self.timestamp_file, "w") + + def organize_new_file(self, pathname): + self.logger.info("Organizing new file: %s", pathname) + file_md = self.md_manager.get_md_from_file(pathname) + + if file_md is not None: + #is_recorded_show = 'MDATA_KEY_CREATOR' in file_md and \ + # file_md['MDATA_KEY_CREATOR'] == "AIRTIMERECORDERSOURCEFABRIC".encode('utf-8') + filepath = self.create_file_path(pathname, file_md) + + self.logger.debug("Moving from %s to %s", pathname, filepath) + self.move_file(pathname, filepath) + else: + filepath = None + self.logger.warn("File %s, has invalid metadata", pathname) + + return filepath diff --git a/install/upgrades/airtime-1.9.0/logging.cfg b/install/upgrades/airtime-1.9.0/logging.cfg new file mode 100644 index 000000000..c0a43564c --- /dev/null +++ b/install/upgrades/airtime-1.9.0/logging.cfg @@ -0,0 +1,22 @@ +[loggers] +keys=root + +[handlers] +keys=fileOutHandler + +[formatters] +keys=simpleFormatter + +[logger_root] +level=DEBUG +handlers=fileOutHandler + +[handler_fileOutHandler] +class=logging.handlers.RotatingFileHandler +level=DEBUG +formatter=simpleFormatter +args=("/var/log/airtime/media-monitor/media-monitor.log", 'a', 1000000, 5,) + +[formatter_simpleFormatter] +format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s +datefmt= diff --git a/python_apps/media-monitor/media-monitor-upgrade.py b/install/upgrades/airtime-1.9.0/media-monitor-upgrade.py similarity index 100% rename from python_apps/media-monitor/media-monitor-upgrade.py rename to install/upgrades/airtime-1.9.0/media-monitor-upgrade.py From d518e6aa15fc52bbd76633dfcb6e3eda890640bf Mon Sep 17 00:00:00 2001 From: James Date: Mon, 18 Jul 2011 17:54:28 -0400 Subject: [PATCH 13/21] - updated version string for RC1 --- VERSION | 2 +- airtime_mvc/application/configs/constants.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index e6be7f73e..5715d0b80 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ PRODUCT_ID=Airtime -PRODUCT_RELEASE=1.9.0-beta4 +PRODUCT_RELEASE=1.9.0-RC1 diff --git a/airtime_mvc/application/configs/constants.php b/airtime_mvc/application/configs/constants.php index 0fc21524d..5a60350a7 100644 --- a/airtime_mvc/application/configs/constants.php +++ b/airtime_mvc/application/configs/constants.php @@ -1,6 +1,6 @@ Date: Mon, 18 Jul 2011 23:25:33 -0400 Subject: [PATCH 14/21] CC-2567: Install api_client separately from pypo -initial commit... --- .../api_clients/install/api_client_install.py | 23 +++++++++++++++++++ python_apps/api_clients/install/config.cfg | 1 + 2 files changed, 24 insertions(+) create mode 100644 python_apps/api_clients/install/api_client_install.py create mode 100644 python_apps/api_clients/install/config.cfg diff --git a/python_apps/api_clients/install/api_client_install.py b/python_apps/api_clients/install/api_client_install.py new file mode 100644 index 000000000..47947c74e --- /dev/null +++ b/python_apps/api_clients/install/api_client_install.py @@ -0,0 +1,23 @@ +import os +import shutil + +def get_current_script_dir(): + return os.path.dirname(os.path.realpath(__file__)) + +def copy_dir(src_dir, dest_dir): + if (os.path.exists(dest_dir)) and (dest_dir != "/"): + shutil.rmtree(dest_dir) + if not (os.path.exists(dest_dir)): + print "Copying directory "+os.path.realpath(src_dir)+" to "+os.path.realpath(dest_dir) + shutil.copytree(src_dir, dest_dir) + +current_script_dir = get_current_script_dir() + +"""load config file""" +try: + config = ConfigObj("current_script_dir/../config.cfg") +except Exception, e: + print 'Error loading config file: ', e + sys.exit(1) + +copy_dir("%s/../../api_clients"%current_script_dir, config["bin_dir"]+"/api_clients/") \ No newline at end of file diff --git a/python_apps/api_clients/install/config.cfg b/python_apps/api_clients/install/config.cfg new file mode 100644 index 000000000..e78abc950 --- /dev/null +++ b/python_apps/api_clients/install/config.cfg @@ -0,0 +1 @@ +bin_dir = "/usr/lib/airtime/api_clients/bin" From ba0458c44235164cc7026dc852ffaed603a04373 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Tue, 19 Jul 2011 11:00:32 +0200 Subject: [PATCH 15/21] CC-2558 : Delete all files from disk, only allow admins to delete. --- airtime_mvc/application/controllers/LibraryController.php | 1 + airtime_mvc/application/models/StoredFile.php | 2 ++ python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index ce2f36c86..67efa7638 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -151,6 +151,7 @@ class LibraryController extends Zend_Controller_Action return; } else { + $res = settype($res, "integer"); $data = array("filepath" => $file->getFilePath(), "delete" => $res); RabbitMq::SendMessageToMediaMonitor("file_delete", $data); } diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index aa711094d..90754b0b6 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -318,6 +318,8 @@ class StoredFile { Playlist::DeleteFileFromAllPlaylists($this->getId()); $this->_file->delete(); + + return $res; } /** diff --git a/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py b/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py index 3af4ac541..acbe3602b 100644 --- a/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py +++ b/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py @@ -112,7 +112,7 @@ class AirtimeNotifier(Notifier): self.logger.info("Adding file to ignore: %s ", filepath) mm.add_filepath_to_ignore(filepath) - if m['delete'] == "true": + if m['delete']: self.logger.info("Deleting file: %s ", filepath) os.unlink(filepath) From 5a9bf18ccc7c159fcf8e4c0fa4fb32601caba146 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Tue, 19 Jul 2011 11:50:49 +0200 Subject: [PATCH 16/21] CC-2558 : Delete all files from disk, only allow admins to delete --- airtime_mvc/application/models/StoredFile.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 90754b0b6..f96c4348f 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -319,7 +319,12 @@ class StoredFile { Playlist::DeleteFileFromAllPlaylists($this->getId()); $this->_file->delete(); - return $res; + if (isset($res)) { + return $res; + } + else { + return false; + } } /** From 9de0d490d875c085ee5ea0d550d91965b9e30263 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Tue, 19 Jul 2011 12:37:06 +0200 Subject: [PATCH 17/21] CC-2569 : Scheduling a rebroadcast show uses old file length access use new metadata key access. --- airtime_mvc/application/models/Schedule.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 26ac92bf4..33abd6d93 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -56,7 +56,7 @@ class ScheduleGroup { // Check if there are any conflicts with existing entries $metadata = $track->getMetadata(); - $length = trim($metadata["length"]); + $length = $metadata['MDATA_KEY_DURATION']; if (empty($length)) { return new PEAR_Error("Length is empty."); } @@ -540,7 +540,7 @@ class Schedule { $retVal = $CC_DBC->query($sql); return $retVal; } - + public static function getSchduledPlaylistCount(){ global $CC_CONFIG, $CC_DBC; $sql = "SELECT count(*) as cnt FROM ".$CC_CONFIG['scheduleTable']; @@ -750,7 +750,7 @@ class Schedule { global $CC_CONFIG, $CC_DBC; $CC_DBC->query("TRUNCATE TABLE ".$CC_CONFIG["scheduleTable"]); } - + public static function createNewFormSections($p_view){ $formWhat = new Application_Form_AddShowWhat(); $formWho = new Application_Form_AddShowWho(); @@ -769,7 +769,7 @@ class Schedule { $formRecord->removeDecorator('DtDdWrapper'); $formAbsoluteRebroadcast->removeDecorator('DtDdWrapper'); $formRebroadcast->removeDecorator('DtDdWrapper'); - + $p_view->what = $formWhat; $p_view->when = $formWhen; $p_view->repeats = $formRepeats; @@ -779,7 +779,7 @@ class Schedule { $p_view->absoluteRebroadcast = $formAbsoluteRebroadcast; $p_view->rebroadcast = $formRebroadcast; $p_view->addNewShow = true; - + $formWhat->populate(array('add_show_id' => '-1')); $formWhen->populate(array('add_show_start_date' => date("Y-m-d"), 'add_show_start_time' => '00:00', From 152c87f099da139d8dccfd461121b2ae31b7bedc Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Tue, 19 Jul 2011 17:45:23 +0200 Subject: [PATCH 18/21] changes to bootstrip for filename. --- .../airtimemediamonitorbootstrap.py | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py b/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py index 012a78aba..97822611e 100644 --- a/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py +++ b/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py @@ -6,7 +6,7 @@ from subprocess import Popen, PIPE class AirtimeMediaMonitorBootstrap(): """AirtimeMediaMonitorBootstrap constructor - + Keyword Arguments: logger -- reference to the media-monitor logging facility pe -- reference to an instance of ProcessEvent @@ -17,36 +17,36 @@ class AirtimeMediaMonitorBootstrap(): self.pe = pe self.api_client = api_client self.mmc = mmc - + """On bootup we want to scan all directories and look for files that weren't there or files that changed before media-monitor process went offline. """ def scan(self): directories = self.get_list_of_watched_dirs(); - + self.logger.info("watched directories found: %s", directories) - + for id, dir in directories.iteritems(): self.logger.debug("%s, %s", id, dir) self.sync_database_to_filesystem(id, dir) - + """Gets a list of files that the Airtime database knows for a specific directory. - You need to provide the directory's row ID, which is obtained when calling + You need to provide the directory's row ID, which is obtained when calling get_list_of_watched_dirs function. dir_id -- row id of the directory in the cc_watched_dirs database table """ def list_db_files(self, dir_id): return self.api_client.list_all_db_files(dir_id) - + """ - returns the path and the database row id for this path for all watched directories. Also + returns the path and the database row id for this path for all watched directories. Also returns the Stor directory, which can be identified by its row id (always has value of "1") """ def get_list_of_watched_dirs(self): json = self.api_client.list_all_watched_dirs() - return json["dirs"] - + return json["dirs"] + """ This function takes in a path name provided by the database (and its corresponding row id) and reads the list of files in the local file system. Its purpose is to discover which files @@ -57,42 +57,42 @@ class AirtimeMediaMonitorBootstrap(): dir -- pathname of the directory """ def sync_database_to_filesystem(self, dir_id, dir): - """ + """ set to hold new and/or modified files. We use a set to make it ok if files are added twice. This is because some of the tests for new files return result sets that are not mutually exclusive from each other. """ new_and_modified_files = set() removed_files = set() - - + + db_known_files_set = set() files = self.list_db_files(dir_id) for file in files['files']: db_known_files_set.add(file) - + new_files = self.mmc.scan_dir_for_new_files(dir) all_files_set = set() for file_path in new_files: if len(file_path.strip(" \n")) > 0: - all_files_set.add(file_path[len(dir):]) - + all_files_set.add(file_path[len(dir):]) + if os.path.exists(self.mmc.timestamp_file): """find files that have been modified since the last time media-monitor process started.""" time_diff_sec = time.time() - os.path.getmtime(self.mmc.timestamp_file) command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable -mmin -%d" % (dir, time_diff_sec/60+1) else: command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable" % dir - + stdout = self.mmc.execCommandAndReturnStdOut(command) stdout = unicode(stdout, "utf_8") new_files = stdout.splitlines() - + for file_path in new_files: if len(file_path.strip(" \n")) > 0: - new_and_modified_files.add(file_path[len(dir)+1:]) - + new_and_modified_files.add(file_path[len(dir):]) + """ new_and_modified_files gives us a set of files that were either copied or modified since the last time media-monitor was running. These files were collected based on @@ -101,28 +101,32 @@ class AirtimeMediaMonitorBootstrap(): not affect last modified timestamp). Lets get a list of files that are on the file-system that the db has no record of, and vice-versa. """ - + deleted_files_set = db_known_files_set - all_files_set new_files_set = all_files_set - db_known_files_set modified_files_set = new_and_modified_files - new_files_set - + self.logger.info("Deleted files: \n%s\n\n"%deleted_files_set) self.logger.info("New files: \n%s\n\n"%new_files_set) - self.logger.info("Modified files: \n%s\n\n"%modified_files_set) - + self.logger.info("Modified files: \n%s\n\n"%modified_files_set) + #"touch" file timestamp self.mmc.touch_index_file() - + for file_path in deleted_files_set: - self.pe.handle_removed_file(False, "%s/%s" % (dir, file_path)) - + self.pe.handle_removed_file(False, "%s%s" % (dir, file_path)) + deleted_files_set.clear() + for file_path in new_files_set: - file_path = "%s/%s" % (dir, file_path) + file_path = "%s%s" % (dir, file_path) if os.path.exists(file_path): self.pe.handle_created_file(False, os.path.basename(file_path), file_path) - + + new_files_set.clear() + for file_path in modified_files_set: - file_path = "%s/%s" % (dir, file_path) + file_path = "%s%s" % (dir, file_path) if os.path.exists(file_path): self.pe.handle_modified_file(False, os.path.basename(file_path), file_path) - + modified_files_set.clear() + From ff2fcde7c0dcd9614c9797cad87e6623ff03e830 Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 19 Jul 2011 12:07:31 -0400 Subject: [PATCH 19/21] CC-2567: Install api_client separately from pypo --- install/airtime-install | 3 +++ install/airtime-uninstall | 4 ++++ install/include/AirtimeIni.php | 5 +++++ install/include/airtime-upgrade.php | 2 ++ python_apps/api_clients/api_client.cfg | 1 + .../api_clients/install/api_client_install.py | 6 ++++-- .../install/api_client_uninstall.py | 21 +++++++++++++++++++ python_apps/api_clients/install/config.cfg | 1 - .../media-monitor/airtime-media-monitor | 2 +- python_apps/pypo/airtime-liquidsoap | 2 +- python_apps/pypo/airtime-playout | 2 +- python_apps/pypo/install/pypo-install.py | 1 - .../show-recorder/airtime-show-recorder | 2 +- 13 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 python_apps/api_clients/api_client.cfg create mode 100644 python_apps/api_clients/install/api_client_uninstall.py delete mode 100644 python_apps/api_clients/install/config.cfg diff --git a/install/airtime-install b/install/airtime-install index f746e22b5..7c2e24bb1 100755 --- a/install/airtime-install +++ b/install/airtime-install @@ -16,6 +16,9 @@ python ${SCRIPTPATH}/../python_apps/create-pypo-user.py php ${SCRIPTPATH}/include/airtime-install.php $@ +echo -e "\n*** API Client Installation ***" +python ${SCRIPTPATH}/../python_apps/api_clients/install/api_client_install.py + echo -e "\n*** Pypo Installation ***" python ${SCRIPTPATH}/../python_apps/pypo/install/pypo-install.py diff --git a/install/airtime-uninstall b/install/airtime-uninstall index 9f708e10f..bab6eae45 100755 --- a/install/airtime-uninstall +++ b/install/airtime-uninstall @@ -20,6 +20,10 @@ python ${SCRIPTPATH}/../python_apps/show-recorder/install/recorder-uninstall.py echo -e "\n*** Uninstalling Media Monitor ***" python ${SCRIPTPATH}/../python_apps/media-monitor/install/media-monitor-uninstall.py +echo -e "\n*** Uninstalling API Client ***" +python ${SCRIPTPATH}/../python_apps/api_clients/install/api_client_uninstall.py + + echo -e "\n*** Removing Pypo User ***" python ${SCRIPTPATH}/../python_apps/remove-pypo-user.py diff --git a/install/include/AirtimeIni.php b/install/include/AirtimeIni.php index 99048cf05..5a2db8dd0 100644 --- a/install/include/AirtimeIni.php +++ b/install/include/AirtimeIni.php @@ -27,6 +27,7 @@ class AirtimeIni const CONF_FILE_AIRTIME = "/etc/airtime/airtime.conf"; const CONF_FILE_PYPO = "/etc/airtime/pypo.cfg"; const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg"; + const CONF_FILE_API_CLIENT = "/etc/airtime/api_client.cfg"; const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg"; const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg"; const CONF_FILE_MONIT = "/etc/monit/conf.d/airtime-monit.cfg"; @@ -65,6 +66,10 @@ class AirtimeIni echo "Could not copy airtime.conf to /etc/airtime/. Exiting."; exit(1); } + if (!copy(__DIR__."/../../python_apps/api_clients/api_client.cfg", AirtimeIni::CONF_FILE_API_CLIENT)){ + echo "Could not copy api_client.cfg to /etc/airtime/. Exiting."; + exit(1); + } if (!copy(__DIR__."/../../python_apps/pypo/pypo.cfg", AirtimeIni::CONF_FILE_PYPO)){ echo "Could not copy pypo.cfg to /etc/airtime/. Exiting."; exit(1); diff --git a/install/include/airtime-upgrade.php b/install/include/airtime-upgrade.php index eb0dd2aa0..822aa3a28 100644 --- a/install/include/airtime-upgrade.php +++ b/install/include/airtime-upgrade.php @@ -94,6 +94,8 @@ $newVersion = AIRTIME_VERSION; $sql = "INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '$newVersion')"; $CC_DBC->query($sql); +echo PHP_EOL."*** Updating Api Client ***".PHP_EOL; +passthru("python ".__DIR__."/../../python_apps/api_clients/install/api_client_install.py"); echo PHP_EOL."*** Updating Pypo ***".PHP_EOL; passthru("python ".__DIR__."/../../python_apps/pypo/install/pypo-install.py"); diff --git a/python_apps/api_clients/api_client.cfg b/python_apps/api_clients/api_client.cfg new file mode 100644 index 000000000..9a0fc8fb6 --- /dev/null +++ b/python_apps/api_clients/api_client.cfg @@ -0,0 +1 @@ +bin_dir = "/usr/lib/airtime/api_clients" diff --git a/python_apps/api_clients/install/api_client_install.py b/python_apps/api_clients/install/api_client_install.py index 47947c74e..e9f216a2a 100644 --- a/python_apps/api_clients/install/api_client_install.py +++ b/python_apps/api_clients/install/api_client_install.py @@ -1,5 +1,7 @@ import os import shutil +import sys +from configobj import ConfigObj def get_current_script_dir(): return os.path.dirname(os.path.realpath(__file__)) @@ -15,9 +17,9 @@ current_script_dir = get_current_script_dir() """load config file""" try: - config = ConfigObj("current_script_dir/../config.cfg") + config = ConfigObj("%s/../api_client.cfg" % current_script_dir) except Exception, e: print 'Error loading config file: ', e sys.exit(1) -copy_dir("%s/../../api_clients"%current_script_dir, config["bin_dir"]+"/api_clients/") \ No newline at end of file +copy_dir("%s/../../api_clients"%current_script_dir, config["bin_dir"]) diff --git a/python_apps/api_clients/install/api_client_uninstall.py b/python_apps/api_clients/install/api_client_uninstall.py new file mode 100644 index 000000000..b662add19 --- /dev/null +++ b/python_apps/api_clients/install/api_client_uninstall.py @@ -0,0 +1,21 @@ +import os +import sys +from configobj import ConfigObj + +def remove_path(path): + os.system('rm -rf "%s"' % path) + +def get_current_script_dir(): + return os.path.dirname(os.path.realpath(__file__)) + +current_script_dir = get_current_script_dir() + +"""load config file""" +try: + config = ConfigObj("%s/../api_client.cfg" % current_script_dir) +except Exception, e: + print 'Error loading config file: ', e + sys.exit(1) + +print "Removing API Client files" +remove_path(config["bin_dir"]) diff --git a/python_apps/api_clients/install/config.cfg b/python_apps/api_clients/install/config.cfg deleted file mode 100644 index e78abc950..000000000 --- a/python_apps/api_clients/install/config.cfg +++ /dev/null @@ -1 +0,0 @@ -bin_dir = "/usr/lib/airtime/api_clients/bin" diff --git a/python_apps/media-monitor/airtime-media-monitor b/python_apps/media-monitor/airtime-media-monitor index c6f892fdf..7bbb017be 100755 --- a/python_apps/media-monitor/airtime-media-monitor +++ b/python_apps/media-monitor/airtime-media-monitor @@ -4,7 +4,7 @@ media_monitor_path="/usr/lib/airtime/media-monitor/" media_monitor_script="MediaMonitor.py" -api_client_path="/usr/lib/airtime/pypo/" +api_client_path="/usr/lib/airtime/" cd ${media_monitor_path} exec 2>&1 diff --git a/python_apps/pypo/airtime-liquidsoap b/python_apps/pypo/airtime-liquidsoap index d7fd805f7..a673b0bad 100755 --- a/python_apps/pypo/airtime-liquidsoap +++ b/python_apps/pypo/airtime-liquidsoap @@ -1,7 +1,7 @@ #!/bin/sh ls_user="pypo" export HOME="/var/tmp/airtime/pypo/" -api_client_path="/usr/lib/airtime/pypo/" +api_client_path="/usr/lib/airtime/" ls_path="/usr/lib/airtime/pypo/bin/liquidsoap_bin/liquidsoap" ls_param="/usr/lib/airtime/pypo/bin/liquidsoap_scripts/ls_script.liq" diff --git a/python_apps/pypo/airtime-playout b/python_apps/pypo/airtime-playout index 0eb206f1c..5337934e5 100755 --- a/python_apps/pypo/airtime-playout +++ b/python_apps/pypo/airtime-playout @@ -4,7 +4,7 @@ pypo_user="pypo" # Location of pypo_cli.py Python script pypo_path="/usr/lib/airtime/pypo/bin/" -api_client_path="/usr/lib/airtime/pypo/" +api_client_path="/usr/lib/airtime/" pypo_script="pypo-cli.py" cd ${pypo_path} exec 2>&1 diff --git a/python_apps/pypo/install/pypo-install.py b/python_apps/pypo/install/pypo-install.py index 5b8df115c..8173742be 100755 --- a/python_apps/pypo/install/pypo-install.py +++ b/python_apps/pypo/install/pypo-install.py @@ -96,7 +96,6 @@ try: sys.exit(1) copy_dir("%s/.."%current_script_dir, config["bin_dir"]+"/bin/") - copy_dir("%s/../../api_clients"%current_script_dir, config["bin_dir"]+"/api_clients/") print "Setting permissions" os.system("chmod -R 755 "+config["bin_dir"]) diff --git a/python_apps/show-recorder/airtime-show-recorder b/python_apps/show-recorder/airtime-show-recorder index 0bd0562e6..73c045864 100755 --- a/python_apps/show-recorder/airtime-show-recorder +++ b/python_apps/show-recorder/airtime-show-recorder @@ -7,7 +7,7 @@ recorder_user="pypo" recorder_path="/usr/lib/airtime/show-recorder/" recorder_script="recorder.py" -api_client_path="/usr/lib/airtime/pypo/" +api_client_path="/usr/lib/airtime/" cd ${recorder_path} exec 2>&1 From 9aa7b7f4033b02e689581ccefee8ae5d3a37880f Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Tue, 19 Jul 2011 18:36:10 +0200 Subject: [PATCH 20/21] CC-2572 : Set Proper Metadata for a recorded show --- .../application/controllers/ApiController.php | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 96f8e31b1..310f3c4ed 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -74,8 +74,8 @@ class ApiController extends Zend_Controller_Action $download = ("true" == $this->_getParam('download')); $logger = Logging::getLogger(); - - if(!in_array($api_key, $CC_CONFIG["apiKey"]) && + + if(!in_array($api_key, $CC_CONFIG["apiKey"]) && is_null(Zend_Auth::getInstance()->getStorage()->read())) { header('HTTP/1.0 401 Unauthorized'); @@ -120,7 +120,7 @@ class ApiController extends Zend_Controller_Action fclose($fp); //make sure to exit here so that no other output is sent. - exit; + exit; } else { $logger->err('Resource in database, but not in storage: "'.$filepath.'"'); } @@ -336,12 +336,12 @@ class ApiController extends Zend_Controller_Action $this->view->fileid = $file_id; $this->view->showinstanceid = $show_instance_id; - + $showCanceled = false; $file = StoredFile::Recall($file_id); //$show_instance = $this->_getParam('show_instance'); - $show_name = ""; + $show_name = null; try { $show_inst = new ShowInstance($show_instance_id); @@ -359,10 +359,17 @@ class ApiController extends Zend_Controller_Action $showCanceled = true; } - $tmpTitle = !(empty($show_name))?$show_name."-":""; - $tmpTitle .= $file->getName(); + if (isset($show_name)) { + $tmpTitle = "$show_name-$show_start_time"; + $tmpTitle = str_replace(":", "-", $tmpTitle); + $tmpTitle = str_replace(" ", "-", $tmpTitle); + } + else { + $tmpTitle = $file->getName(); + } $file->setMetadataValue('MDATA_KEY_TITLE', $tmpTitle); + $file->setMetadataValue('MDATA_KEY_CREATOR', "Airtime Show Recorder"); if (!$showCanceled && Application_Model_Preference::GetDoSoundCloudUpload()) { @@ -392,7 +399,7 @@ class ApiController extends Zend_Controller_Action } $this->view->id = $file_id; - + } public function mediaMonitorSetupAction() { @@ -409,7 +416,7 @@ class ApiController extends Zend_Controller_Action print 'You are not allowed to access this resource.'; exit; } - + $this->view->stor = MusicDir::getStorDir()->getDirectory(); } @@ -439,7 +446,7 @@ class ApiController extends Zend_Controller_Action if ($mode == "create") { $filepath = $md['MDATA_KEY_FILEPATH']; $file = StoredFile::RecallByFilepath($filepath); - + if (is_null($file)) { $file = StoredFile::Insert($md); } else { @@ -447,7 +454,7 @@ class ApiController extends Zend_Controller_Action return; } - + //Martin Konecny July 14th, 2011: The following commented out code is the way //we used to check for duplicates (by md5). Why are we checking by md5 and //not by filepath? @@ -509,7 +516,7 @@ class ApiController extends Zend_Controller_Action $this->view->id = $file->getId(); } - + public function listAllFilesAction() { global $CC_CONFIG; @@ -522,10 +529,10 @@ class ApiController extends Zend_Controller_Action exit; } $dir_id = $request->getParam('dir_id'); - + $this->view->files = StoredFile::listAllFiles($dir_id); } - + public function listAllWatchedDirsAction() { global $CC_CONFIG; @@ -537,69 +544,69 @@ class ApiController extends Zend_Controller_Action print 'You are not allowed to access this resource.'; exit; } - + $result = array(); - + $arrWatchedDirs = MusicDir::getWatchedDirs(); $storDir = MusicDir::getStorDir(); - + $result[$storDir->getId()] = $storDir->getDirectory(); - + foreach ($arrWatchedDirs as $watchedDir){ $result[$watchedDir->getId()] = $watchedDir->getDirectory(); } - + $this->view->dirs = $result; } - + public function addWatchedDirAction() { global $CC_CONFIG; $request = $this->getRequest(); $api_key = $request->getParam('api_key'); $path = base64_decode($request->getParam('path')); - + if (!in_array($api_key, $CC_CONFIG["apiKey"])) { header('HTTP/1.0 401 Unauthorized'); print 'You are not allowed to access this resource.'; exit; } - + $this->view->msg = MusicDir::addWatchedDir($path); } - + public function removeWatchedDirAction() { global $CC_CONFIG; $request = $this->getRequest(); $api_key = $request->getParam('api_key'); $path = base64_decode($request->getParam('path')); - + if (!in_array($api_key, $CC_CONFIG["apiKey"])) { header('HTTP/1.0 401 Unauthorized'); print 'You are not allowed to access this resource.'; exit; } - + $this->view->msg = MusicDir::removeWatchedDir($path); } - + public function setStorageDirAction() { global $CC_CONFIG; $request = $this->getRequest(); $api_key = $request->getParam('api_key'); $path = base64_decode($request->getParam('path')); - + if (!in_array($api_key, $CC_CONFIG["apiKey"])) { header('HTTP/1.0 401 Unauthorized'); print 'You are not allowed to access this resource.'; exit; } - + $this->view->msg = MusicDir::setStorDir($path); } } From 7d79fa7b632df49c765c48b83c32701e43d6ec46 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Tue, 19 Jul 2011 19:02:23 +0200 Subject: [PATCH 21/21] CC-2572 : Set Proper Metadata for a recorded show --- airtime_mvc/application/controllers/ApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 310f3c4ed..112fdf575 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -361,7 +361,6 @@ class ApiController extends Zend_Controller_Action if (isset($show_name)) { $tmpTitle = "$show_name-$show_start_time"; - $tmpTitle = str_replace(":", "-", $tmpTitle); $tmpTitle = str_replace(" ", "-", $tmpTitle); } else { @@ -370,6 +369,7 @@ class ApiController extends Zend_Controller_Action $file->setMetadataValue('MDATA_KEY_TITLE', $tmpTitle); $file->setMetadataValue('MDATA_KEY_CREATOR', "Airtime Show Recorder"); + $file->setMetadataValue('MDATA_KEY_TRACKNUMBER', null); if (!$showCanceled && Application_Model_Preference::GetDoSoundCloudUpload()) {