diff --git a/CREDITS b/CREDITS index 3a15126a2..d303519ef 100644 --- a/CREDITS +++ b/CREDITS @@ -1,3 +1,10 @@ +======= +CREDITS +======= +Version 2.1.3 +------------- +Same as previous version. + ======= CREDITS ======= diff --git a/VERSION b/VERSION index 0b9f4ef5f..682777c6f 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ PRODUCT_ID=Airtime -PRODUCT_RELEASE=2.1.2 +PRODUCT_RELEASE=2.1.3 diff --git a/changelog b/changelog index 579c4db28..96eb74af9 100644 --- a/changelog +++ b/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 * Bug Fixes * Fixed problem where playout engine may not retrieve program schedule after extended periods of user inactivity. diff --git a/install_minimal/airtime-install b/install_minimal/airtime-install index 43302b604..86691c427 100755 --- a/install_minimal/airtime-install +++ b/install_minimal/airtime-install @@ -152,10 +152,6 @@ fi if [ -e /etc/init.d/airtime-playout ]; then invoke-rc.d airtime-playout stop > /dev/null 2>&1 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 DO_UPGRADE diff --git a/install_minimal/include/airtime-constants.php b/install_minimal/include/airtime-constants.php index c33d25441..1d09fca25 100644 --- a/install_minimal/include/airtime-constants.php +++ b/install_minimal/include/airtime-constants.php @@ -1,3 +1,3 @@ = 2): @@ -56,7 +56,7 @@ class MediaMonitorCommon: try: uid = pwd.getpwnam(euid)[2] gid = grp.getgrnam(egid)[2] - + #drop root permissions and become "nobody" os.setegid(gid) os.seteuid(uid) @@ -85,15 +85,15 @@ class MediaMonitorCommon: except Exception, e: self.logger.warn(u"Failed to check owner/group/permissions for %s", item) return False - + def make_file_readable(self, pathname, is_dir): if is_dir: #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: #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): """ Should only call this function if is_readable() returns False. This function @@ -120,7 +120,7 @@ class MediaMonitorCommon: except Exception, e: #something went wrong while we were trying to make world readable. return False - + return True #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. #Don't need to log this, let's just "return" pass - + #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) if(os.path.exists(new_filepath)): - i = i+1; + i = i + 1; else: filepath = new_filepath break @@ -231,7 +231,7 @@ class MediaMonitorCommon: 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" + md['MDATA_KEY_BITRATE'] = str(md['MDATA_KEY_BITRATE'] / 1000) + "kbps" filepath = None #file is recorded by Airtime @@ -274,7 +274,7 @@ class MediaMonitorCommon: if p.returncode != 0: self.logger.warn("command \n%s\n return with a non-zero return value", command) self.logger.error(stderr) - + try: """ File name charset encoding is UTF-8. @@ -283,14 +283,14 @@ class MediaMonitorCommon: except Exception, e: stdout = None self.logger.error("Could not decode %s using UTF-8" % stdout) - + return stdout def scan_dir_for_new_files(self, dir): command = 'find "%s" -iname "*.ogg" -o -iname "*.mp3" -type f -readable' % dir.replace('"', '\\"') self.logger.debug(command) stdout = self.exec_command(command) - + return stdout.splitlines() def touch_index_file(self): @@ -312,7 +312,7 @@ class MediaMonitorCommon: self.move_file(pathname, filepath) self.make_readable(filepath) return filepath - + 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 #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: #print pathname for py-interpreter.log print pathname - + return (return_code == 0) - + def move_to_problem_dir(self, source): - + dest = os.path.join(self.config.problem_directory, os.path.basename(source)) - + try: omask = os.umask(0) os.rename(source, dest) @@ -338,4 +338,4 @@ class MediaMonitorCommon: self.logger.error("traceback: %s", traceback.format_exc()) finally: os.umask(omask) - + diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index 4e2cfaf29..4e350d59c 100644 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -429,7 +429,7 @@ class PypoFetch(Thread): fileExt = os.path.splitext(media_item['uri'])[1] dst = os.path.join(download_dir, media_item['id'] + fileExt) media_item['dst'] = dst - media_item['started_copying'] = False + media_item['file_ready'] = False media_filtered[key] = media_item self.media_prepare_queue.put(copy.copy(media_filtered)) diff --git a/python_apps/pypo/pypofile.py b/python_apps/pypo/pypofile.py index cf8e199e9..67e829464 100644 --- a/python_apps/pypo/pypofile.py +++ b/python_apps/pypo/pypofile.py @@ -60,22 +60,28 @@ class PypoFile(Thread): except Exception, e: dst_exists = False - media_item['already_exist'] = False do_copy = False if dst_exists: if src_size != dst_size: do_copy = True else: - self.logger.debug("file %s already exists in local cache as %s, skipping cpoying..." % (src, dst)) - media_item['already_exist'] = True + self.logger.debug("file %s already exists in local cache as %s, skipping copying..." % (src, dst)) else: do_copy = True + media_item['file_ready'] = not do_copy + if do_copy: self.logger.debug("copying from %s to local cache %s" % (src, dst)) 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 diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py index cb71937a3..67191f81f 100644 --- a/python_apps/pypo/pypopush.py +++ b/python_apps/pypo/pypopush.py @@ -340,11 +340,11 @@ class PypoPush(Thread): give up on it. """ 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) iter_num += 1 - if media_item['started_copying'] or media_item['already_exist']: + if media_item['file_ready']: self.telnet_to_liquidsoap(media_item) else: self.logger.warn("File %s did not become ready in less than 5 seconds. Skipping...", media_item['dst'])