Merge branch '2.1.x' of dev.sourcefabric.org:airtime into 2.1.x
This commit is contained in:
commit
6d056daa2f
7
CREDITS
7
CREDITS
|
@ -1,3 +1,10 @@
|
||||||
|
=======
|
||||||
|
CREDITS
|
||||||
|
=======
|
||||||
|
Version 2.1.3
|
||||||
|
-------------
|
||||||
|
Same as previous version.
|
||||||
|
|
||||||
=======
|
=======
|
||||||
CREDITS
|
CREDITS
|
||||||
=======
|
=======
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1,2 +1,2 @@
|
||||||
PRODUCT_ID=Airtime
|
PRODUCT_ID=Airtime
|
||||||
PRODUCT_RELEASE=2.1.2
|
PRODUCT_RELEASE=2.1.3
|
||||||
|
|
12
changelog
12
changelog
|
@ -1,3 +1,15 @@
|
||||||
|
2.1.3 - July 4th, 2012
|
||||||
|
* Changes
|
||||||
|
* Clarify inputs and output labels under stream settings
|
||||||
|
* Bug Fixes
|
||||||
|
* Fix playout engine crashing in rare cases after the system is restarted
|
||||||
|
* Fix entries in the Calendar unable to have multiple icons (recorded icon, soundcloud icon etc.)
|
||||||
|
* Fixed unwatching a watched folder with a large number of files (50,000+) can take a long time
|
||||||
|
* Fixed files deleted in the Web UI would delete files from the disk in watched folders
|
||||||
|
* Fixed jQuery widgets not showing the incorrectly showing the past Sunday on Sunday
|
||||||
|
* Fixed dragging and dropping tracks into a live show could cause to web UI to become unsynchronized from what is actually playing
|
||||||
|
* Fixed unable to receive mono streams for Master or Show source rebroadcasts
|
||||||
|
|
||||||
2.1.2 - June 18th, 2012
|
2.1.2 - June 18th, 2012
|
||||||
* Bug Fixes
|
* Bug Fixes
|
||||||
* Fixed problem where playout engine may not retrieve program schedule after extended periods of user inactivity.
|
* Fixed problem where playout engine may not retrieve program schedule after extended periods of user inactivity.
|
||||||
|
|
|
@ -152,10 +152,6 @@ fi
|
||||||
if [ -e /etc/init.d/airtime-playout ]; then
|
if [ -e /etc/init.d/airtime-playout ]; then
|
||||||
invoke-rc.d airtime-playout stop > /dev/null 2>&1
|
invoke-rc.d airtime-playout stop > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
if [ -e /etc/init.d/airtime-show-recorder ]; then
|
|
||||||
invoke-rc.d airtime-show-recorder stop > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
#export these variables to make them available in sub bash scripts
|
#export these variables to make them available in sub bash scripts
|
||||||
export DO_UPGRADE
|
export DO_UPGRADE
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
define('AIRTIME_VERSION', '2.1.2');
|
define('AIRTIME_VERSION', '2.1.3');
|
||||||
|
|
|
@ -111,6 +111,10 @@ if (strcmp($version, "2.1.1") < 0){
|
||||||
if (strcmp($version, "2.1.2") < 0){
|
if (strcmp($version, "2.1.2") < 0){
|
||||||
passthru("php --php-ini $SCRIPTPATH/../airtime-php.ini $SCRIPTPATH/../upgrades/airtime-2.1.2/airtime-upgrade.php");
|
passthru("php --php-ini $SCRIPTPATH/../airtime-php.ini $SCRIPTPATH/../upgrades/airtime-2.1.2/airtime-upgrade.php");
|
||||||
pause();
|
pause();
|
||||||
}
|
}
|
||||||
|
if (strcmp($version, "2.1.3") < 0){
|
||||||
|
passthru("php --php-ini $SCRIPTPATH/../airtime-php.ini $SCRIPTPATH/../upgrades/airtime-2.1.3/airtime-upgrade.php");
|
||||||
|
pause();
|
||||||
|
}
|
||||||
|
|
||||||
echo "******************************* Upgrade Complete *******************************".PHP_EOL;
|
echo "******************************* Upgrade Complete *******************************".PHP_EOL;
|
||||||
|
|
|
@ -20,7 +20,7 @@ from configobj import ConfigObj
|
||||||
import string
|
import string
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
AIRTIME_VERSION = "2.1.2"
|
AIRTIME_VERSION = "2.1.3"
|
||||||
|
|
||||||
def api_client_factory(config, logger=None):
|
def api_client_factory(config, logger=None):
|
||||||
if logger != None:
|
if logger != None:
|
||||||
|
|
|
@ -31,7 +31,7 @@ class MediaMonitorCommon:
|
||||||
|
|
||||||
def is_temp_file(self, filename):
|
def is_temp_file(self, filename):
|
||||||
info = filename.split(".")
|
info = filename.split(".")
|
||||||
|
|
||||||
# if file doesn't have any extension, info[-2] throws exception
|
# if file doesn't have any extension, info[-2] throws exception
|
||||||
# Hence, checking length of info before we do anything
|
# Hence, checking length of info before we do anything
|
||||||
if(len(info) >= 2):
|
if(len(info) >= 2):
|
||||||
|
@ -56,7 +56,7 @@ class MediaMonitorCommon:
|
||||||
try:
|
try:
|
||||||
uid = pwd.getpwnam(euid)[2]
|
uid = pwd.getpwnam(euid)[2]
|
||||||
gid = grp.getgrnam(egid)[2]
|
gid = grp.getgrnam(egid)[2]
|
||||||
|
|
||||||
#drop root permissions and become "nobody"
|
#drop root permissions and become "nobody"
|
||||||
os.setegid(gid)
|
os.setegid(gid)
|
||||||
os.seteuid(uid)
|
os.seteuid(uid)
|
||||||
|
@ -85,15 +85,15 @@ class MediaMonitorCommon:
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.logger.warn(u"Failed to check owner/group/permissions for %s", item)
|
self.logger.warn(u"Failed to check owner/group/permissions for %s", item)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def make_file_readable(self, pathname, is_dir):
|
def make_file_readable(self, pathname, is_dir):
|
||||||
if is_dir:
|
if is_dir:
|
||||||
#set to 755
|
#set to 755
|
||||||
os.chmod(pathname, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR | stat.S_IRGRP|stat.S_IXGRP | stat.S_IROTH|stat.S_IXOTH)
|
os.chmod(pathname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
||||||
else:
|
else:
|
||||||
#set to 644
|
#set to 644
|
||||||
os.chmod(pathname, stat.S_IRUSR|stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
|
os.chmod(pathname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
|
||||||
|
|
||||||
def make_readable(self, pathname):
|
def make_readable(self, pathname):
|
||||||
"""
|
"""
|
||||||
Should only call this function if is_readable() returns False. This function
|
Should only call this function if is_readable() returns False. This function
|
||||||
|
@ -120,7 +120,7 @@ class MediaMonitorCommon:
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
#something went wrong while we were trying to make world readable.
|
#something went wrong while we were trying to make world readable.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
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.
|
||||||
|
@ -165,7 +165,7 @@ class MediaMonitorCommon:
|
||||||
#non-critical exception because we probably tried to delete a non-empty dir.
|
#non-critical exception because we probably tried to delete a non-empty dir.
|
||||||
#Don't need to log this, let's just "return"
|
#Don't need to log this, let's just "return"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#checks if path exists already in stor. If the path exists and the md5s are the
|
#checks if path exists already in stor. If the path exists and the md5s are the
|
||||||
|
@ -195,7 +195,7 @@ class MediaMonitorCommon:
|
||||||
self.logger.error("Trying %s", new_filepath)
|
self.logger.error("Trying %s", new_filepath)
|
||||||
|
|
||||||
if(os.path.exists(new_filepath)):
|
if(os.path.exists(new_filepath)):
|
||||||
i = i+1;
|
i = i + 1;
|
||||||
else:
|
else:
|
||||||
filepath = new_filepath
|
filepath = new_filepath
|
||||||
break
|
break
|
||||||
|
@ -231,7 +231,7 @@ class MediaMonitorCommon:
|
||||||
md['MDATA_KEY_TRACKNUMBER'] = "%02d" % (int(md['MDATA_KEY_TRACKNUMBER']))
|
md['MDATA_KEY_TRACKNUMBER'] = "%02d" % (int(md['MDATA_KEY_TRACKNUMBER']))
|
||||||
|
|
||||||
#format bitrate as 128kbps
|
#format bitrate as 128kbps
|
||||||
md['MDATA_KEY_BITRATE'] = str(md['MDATA_KEY_BITRATE']/1000)+"kbps"
|
md['MDATA_KEY_BITRATE'] = str(md['MDATA_KEY_BITRATE'] / 1000) + "kbps"
|
||||||
|
|
||||||
filepath = None
|
filepath = None
|
||||||
#file is recorded by Airtime
|
#file is recorded by Airtime
|
||||||
|
@ -274,7 +274,7 @@ class MediaMonitorCommon:
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
self.logger.warn("command \n%s\n return with a non-zero return value", command)
|
self.logger.warn("command \n%s\n return with a non-zero return value", command)
|
||||||
self.logger.error(stderr)
|
self.logger.error(stderr)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
"""
|
"""
|
||||||
File name charset encoding is UTF-8.
|
File name charset encoding is UTF-8.
|
||||||
|
@ -283,14 +283,14 @@ class MediaMonitorCommon:
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
stdout = None
|
stdout = None
|
||||||
self.logger.error("Could not decode %s using UTF-8" % stdout)
|
self.logger.error("Could not decode %s using UTF-8" % stdout)
|
||||||
|
|
||||||
return stdout
|
return stdout
|
||||||
|
|
||||||
def scan_dir_for_new_files(self, dir):
|
def scan_dir_for_new_files(self, dir):
|
||||||
command = 'find "%s" -iname "*.ogg" -o -iname "*.mp3" -type f -readable' % dir.replace('"', '\\"')
|
command = 'find "%s" -iname "*.ogg" -o -iname "*.mp3" -type f -readable' % dir.replace('"', '\\"')
|
||||||
self.logger.debug(command)
|
self.logger.debug(command)
|
||||||
stdout = self.exec_command(command)
|
stdout = self.exec_command(command)
|
||||||
|
|
||||||
return stdout.splitlines()
|
return stdout.splitlines()
|
||||||
|
|
||||||
def touch_index_file(self):
|
def touch_index_file(self):
|
||||||
|
@ -312,7 +312,7 @@ class MediaMonitorCommon:
|
||||||
self.move_file(pathname, filepath)
|
self.move_file(pathname, filepath)
|
||||||
self.make_readable(filepath)
|
self.make_readable(filepath)
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
def test_file_playability(self, pathname):
|
def test_file_playability(self, pathname):
|
||||||
#when there is an single apostrophe inside of a string quoted by apostrophes, we can only escape it by replace that apostrophe
|
#when there is an single apostrophe inside of a string quoted by apostrophes, we can only escape it by replace that apostrophe
|
||||||
#with '\''. This breaks the string into two, and inserts an escaped single quote in between them.
|
#with '\''. This breaks the string into two, and inserts an escaped single quote in between them.
|
||||||
|
@ -323,13 +323,13 @@ class MediaMonitorCommon:
|
||||||
if return_code != 0:
|
if return_code != 0:
|
||||||
#print pathname for py-interpreter.log
|
#print pathname for py-interpreter.log
|
||||||
print pathname
|
print pathname
|
||||||
|
|
||||||
return (return_code == 0)
|
return (return_code == 0)
|
||||||
|
|
||||||
def move_to_problem_dir(self, source):
|
def move_to_problem_dir(self, source):
|
||||||
|
|
||||||
dest = os.path.join(self.config.problem_directory, os.path.basename(source))
|
dest = os.path.join(self.config.problem_directory, os.path.basename(source))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
omask = os.umask(0)
|
omask = os.umask(0)
|
||||||
os.rename(source, dest)
|
os.rename(source, dest)
|
||||||
|
@ -338,4 +338,4 @@ class MediaMonitorCommon:
|
||||||
self.logger.error("traceback: %s", traceback.format_exc())
|
self.logger.error("traceback: %s", traceback.format_exc())
|
||||||
finally:
|
finally:
|
||||||
os.umask(omask)
|
os.umask(omask)
|
||||||
|
|
||||||
|
|
|
@ -429,7 +429,7 @@ class PypoFetch(Thread):
|
||||||
fileExt = os.path.splitext(media_item['uri'])[1]
|
fileExt = os.path.splitext(media_item['uri'])[1]
|
||||||
dst = os.path.join(download_dir, media_item['id'] + fileExt)
|
dst = os.path.join(download_dir, media_item['id'] + fileExt)
|
||||||
media_item['dst'] = dst
|
media_item['dst'] = dst
|
||||||
media_item['started_copying'] = False
|
media_item['file_ready'] = False
|
||||||
media_filtered[key] = media_item
|
media_filtered[key] = media_item
|
||||||
|
|
||||||
self.media_prepare_queue.put(copy.copy(media_filtered))
|
self.media_prepare_queue.put(copy.copy(media_filtered))
|
||||||
|
|
|
@ -60,22 +60,28 @@ class PypoFile(Thread):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
dst_exists = False
|
dst_exists = False
|
||||||
|
|
||||||
media_item['already_exist'] = False
|
|
||||||
do_copy = False
|
do_copy = False
|
||||||
if dst_exists:
|
if dst_exists:
|
||||||
if src_size != dst_size:
|
if src_size != dst_size:
|
||||||
do_copy = True
|
do_copy = True
|
||||||
else:
|
else:
|
||||||
self.logger.debug("file %s already exists in local cache as %s, skipping cpoying..." % (src, dst))
|
self.logger.debug("file %s already exists in local cache as %s, skipping copying..." % (src, dst))
|
||||||
media_item['already_exist'] = True
|
|
||||||
else:
|
else:
|
||||||
do_copy = True
|
do_copy = True
|
||||||
|
|
||||||
|
media_item['file_ready'] = not do_copy
|
||||||
|
|
||||||
if do_copy:
|
if do_copy:
|
||||||
self.logger.debug("copying from %s to local cache %s" % (src, dst))
|
self.logger.debug("copying from %s to local cache %s" % (src, dst))
|
||||||
try:
|
try:
|
||||||
|
|
||||||
media_item['started_copying'] = True
|
"""
|
||||||
|
List file as "ready" before it starts copying because by the time
|
||||||
|
Liquidsoap is ready to play this file, it should have at least started
|
||||||
|
copying (and can continue copying while Liquidsoap reads from the beginning
|
||||||
|
of the file)
|
||||||
|
"""
|
||||||
|
media_item['file_ready'] = True
|
||||||
|
|
||||||
"""
|
"""
|
||||||
copy will overwrite dst if it already exists
|
copy will overwrite dst if it already exists
|
||||||
|
|
|
@ -340,11 +340,11 @@ class PypoPush(Thread):
|
||||||
give up on it.
|
give up on it.
|
||||||
"""
|
"""
|
||||||
iter_num = 0
|
iter_num = 0
|
||||||
while not media_item['started_copying'] and iter_num < 50:
|
while not media_item['file_ready'] and iter_num < 50:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
iter_num += 1
|
iter_num += 1
|
||||||
|
|
||||||
if media_item['started_copying'] or media_item['already_exist']:
|
if media_item['file_ready']:
|
||||||
self.telnet_to_liquidsoap(media_item)
|
self.telnet_to_liquidsoap(media_item)
|
||||||
else:
|
else:
|
||||||
self.logger.warn("File %s did not become ready in less than 5 seconds. Skipping...", media_item['dst'])
|
self.logger.warn("File %s did not become ready in less than 5 seconds. Skipping...", media_item['dst'])
|
||||||
|
|
Loading…
Reference in New Issue