Merge branch 'devel' of dev.sourcefabric.org:airtime into devel

This commit is contained in:
Naomi Aro 2012-05-07 14:29:02 +02:00
commit 41e7150462
23 changed files with 461 additions and 185 deletions

View file

@ -12,21 +12,26 @@ require_once "DateHelper.php";
require_once "OsPath.php"; require_once "OsPath.php";
require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php'; require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php';
//DateTime in PHP 5.3.0+ need a default timezone set. Set to UTC initially
//in case Application_Model_Preference::GetTimezone fails and creates needs to create
//a log entry. This log entry requires a call to date(), which then complains that
//timezone isn't set. Setting a default timezone allows us to create a a graceful log
//that getting the real timezone failed, without PHP complaining that it cannot log because
//there is no timezone :|.
date_default_timezone_set('UTC');
date_default_timezone_set(Application_Model_Preference::GetTimezone());
global $CC_CONFIG; global $CC_CONFIG;
$CC_CONFIG['airtime_version'] = Application_Model_Preference::GetAirtimeVersion(); $CC_CONFIG['airtime_version'] = Application_Model_Preference::GetAirtimeVersion();
require_once __DIR__."/configs/navigation.php"; require_once __DIR__."/configs/navigation.php";
//DateTime in PHP 5.3.0+ need a default timezone set.
date_default_timezone_set(Application_Model_Preference::GetTimezone());
Zend_Validate::setDefaultNamespaces("Zend"); Zend_Validate::setDefaultNamespaces("Zend");
$front = Zend_Controller_Front::getInstance(); $front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new RabbitMqPlugin()); $front->registerPlugin(new RabbitMqPlugin());
//Logging::debug($_SERVER['REQUEST_URI']);
/* The bootstrap class should only be used to initialize actions that return a view. /* The bootstrap class should only be used to initialize actions that return a view.
Actions that return JSON will not use the bootstrap class! */ Actions that return JSON will not use the bootstrap class! */
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap class Bootstrap extends Zend_Application_Bootstrap_Bootstrap

View file

@ -45,7 +45,7 @@ class Application_Common_OsPath{
/* Similar to the os.path.join python method /* Similar to the os.path.join python method
* http://stackoverflow.com/a/1782990/276949 */ * http://stackoverflow.com/a/1782990/276949 */
function join() { public static function join() {
$args = func_get_args(); $args = func_get_args();
$paths = array(); $paths = array();

View file

@ -0,0 +1,109 @@
<?php
class Airtime_Zend_Log extends Zend_Log
{
/**
*
* @var boolean
*/
protected $_registeredErrorHandler = false;
/**
*
* @var array|boolean
*/
protected $_errorHandlerMap = false;
/**
*
* @var callback
*/
protected $_origErrorHandler = null;
public function __construct(Zend_Log_Writer_Abstract $writer = null)
{
parent::__construct($writer);
}
/**
* Error Handler will convert error into log message, and then call the original error handler
*
* @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param array $errcontext
* @return boolean
*/
public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
{
$errorLevel = error_reporting();
if ($errorLevel && $errno) {
if (isset($this->_errorHandlerMap[$errno])) {
$priority = $this->_errorHandlerMap[$errno];
} else {
$priority = Zend_Log::INFO;
}
$this->log($errstr, $priority, array('errno'=>$errno, 'file'=>$errfile, 'line'=>$errline, 'context'=>$errcontext));
}
if ($this->_origErrorHandler !== null) {
return call_user_func($this->_origErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext);
}
return false;
}
/**
* Register Logging system as an error handler to log php errors
* Note: it still calls the original error handler if set_error_handler is able to return it.
*
* Errors will be mapped as:
* E_NOTICE, E_USER_NOTICE => NOTICE
* E_WARNING, E_CORE_WARNING, E_USER_WARNING => WARN
* E_ERROR, E_USER_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR => ERR
* E_DEPRECATED, E_STRICT, E_USER_DEPRECATED => DEBUG
* (unknown/other) => INFO
*
* @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler
*
* @return Zend_Log
*/
public function registerErrorHandler()
{
// Only register once. Avoids loop issues if it gets registered twice.
if ($this->_registeredErrorHandler) {
return $this;
}
$this->_origErrorHandler = set_error_handler(array($this, 'errorHandler'));
// Contruct a default map of phpErrors to Zend_Log priorities.
// Some of the errors are uncatchable, but are included for completeness
$this->_errorHandlerMap = array(
E_NOTICE => Zend_Log::NOTICE,
E_USER_NOTICE => Zend_Log::NOTICE,
E_WARNING => Zend_Log::WARN,
E_CORE_WARNING => Zend_Log::WARN,
E_USER_WARNING => Zend_Log::WARN,
E_ERROR => Zend_Log::ERR,
E_USER_ERROR => Zend_Log::ERR,
E_CORE_ERROR => Zend_Log::ERR,
E_RECOVERABLE_ERROR => Zend_Log::ERR,
E_STRICT => Zend_Log::DEBUG,
);
// PHP 5.3.0+
if (defined('E_DEPRECATED')) {
$this->_errorHandlerMap['E_DEPRECATED'] = Zend_Log::DEBUG;
}
if (defined('E_USER_DEPRECATED')) {
$this->_errorHandlerMap['E_USER_DEPRECATED'] = Zend_Log::DEBUG;
}
$this->_registeredErrorHandler = true;
return $this;
}
}

View file

@ -6,10 +6,19 @@ class Logging {
private static $_path; private static $_path;
public static function getLogger(){ public static function getLogger(){
if (!isset(self::$logger)) { if (!isset(self::$_logger)) {
$writer = new Zend_Log_Writer_Stream(self::$_path); $writer = new Zend_Log_Writer_Stream(self::$_path);
if (Zend_Version::compareVersion("1.11") > 0){
//Running Zend version 1.10 or lower. Need to instantiate our
//own Zend Log class with backported code from 1.11.
require_once __DIR__."/AirtimeLog.php";
self::$_logger = new Airtime_Zend_Log($writer);
} else {
self::$_logger = new Zend_Log($writer); self::$_logger = new Zend_Log($writer);
} }
self::$_logger->registerErrorHandler();
}
return self::$_logger; return self::$_logger;
} }

View file

@ -4,7 +4,7 @@ class Application_Model_Preference
{ {
public static function SetValue($key, $value, $isUserValue = false){ public static function SetValue($key, $value, $isUserValue = false){
global $CC_CONFIG; try {
$con = Propel::getConnection(); $con = Propel::getConnection();
//called from a daemon process //called from a daemon process
@ -61,11 +61,19 @@ class Application_Model_Preference
." VALUES ($id, '$key', $value)"; ." VALUES ($id, '$key', $value)";
} }
} }
return $con->exec($sql);
$con->exec($sql);
} catch (Exception $e){
header('HTTP/1.0 503 Service Unavailable');
Logging::log("Could not connect to database.");
exit;
}
} }
public static function GetValue($key, $isUserValue = false){ public static function GetValue($key, $isUserValue = false){
global $CC_CONFIG; try {
$con = Propel::getConnection(); $con = Propel::getConnection();
//Check if key already exists //Check if key already exists
@ -94,6 +102,11 @@ class Application_Model_Preference
$result = $con->query($sql)->fetchColumn(0); $result = $con->query($sql)->fetchColumn(0);
return ($result !== false) ? $result : ""; return ($result !== false) ? $result : "";
} }
} catch (Exception $e) {
header('HTTP/1.0 503 Service Unavailable');
Logging::log("Could not connect to database.");
exit;
}
} }
public static function GetHeadTitle(){ public static function GetHeadTitle(){
@ -561,11 +574,13 @@ class Application_Model_Preference
public static function GetAirtimeVersion(){ public static function GetAirtimeVersion(){
if (defined('APPLICATION_ENV') && APPLICATION_ENV == "development" && function_exists('exec')){ if (defined('APPLICATION_ENV') && APPLICATION_ENV == "development" && function_exists('exec')){
return self::GetValue("system_version")."+".exec("git rev-parse --short HEAD"); $version = exec("git rev-parse --short HEAD 2>/dev/null", $out, $return_code);
} else { if ($return_code == 0){
return self::GetValue("system_version"); return self::GetValue("system_version")."+".$version;
} }
} }
return self::GetValue("system_version");
}
public static function GetLatestVersion(){ public static function GetLatestVersion(){
$latest = self::GetValue("latest_version"); $latest = self::GetValue("latest_version");

View file

@ -26,6 +26,12 @@ class Application_Model_Scheduler {
$this->epochNow = microtime(true); $this->epochNow = microtime(true);
$this->nowDT = DateTime::createFromFormat("U.u", $this->epochNow, new DateTimeZone("UTC")); $this->nowDT = DateTime::createFromFormat("U.u", $this->epochNow, new DateTimeZone("UTC"));
if ($this->nowDT === false){
// DateTime::createFromFormat does not support millisecond string formatting in PHP 5.3.2 (Ubuntu 10.04).
// In PHP 5.3.3 (Ubuntu 10.10), this has been fixed.
$this->nowDT = DateTime::createFromFormat("U", time(), new DateTimeZone("UTC"));
}
$this->user = Application_Model_User::GetCurrentUser(); $this->user = Application_Model_User::GetCurrentUser();
} }
@ -190,6 +196,12 @@ class Application_Model_Scheduler {
$endEpoch = bcadd($startEpoch , (string) $durationSeconds, 6); $endEpoch = bcadd($startEpoch , (string) $durationSeconds, 6);
$dt = DateTime::createFromFormat("U.u", $endEpoch, new DateTimeZone("UTC")); $dt = DateTime::createFromFormat("U.u", $endEpoch, new DateTimeZone("UTC"));
if ($dt === false) {
//PHP 5.3.2 problem
$dt = DateTime::createFromFormat("U", intval($endEpoch), new DateTimeZone("UTC"));
}
return $dt; return $dt;
} }

View file

@ -91,6 +91,27 @@ class Application_Model_StoredFile {
} }
else { else {
$dbMd = array(); $dbMd = array();
if (isset($p_md["MDATA_KEY_YEAR"])){
// We need to make sure to clean this value before inserting into database.
// If value is outside of range [-2^31, 2^31-1] then postgresl will throw error
// when trying to retrieve this value. We could make sure number is within these bounds,
// but simplest is to do substring to 4 digits (both values are garbage, but at least our
// new garbage value won't cause errors). If the value is 2012-01-01, then substring to
// 4 digits is an OK result.
// CC-3771
$year = $p_md["MDATA_KEY_YEAR"];
if (strlen($year) > 4){
$year = substr($year, 0, 4);
}
if (!is_numeric($year)){
$year = 0;
}
$p_md["MDATA_KEY_YEAR"] = $year;
}
foreach ($p_md as $mdConst => $mdValue) { foreach ($p_md as $mdConst => $mdValue) {
$dbMd[constant($mdConst)] = $mdValue; $dbMd[constant($mdConst)] = $mdValue;
} }
@ -235,7 +256,9 @@ class Application_Model_StoredFile {
foreach ($c['user'] as $constant => $value) { foreach ($c['user'] as $constant => $value) {
if (preg_match('/^MDATA_KEY/', $constant)) { if (preg_match('/^MDATA_KEY/', $constant)) {
if (isset($dbmd_copy[$value])) { if (isset($dbmd_copy[$value])) {
$md[$constant] = $this->getDbColMetadataValue($value); $propelColumn = $dbmd_copy[$value];
$method = "get$propelColumn";
$md[$constant] = $this->_file->$method();
} }
} }
} }

View file

@ -1,8 +1,8 @@
<?php <?php
//error_reporting(E_ALL|E_STRICT); //error_reporting(E_ALL|E_STRICT);
error_reporting(E_ALL); //error_reporting(E_ALL);
ini_set('display_errors', 'on'); //ini_set('display_errors', 'on');
// Define path to application directory // Define path to application directory
defined('APPLICATION_PATH') defined('APPLICATION_PATH')

View file

@ -160,6 +160,19 @@ if [ "$DO_UPGRADE" -eq "1" ]; then
php --php-ini ${SCRIPTPATH}/airtime-php.ini ${SCRIPTPATH}/include/airtime-upgrade.php $@ php --php-ini ${SCRIPTPATH}/airtime-php.ini ${SCRIPTPATH}/include/airtime-upgrade.php $@
fi fi
set +e
if [ "$DO_UPGRADE" -eq "0" ]; then
php --php-ini ${SCRIPTPATH}/airtime-php.ini ${SCRIPTPATH}/include/airtime-install.php $@
result=$?
if [ "$result" -ne "0" ]; then
#There was an error, exit with error code.
echo "There was an error during install. Exit code $result"
exit 1
fi
fi
set -e
$SCRIPTPATH/include/airtime-copy-files.sh $SCRIPTPATH/include/airtime-copy-files.sh
$SCRIPTPATH/include/airtime-initialize.sh $@ $SCRIPTPATH/include/airtime-initialize.sh $@

View file

@ -261,7 +261,7 @@ class AirtimeInstall
// Put Propel sql files in Database // Put Propel sql files in Database
//$command = AirtimeInstall::CONF_DIR_WWW."/library/propel/generator/bin/propel-gen ".AirtimeInstall::CONF_DIR_WWW."/build/ insert-sql 2>/dev/null"; //$command = AirtimeInstall::CONF_DIR_WWW."/library/propel/generator/bin/propel-gen ".AirtimeInstall::CONF_DIR_WWW."/build/ insert-sql 2>/dev/null";
$dir = AirtimeInstall::CONF_DIR_WWW."/build/sql/"; $dir = self::GetAirtimeSrcDir()."/build/sql/";
$files = array("schema.sql", "sequences.sql", "views.sql", "triggers.sql", "defaultdata.sql"); $files = array("schema.sql", "sequences.sql", "views.sql", "triggers.sql", "defaultdata.sql");
foreach ($files as $f){ foreach ($files as $f){
@ -453,7 +453,7 @@ class AirtimeInstall
} }
touch($file); touch($file);
chmod($file, 0755); chmod($file, 0644);
chown($file, $CC_CONFIG['webServerUser']); chown($file, $CC_CONFIG['webServerUser']);
chgrp($file, $CC_CONFIG['webServerUser']); chgrp($file, $CC_CONFIG['webServerUser']);
} }

View file

@ -35,11 +35,18 @@ AIRTIMEROOT=$SCRIPTPATH/../../
echo "* Creating /etc/airtime" echo "* Creating /etc/airtime"
mkdir -p /etc/airtime mkdir -p /etc/airtime
#if [ "$DO_UPGRADE" -eq "0" ]; then
if [ ! -e /etc/airtime/airtime.conf ]; then if [ ! -e /etc/airtime/airtime.conf ]; then
#config file airtime.conf exists, but Airtime is not installed
cp $AIRTIMEROOT/airtime_mvc/build/airtime.conf /etc/airtime cp $AIRTIMEROOT/airtime_mvc/build/airtime.conf /etc/airtime
fi fi
#if [ -e /etc/airtime/airtime.conf -a "$DO_UPGRADE" -eq "0" ]; then
#config file airtime.conf exists, but Airtime is not installed
# mv /etc/airtime/airtime.conf airtime.conf.bak
# cp $AIRTIMEROOT/airtime_mvc/build/airtime.conf /etc/airtime
#fi
echo "* Creating /etc/monit/conf.d/monit-airtime-generic.cfg" echo "* Creating /etc/monit/conf.d/monit-airtime-generic.cfg"
mkdir -p /etc/monit/conf.d/ mkdir -p /etc/monit/conf.d/
if [ ! -e /etc/monit/conf.d/monit-airtime-generic.cfg ]; then if [ ! -e /etc/monit/conf.d/monit-airtime-generic.cfg ]; then
@ -78,6 +85,13 @@ ln -sf /usr/lib/airtime/utils/airtime-log /usr/bin/airtime-log
ln -sf /usr/lib/airtime/utils/airtime-test-soundcard /usr/bin/airtime-test-soundcard ln -sf /usr/lib/airtime/utils/airtime-test-soundcard /usr/bin/airtime-test-soundcard
ln -sf /usr/lib/airtime/utils/airtime-test-stream /usr/bin/airtime-test-stream ln -sf /usr/lib/airtime/utils/airtime-test-stream /usr/bin/airtime-test-stream
echo "* Creating /var/log/airtime"
mkdir -p /var/log/airtime
chmod a+x /var/log/airtime
touch /var/log/airtime/zendphp.log
chown www-data:www-data /var/log/airtime/zendphp.log
chmod 644 /var/log/airtime/zendphp.log
if [ "$web" = "t" ]; then if [ "$web" = "t" ]; then
echo "* Creating /usr/share/airtime" echo "* Creating /usr/share/airtime"
rm -rf "/usr/share/airtime" rm -rf "/usr/share/airtime"

View file

@ -14,22 +14,6 @@ SCRIPTPATH=`dirname $SCRIPT`
AIRTIMEROOT=$SCRIPTPATH/../../ AIRTIMEROOT=$SCRIPTPATH/../../
#virtualenv_bin="/usr/lib/airtime/airtime_virtualenv/bin/"
#. ${virtualenv_bin}activate
set +e
if [ "$DO_UPGRADE" -eq "0" ]; then
php --php-ini ${SCRIPTPATH}/../airtime-php.ini ${SCRIPTPATH}/airtime-install.php $@
result=$?
if [ "$result" -ne "0" ]; then
#There was an error, exit with error code.
echo "There was an error during install. Exit code $result"
exit 1
fi
fi
set -e
if [ "$mediamonitor" = "t" ]; then if [ "$mediamonitor" = "t" ]; then
python $AIRTIMEROOT/python_apps/media-monitor/install/media-monitor-initialize.py python $AIRTIMEROOT/python_apps/media-monitor/install/media-monitor-initialize.py
fi fi

View file

@ -3,9 +3,6 @@
* @package Airtime * @package Airtime
* @copyright 2011 Sourcefabric O.P.S. * @copyright 2011 Sourcefabric O.P.S.
* @license http://www.gnu.org/licenses/gpl.txt * @license http://www.gnu.org/licenses/gpl.txt
*
* Checks if a previous version of Airtime is currently installed and upgrades Airtime if so.
* Performs a new install (new configs, database install) otherwise.
*/ */
require_once(__DIR__.'/AirtimeIni.php'); require_once(__DIR__.'/AirtimeIni.php');
require_once(__DIR__.'/AirtimeInstall.php'); require_once(__DIR__.'/AirtimeInstall.php');
@ -56,7 +53,7 @@ if ($overwrite) {
} }
// Update the build.properties file to point to the correct directory. // Update the build.properties file to point to the correct directory.
AirtimeIni::UpdateIniValue(AirtimeInstall::CONF_DIR_WWW.'/build/build.properties', 'project.home', AirtimeInstall::CONF_DIR_WWW); //AirtimeIni::UpdateIniValue(AirtimeInstall::CONF_DIR_WWW.'/build/build.properties', 'project.home', AirtimeInstall::CONF_DIR_WWW);
require_once(AirtimeInstall::GetAirtimeSrcDir().'/application/configs/conf.php'); require_once(AirtimeInstall::GetAirtimeSrcDir().'/application/configs/conf.php');
@ -74,6 +71,4 @@ if ($db_install) {
} }
} }
AirtimeInstall::CreateZendPhpLogFile();
/* FINISHED AIRTIME PHP INSTALLER */ /* FINISHED AIRTIME PHP INSTALLER */

View file

@ -5,14 +5,23 @@
*/ */
class AirtimeDatabaseUpgrade{ class AirtimeDatabaseUpgrade{
public static function start(){ public static function start($p_dbValues){
echo "* Updating Database".PHP_EOL; echo "* Updating Database".PHP_EOL;
self::task0(); self::task0($p_dbValues);
self::task1(); self::task1();
echo " * Complete".PHP_EOL;
} }
private static function task0(){ private static function task0($p_dbValues){
UpgradeCommon::MigrateTablesToVersion(__DIR__, '20120411174904'); //UpgradeCommon::MigrateTablesToVersion(__DIR__, '20120411174904');
$username = $p_dbValues['database']['dbuser'];
$password = $p_dbValues['database']['dbpass'];
$host = $p_dbValues['database']['host'];
$database = $p_dbValues['database']['dbname'];
$dir = __DIR__;
passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/data/upgrade.sql $database 2>&1 | grep -v \"will create implicit index\"");
$sql = "INSERT INTO cc_pref(\"keystr\", \"valstr\") VALUES('scheduled_play_switch', 'on')"; $sql = "INSERT INTO cc_pref(\"keystr\", \"valstr\") VALUES('scheduled_play_switch', 'on')";
UpgradeCommon::queryDb($sql); UpgradeCommon::queryDb($sql);

View file

@ -49,9 +49,13 @@ require_once 'ConfFileUpgrade.php';
require_once 'DbUpgrade.php'; require_once 'DbUpgrade.php';
require_once 'MiscUpgrade.php'; require_once 'MiscUpgrade.php';
$filename = "/etc/airtime/airtime.conf";
$values = parse_ini_file($filename, true);
UpgradeCommon::connectToDatabase(); UpgradeCommon::connectToDatabase();
UpgradeCommon::SetDefaultTimezone(); UpgradeCommon::SetDefaultTimezone();
AirtimeConfigFileUpgrade::start(); AirtimeConfigFileUpgrade::start();
AirtimeDatabaseUpgrade::start(); AirtimeDatabaseUpgrade::start($values);
AirtimeMiscUpgrade::start(); AirtimeMiscUpgrade::start();

View file

@ -13,14 +13,14 @@ class Version20120411174904 extends AbstractMigration
public function up(Schema $schema) public function up(Schema $schema)
{ {
$this->_addSql("ALTER TABLE cc_show_instances ADD created timestamp"); $this->_addSql("ALTER TABLE cc_show_instances ADD created timestamp");
$this->_addSql("ALTER TABLE cc_show_instances ALTER COLUMN created SET NOT NULL");
$this->_addSql("ALTER TABLE cc_show_instances ADD last_scheduled timestamp"); $this->_addSql("ALTER TABLE cc_show_instances ADD last_scheduled timestamp");
//setting these to a default now for timeline refresh purposes. //setting these to a default now for timeline refresh purposes.
$now = gmdate("Y-m-d H:i:s"); $now = gmdate("Y-m-d H:i:s");
$this->_addSql("UPDATE cc_show_instances SET created = '$now'"); $this->_addSql("UPDATE cc_show_instances SET created = '$now'");
$this->_addSql("UPDATE cc_show_instances SET last_scheduled = '$now'"); $this->_addSql("UPDATE cc_show_instances SET last_scheduled = '$now'");
$this->_addSql("ALTER TABLE cc_show_instances ALTER COLUMN created SET NOT NULL");
} }
public function down(Schema $schema) public function down(Schema $schema)

View file

@ -0,0 +1,111 @@
DROP TRIGGER calculate_position ON cc_playlistcontents;
DROP FUNCTION calculate_position();
DROP VIEW cc_playlisttimes;
CREATE FUNCTION airtime_to_int(chartoconvert character varying) RETURNS integer
AS
'SELECT CASE WHEN trim($1) SIMILAR TO ''[0-9]+'' THEN CAST(trim($1) AS integer) ELSE NULL END;'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
ALTER TABLE cc_files
DROP CONSTRAINT cc_music_dirs_folder_fkey;
ALTER TABLE cc_playlist
DROP CONSTRAINT cc_playlist_editedby_fkey;
CREATE SEQUENCE cc_subjs_token_id_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
CREATE TABLE cc_subjs_token (
id integer DEFAULT nextval('cc_subjs_token_id_seq'::regclass) NOT NULL,
user_id integer NOT NULL,
"action" character varying(255) NOT NULL,
token character varying(40) NOT NULL,
created timestamp without time zone NOT NULL
);
ALTER TABLE cc_files
ADD COLUMN utime timestamp(6) without time zone,
ADD COLUMN lptime timestamp(6) without time zone,
ADD COLUMN file_exists boolean DEFAULT true,
ALTER COLUMN bit_rate TYPE integer USING airtime_to_int(bit_rate) /* TYPE change - table: cc_files original: character varying(32) new: integer */,
ALTER COLUMN sample_rate TYPE integer USING airtime_to_int(bit_rate) /* TYPE change - table: cc_files original: character varying(32) new: integer */,
ALTER COLUMN length TYPE interval /* TYPE change - table: cc_files original: time without time zone new: interval */,
ALTER COLUMN length SET DEFAULT '00:00:00'::interval;
ALTER TABLE cc_music_dirs
ADD COLUMN "exists" boolean DEFAULT true,
ADD COLUMN watched boolean DEFAULT true;
ALTER TABLE cc_playlist
DROP COLUMN "state",
DROP COLUMN currentlyaccessing,
DROP COLUMN editedby,
DROP COLUMN creator,
ADD COLUMN utime timestamp(6) without time zone,
ADD COLUMN creator_id integer,
ADD COLUMN length interval DEFAULT '00:00:00'::interval;
ALTER TABLE cc_playlistcontents
ALTER COLUMN cliplength TYPE interval /* TYPE change - table: cc_playlistcontents original: time without time zone new: interval */,
ALTER COLUMN cliplength SET DEFAULT '00:00:00'::interval,
ALTER COLUMN cuein TYPE interval /* TYPE change - table: cc_playlistcontents original: time without time zone new: interval */,
ALTER COLUMN cuein SET DEFAULT '00:00:00'::interval,
ALTER COLUMN cueout TYPE interval /* TYPE change - table: cc_playlistcontents original: time without time zone new: interval */,
ALTER COLUMN cueout SET DEFAULT '00:00:00'::interval;
ALTER TABLE cc_schedule
DROP COLUMN playlist_id,
DROP COLUMN group_id,
DROP COLUMN schedule_group_played,
ADD COLUMN playout_status smallint DEFAULT 1 NOT NULL,
ALTER COLUMN clip_length TYPE interval /* TYPE change - table: cc_schedule original: time without time zone new: interval */,
ALTER COLUMN clip_length SET DEFAULT '00:00:00'::interval,
ALTER COLUMN cue_in TYPE interval /* TYPE change - table: cc_schedule original: time without time zone new: interval */,
ALTER COLUMN cue_in SET DEFAULT '00:00:00'::interval,
ALTER COLUMN cue_out TYPE interval /* TYPE change - table: cc_schedule original: time without time zone new: interval */,
ALTER COLUMN cue_out SET DEFAULT '00:00:00'::interval;
ALTER TABLE cc_show
ADD COLUMN live_stream_using_airtime_auth boolean DEFAULT false,
ADD COLUMN live_stream_using_custom_auth boolean DEFAULT false,
ADD COLUMN live_stream_user character varying(255),
ADD COLUMN live_stream_pass character varying(255);
ALTER TABLE cc_show_instances
ADD COLUMN created timestamp without time zone,
ADD COLUMN last_scheduled timestamp without time zone,
ALTER COLUMN time_filled TYPE interval /* TYPE change - table: cc_show_instances original: time without time zone new: interval */,
ALTER COLUMN time_filled SET DEFAULT '00:00:00'::interval;
UPDATE cc_show_instances SET created = now();
ALTER TABLE cc_show_instances
ALTER COLUMN created SET NOT NULL;
ALTER TABLE cc_subjs_token
ADD CONSTRAINT cc_subjs_token_pkey PRIMARY KEY (id);
ALTER TABLE cc_files
ADD CONSTRAINT cc_music_dirs_folder_fkey FOREIGN KEY (directory) REFERENCES cc_music_dirs(id);
ALTER TABLE cc_playlist
ADD CONSTRAINT cc_playlist_createdby_fkey FOREIGN KEY (creator_id) REFERENCES cc_subjs(id);
ALTER TABLE cc_subjs_token
ADD CONSTRAINT cc_subjs_token_idx UNIQUE (token);
ALTER TABLE cc_subjs_token
ADD CONSTRAINT cc_subjs_token_userid_fkey FOREIGN KEY (user_id) REFERENCES cc_subjs(id) ON DELETE CASCADE;
CREATE INDEX cc_files_file_exists_idx ON cc_files USING btree (file_exists);
DROP FUNCTION airtime_to_int(chartoconvert character varying);

View file

@ -88,17 +88,16 @@ class AirtimeMetadata:
try: try:
airtime_file = mutagen.File(m['MDATA_KEY_FILEPATH'], easy=True) airtime_file = mutagen.File(m['MDATA_KEY_FILEPATH'], easy=True)
for key in m.keys() : for key in m:
if key in self.airtime2mutagen: if key in self.airtime2mutagen:
value = m[key] value = m[key]
if (value is not None):
self.logger.debug("Saving %s to file", key)
self.logger.debug(value)
if isinstance(value, basestring) and (len(value) > 0):
airtime_file[self.airtime2mutagen[key]] = api_client.encode_to(value, 'utf-8')
elif isinstance(value, int):
airtime_file[self.airtime2mutagen[key]] = str(value)
if value is not None:
value = unicode(value)
if len(value) > 0:
self.logger.debug("Saving key '%s' with value '%s' to file", key, value)
airtime_file[self.airtime2mutagen[key]] = value
airtime_file.save() airtime_file.save()
except Exception, e: except Exception, e:

View file

@ -103,9 +103,9 @@ class AirtimeNotifier(Notifier):
self.mmc.ensure_is_dir(self.config.imported_directory) self.mmc.ensure_is_dir(self.config.imported_directory)
self.mmc.ensure_is_dir(self.config.organize_directory) self.mmc.ensure_is_dir(self.config.organize_directory)
self.mmc.set_needed_file_permissions(self.config.storage_directory, True) self.mmc.is_readable(self.config.storage_directory, True)
self.mmc.set_needed_file_permissions(self.config.imported_directory, True) self.mmc.is_readable(self.config.imported_directory, True)
self.mmc.set_needed_file_permissions(self.config.organize_directory, True) self.mmc.is_readable(self.config.organize_directory, True)
self.watch_directory(new_storage_directory) self.watch_directory(new_storage_directory)
elif m['event_type'] == "file_delete": elif m['event_type'] == "file_delete":
@ -150,7 +150,6 @@ class AirtimeNotifier(Notifier):
file_md = None file_md = None
data = None data = None
if (os.path.exists(filepath) and (mode == self.config.MODE_CREATE)): if (os.path.exists(filepath) and (mode == self.config.MODE_CREATE)):
if file_md is None: if file_md is None:
mutagen = self.md_manager.get_md_from_file(filepath) mutagen = self.md_manager.get_md_from_file(filepath)
@ -192,17 +191,13 @@ class AirtimeNotifier(Notifier):
mm = self.proc_fun() mm = self.proc_fun()
self.mmc.set_needed_file_permissions(directory, True) self.mmc.is_readable(directory, True)
for (path, dirs, files) in os.walk(directory): for (path, dirs, files) in os.walk(directory):
for d in dirs:
self.mmc.set_needed_file_permissions(os.path.join(path, d), True)
for filename in files: for filename in files:
full_filepath = os.path.join(path, filename) full_filepath = os.path.join(path, filename)
if self.mmc.is_audio_file(full_filepath): if self.mmc.is_audio_file(full_filepath):
if self.mmc.set_needed_file_permissions(full_filepath, False): if self.mmc.is_readable(full_filepath, False):
self.logger.info("importing %s", full_filepath) self.logger.info("importing %s", full_filepath)
event = {'filepath': full_filepath, 'mode': self.config.MODE_CREATE, 'is_recorded_show': False} event = {'filepath': full_filepath, 'mode': self.config.MODE_CREATE, 'is_recorded_show': False}
mm.multi_queue.put(event) mm.multi_queue.put(event)

View file

@ -134,7 +134,7 @@ class AirtimeProcessEvent(ProcessEvent):
#file is being overwritten/replaced in GUI. #file is being overwritten/replaced in GUI.
elif "goutputstream" in pathname: elif "goutputstream" in pathname:
self.temp_files[pathname] = None self.temp_files[pathname] = None
elif self.mmc.is_audio_file(pathname): elif self.mmc.is_audio_file(pathname) and self.mmc.is_readable(pathname, False):
if self.mmc.is_parent_directory(pathname, self.config.organize_directory): if self.mmc.is_parent_directory(pathname, self.config.organize_directory):
#file was created in /srv/airtime/stor/organize. Need to process and move #file was created in /srv/airtime/stor/organize. Need to process and move
#to /srv/airtime/stor/imported #to /srv/airtime/stor/imported
@ -151,14 +151,14 @@ class AirtimeProcessEvent(ProcessEvent):
self.logger.error('Exception: %s', e) self.logger.error('Exception: %s', e)
self.logger.error("traceback: %s", traceback.format_exc()) self.logger.error("traceback: %s", traceback.format_exc())
self.mmc.set_needed_file_permissions(pathname, dir) self.mmc.is_readable(pathname, dir)
is_recorded = self.mmc.is_parent_directory(pathname, self.config.recorded_directory) is_recorded = self.mmc.is_parent_directory(pathname, self.config.recorded_directory)
self.file_events.append({'mode': self.config.MODE_CREATE, 'filepath': pathname, 'is_recorded_show': is_recorded}) self.file_events.append({'mode': self.config.MODE_CREATE, 'filepath': pathname, 'is_recorded_show': is_recorded})
else: else:
#event is because of a created directory #event is because of a created directory
if self.mmc.is_parent_directory(pathname, self.config.storage_directory): if self.mmc.is_parent_directory(pathname, self.config.storage_directory):
self.mmc.set_needed_file_permissions(pathname, dir) self.mmc.is_readable(pathname, dir)
def process_IN_MODIFY(self, event): def process_IN_MODIFY(self, event):
# if IN_MODIFY is followed by IN_CREATE, it's not true modify event # if IN_MODIFY is followed by IN_CREATE, it's not true modify event
@ -236,10 +236,9 @@ class AirtimeProcessEvent(ProcessEvent):
filename = self.mount_file_dir +"/mtab" filename = self.mount_file_dir +"/mtab"
if event.pathname in filename: if event.pathname in filename:
self.handle_mount_change() self.handle_mount_change()
#if stuff dropped in stor via a UI move must change file permissions.
self.mmc.set_needed_file_permissions(event.pathname, event.dir)
if not event.dir: if not event.dir:
if self.mmc.is_audio_file(event.name): if self.mmc.is_audio_file(event.name) and self.mmc.is_readable(event.pathname, False):
if event.cookie in self.temp_files: if event.cookie in self.temp_files:
self.file_events.append({'filepath': event.pathname, 'mode': self.config.MODE_MODIFY}) self.file_events.append({'filepath': event.pathname, 'mode': self.config.MODE_MODIFY})
del self.temp_files[event.cookie] del self.temp_files[event.cookie]

View file

@ -49,7 +49,7 @@ class MediaMonitorCommon:
return False return False
#check if file is readable by "nobody" #check if file is readable by "nobody"
def has_correct_permissions(self, filepath, euid='nobody', egid='nogroup'): def is_user_readable(self, filepath, euid='nobody', egid='nogroup'):
try: try:
uid = pwd.getpwnam(euid)[2] uid = pwd.getpwnam(euid)[2]
@ -76,35 +76,13 @@ class MediaMonitorCommon:
return readable return readable
# the function only changes the permission if its not readable by www-data # the function only changes the permission if its not readable by www-data
def set_needed_file_permissions(self, item, is_dir): def is_readable(self, item, is_dir):
try: try:
omask = os.umask(0) return self.is_user_readable(item, 'www-data', 'www-data') \
if not self.has_correct_permissions(item, 'www-data', 'www-data') or not self.has_correct_permissions(item, 'pypo', 'pypo'): and self.is_user_readable(item, 'pypo', 'pypo')
# stats.st_mode is the original permission
# stat.S_IROTH - readable by all permission
# stat.S_IXOTH - excutable by all permission
# try to set permission
if self.is_parent_directory(item, self.config.storage_directory) or self.is_parent_directory(item, self.config.imported_directory) or self.is_parent_directory(item, self.config.organize_directory):
if is_dir is True:
os.chmod(item, 02777)
else:
os.chmod(item, 0666)
else :
# add world readable permission
stats = os.stat(item)
if is_dir is True:
bitor = stats.st_mode | stat.S_IROTH | stat.S_IXOTH
else:
bitor = stats.st_mode | stat.S_IROTH
os.chmod(item, bitor)
except Exception, e: except Exception, e:
self.logger.error("Failed to change file's owner/group/permissions. %s", e) self.logger.warn("Failed to check owner/group/permissions for %s", item)
self.logger.error("traceback: %s", traceback.format_exc())
return False return False
finally:
os.umask(omask)
return True
#checks if path is a directory, and if it doesnt exist, then creates it. #checks if path is a directory, and if it doesnt exist, then creates it.
#Otherwise prints error to log file. #Otherwise prints error to log file.

View file

@ -51,6 +51,7 @@ def configure_locale():
if current_locale_encoding not in ['utf-8', 'utf8']: if current_locale_encoding not in ['utf-8', 'utf8']:
logger.error("Need a UTF-8 locale. Currently '%s'. Exiting..." % current_locale_encoding) logger.error("Need a UTF-8 locale. Currently '%s'. Exiting..." % current_locale_encoding)
sys.exit(1)
# configure logging # configure logging
try: try:

View file

@ -129,7 +129,8 @@ class PypoPush(Thread):
tn.write('exit\n') tn.write('exit\n')
tn.read_all() tn.read_all()
except Exception, e: except Exception, e:
self.logger.error(str(e)) self.logger.error("Error connecting to Liquidsoap: %s", e)
response = []
finally: finally:
self.telnet_lock.release() self.telnet_lock.release()