From 8d08aa888b8c93301bbfbacb82f6104eb3946e07 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 14:54:52 -0400 Subject: [PATCH 01/13] cc-4421: Added special handling for files that mutagen returns None for. --- .../application/controllers/ApiController.php | 2 +- .../media-monitor2/media/monitor/metadata.py | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index c22e63d55..94cb7e518 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -901,7 +901,7 @@ class ApiController extends Zend_Controller_Action foreach ($data as $pair) { list($id, $gain) = $pair; - + // TODO : move this code into model -- RG $file = Application_Model_StoredFile::Recall($p_id = $id)->getPropelOrm(); $file->setDbReplayGain($gain); $file->save(); diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index 22968fcf0..36a02e097 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -178,14 +178,19 @@ class Metadata(Loggable): return # TODO : Simplify the way all of these rules are handled right not it's # extremely unclear and needs to be refactored. - if full_mutagen is None: raise BadSongFile(fpath) + #if full_mutagen is None: raise BadSongFile(fpath) + if full_mutagen is None: full_mutagen = {} self.__metadata = Metadata.airtime_dict(full_mutagen) # Now we extra the special values that are calculated from the mutagen # object itself: for special_key,f in airtime_special.iteritems(): - new_val = f(full_mutagen) - if new_val is not None: - self.__metadata[special_key] = new_val + try: + new_val = f(full_mutagen) + if new_val is not None: + self.__metadata[special_key] = new_val + except Exception: + self.logger.info("Could not get special key %s for %d" % + special_key, fpath) # Finally, we "normalize" all the metadata here: self.__metadata = mmp.normalized_metadata(self.__metadata, fpath) # Now we must load the md5: From 05f4bb64b238c5f4fc627167b6aa83bcbceb43f3 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 15:05:36 -0400 Subject: [PATCH 02/13] cc-4421: Created a fake mutagen object for files that mutagen does not recognize --- .../media-monitor2/media/monitor/metadata.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index 36a02e097..bb26485ef 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -41,6 +41,17 @@ airtime2mutagen = { "MDATA_KEY_COPYRIGHT" : "copyright", } +class FakeMutagen(object): + """ + Need this fake mutagen object so that airtime_special functions return a + proper default value instead of throwing an exceptions for files that + mutagen doesn't recognize + """ + def __init__(self,path): + self.path = path + self.mime = True + self.info = True + # Some airtime attributes are special because they must use the mutagen object # itself to calculate the value that they need. The lambda associated with each # key should attempt to extract the corresponding value from the mutagen object @@ -179,7 +190,7 @@ class Metadata(Loggable): # TODO : Simplify the way all of these rules are handled right not it's # extremely unclear and needs to be refactored. #if full_mutagen is None: raise BadSongFile(fpath) - if full_mutagen is None: full_mutagen = {} + if full_mutagen is None: full_mutagen = FakeMutagen(fpath) self.__metadata = Metadata.airtime_dict(full_mutagen) # Now we extra the special values that are calculated from the mutagen # object itself: @@ -188,9 +199,10 @@ class Metadata(Loggable): new_val = f(full_mutagen) if new_val is not None: self.__metadata[special_key] = new_val - except Exception: + except Exception as e: self.logger.info("Could not get special key %s for %d" % special_key, fpath) + self.logger.info(str(e)) # Finally, we "normalize" all the metadata here: self.__metadata = mmp.normalized_metadata(self.__metadata, fpath) # Now we must load the md5: From f754d78687c4bbc675e49a5bc52c48629c8bf39e Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 15:12:54 -0400 Subject: [PATCH 03/13] Formatted comments and = --- airtime_mvc/application/models/Schedule.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index de81f04f7..7c86a57d3 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -61,11 +61,13 @@ SQL; } /** - * Queries the database for the set of schedules one hour before and after the given time. - * If a show starts and ends within that time that is considered the current show. Then the - * scheduled item before it is the previous show, and the scheduled item after it is the next - * show. This way the dashboard getCurrentPlaylist is very fast. But if any one of the three - * show types are not found through this mechanism a call is made to the old way of querying + * Queries the database for the set of schedules one hour before + * and after the given time. If a show starts and ends within that + * time that is considered the current show. Then the scheduled item + * before it is the previous show, and the scheduled item after it + * is the next show. This way the dashboard getCurrentPlaylist is + * very fast. But if any one of the three show types are not found + * through this mechanism a call is made to the old way of querying * the database to find the track info. **/ public static function GetPrevCurrentNext($p_previousShowID, $p_currentShowID, $p_nextShowID, $p_timeNow) @@ -118,8 +120,8 @@ SQL; $numberOfRows = count($rows); $results['previous'] = null; - $results['current'] = null; - $results['next'] = null; + $results['current'] = null; + $results['next'] = null; $timeNowAsMillis = strtotime($p_timeNow); for ($i = 0; $i < $numberOfRows; ++$i) { From e31d570a1c69ba2ef34e64b67b4e535671d985c4 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 15:13:37 -0400 Subject: [PATCH 04/13] Sql formatting --- airtime_mvc/application/models/Schedule.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 7c86a57d3..3b23548c3 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -85,10 +85,16 @@ SQL; LEFT JOIN cc_show_instances si ON st.instance_id = si.id"; $streamColumns = "ws.name AS artist_name, wm.liquidsoap_data AS track_title, "; - $streamJoin = "FROM cc_schedule AS st JOIN cc_webstream ws ON st.stream_id = ws.id - LEFT JOIN cc_show_instances AS si ON st.instance_id = si.id - LEFT JOIN cc_subjs AS sub on sub.id = ws.creator_id - LEFT JOIN (SELECT * FROM cc_webstream_metadata ORDER BY start_time DESC LIMIT 1) AS wm on st.id = wm.instance_id"; + $streamJoin = << Date: Thu, 13 Sep 2012 15:16:46 -0400 Subject: [PATCH 05/13] comment formatting --- airtime_mvc/application/models/Schedule.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 3b23548c3..980c941d4 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -486,8 +486,9 @@ SQL; } /** - * Compute the difference between two times in the format "HH:MM:SS.mmmmmm". - * Note: currently only supports calculating millisec differences. + * Compute the difference between two times in the format . + * "HH:MM:SS.mmmmmm" Note: currently only supports calculating . + * millisec differences . * * @param string $p_time1 * @param string $p_time2 From 9eaa05274462c4f8db08bd21bdf2460c4aaeef2c Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 15:38:30 -0400 Subject: [PATCH 06/13] FakeMutagen inherits from dict --- python_apps/media-monitor2/media/monitor/metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index bb26485ef..d1bd9c0be 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -41,7 +41,7 @@ airtime2mutagen = { "MDATA_KEY_COPYRIGHT" : "copyright", } -class FakeMutagen(object): +class FakeMutagen(dict): """ Need this fake mutagen object so that airtime_special functions return a proper default value instead of throwing an exceptions for files that @@ -51,6 +51,7 @@ class FakeMutagen(object): self.path = path self.mime = True self.info = True + dict.__init__(self) # Some airtime attributes are special because they must use the mutagen object # itself to calculate the value that they need. The lambda associated with each From 1e76b6c751095e6e8509695aae2a529fe8ae9200 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 15:45:30 -0400 Subject: [PATCH 07/13] Fixed typo --- python_apps/media-monitor2/media/monitor/metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index d1bd9c0be..8ac0ca98f 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -201,7 +201,7 @@ class Metadata(Loggable): if new_val is not None: self.__metadata[special_key] = new_val except Exception as e: - self.logger.info("Could not get special key %s for %d" % + self.logger.info("Could not get special key %s for %s" % special_key, fpath) self.logger.info(str(e)) # Finally, we "normalize" all the metadata here: From 251e0f054a5375b3e46c8b2eb4aa3ae0e6510f09 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 15:48:36 -0400 Subject: [PATCH 08/13] Typo --- python_apps/media-monitor2/media/monitor/metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index 8ac0ca98f..bde17bc45 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -202,7 +202,7 @@ class Metadata(Loggable): self.__metadata[special_key] = new_val except Exception as e: self.logger.info("Could not get special key %s for %s" % - special_key, fpath) + (special_key, fpath)) self.logger.info(str(e)) # Finally, we "normalize" all the metadata here: self.__metadata = mmp.normalized_metadata(self.__metadata, fpath) From da72c00f28b9b574698cadff9f7d54854dbb56be Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Thu, 13 Sep 2012 16:10:17 -0400 Subject: [PATCH 09/13] CC-4370: Transitioning between two webstreams: a hiccup in the stream is audible -fixed --- airtime_mvc/application/models/Schedule.php | 26 +++++++++++++++++++++ python_apps/pypo/pypopush.py | 21 +++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index de81f04f7..fae6b5b94 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -789,6 +789,30 @@ SQL; } } + /** + * Purpose of this function is to iterate through the entire + * schedule array that was just built and fix the data up a bit. For + * example, if we have two consecutive webstreams, we don't need the + * first webstream to shutdown the output, when the second one will + * just switch it back on. Preventing this behaviour stops hiccups + * in output sound. + */ + private static function filterData(&$data) + { + $previous_key = null; + $previous_val = null; + foreach ($data as $k => $v) { + if ($v["type"] == "stream_buffer_start" + && !is_null($previous_val) + && $previous_val["type"] == "stream_output_end") { + + unset($data[$previous_key]); + } + $previous_key = $k; + $previous_val = $v; + } + } + public static function getSchedule($p_fromDateTime = null, $p_toDateTime = null) { list($range_start, $range_end) = self::getRangeStartAndEnd($p_fromDateTime, $p_toDateTime); @@ -799,6 +823,8 @@ SQL; self::createInputHarborKickTimes($data, $range_start, $range_end); self::createScheduledEvents($data, $range_start, $range_end); + self::filterData($data["media"]); + return $data; } diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py index 5a5350823..1a9672872 100644 --- a/python_apps/pypo/pypopush.py +++ b/python_apps/pypo/pypopush.py @@ -434,7 +434,9 @@ class PypoPush(Thread): self.start_web_stream_buffer(media_item) self.start_web_stream(media_item) elif media_item['type'] == "stream_buffer_end": - self.stop_web_stream(media_item) + self.stop_web_stream_buffer(media_item) + elif media_item['type'] == "stream_output_end": + self.stop_web_stream_output(media_item) except Exception, e: self.logger.error('Pypo Push Exception: %s', e) @@ -509,7 +511,7 @@ class PypoPush(Thread): finally: self.telnet_lock.release() - def stop_web_stream(self, media_item): + def stop_web_stream_buffer(self, media_item): try: self.telnet_lock.acquire() tn = telnetlib.Telnet(LS_HOST, LS_PORT) @@ -519,6 +521,21 @@ class PypoPush(Thread): self.logger.debug(msg) tn.write(msg) + tn.write("exit\n") + self.logger.debug(tn.read_all()) + + self.current_stream_info = None + except Exception, e: + self.logger.error(str(e)) + finally: + self.telnet_lock.release() + + def stop_web_stream_output(self, media_item): + try: + self.telnet_lock.acquire() + tn = telnetlib.Telnet(LS_HOST, LS_PORT) + #dynamic_source.stop http://87.230.101.24:80/top100station.mp3 + msg = 'dynamic_source.output_stop\n' self.logger.debug(msg) tn.write(msg) From 505b53fa16e34ffd0c862c0e45abfe994e1a8652 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Thu, 13 Sep 2012 16:11:06 -0400 Subject: [PATCH 10/13] cc-1665: fix broken checkin for webstream --- .../application/controllers/WebstreamController.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/application/controllers/WebstreamController.php b/airtime_mvc/application/controllers/WebstreamController.php index 0a691246f..0521d58b1 100644 --- a/airtime_mvc/application/controllers/WebstreamController.php +++ b/airtime_mvc/application/controllers/WebstreamController.php @@ -24,7 +24,7 @@ class WebstreamController extends Zend_Controller_Action $webstream = new CcWebstream(); - //we're not saving this primary key in the DB so it's OK + //we're not saving this primary key in the DB so it's OK to be -1 $webstream->setDbId(-1); $webstream->setDbName("Untitled Webstream"); $webstream->setDbDescription(""); @@ -34,7 +34,6 @@ class WebstreamController extends Zend_Controller_Action $webstream->setDbCreatorId($userInfo->id); $webstream->setDbUtime(new DateTime("now", new DateTimeZone('UTC'))); $webstream->setDbMtime(new DateTime("now", new DateTimeZone('UTC'))); - //$webstream->save(); /* $type = "stream"; @@ -50,7 +49,7 @@ class WebstreamController extends Zend_Controller_Action //clear the session in case an old playlist was open: CC-4196 Application_Model_Library::changePlaylist(null, null); - $this->view->obj = new Application_Model_Webstream($webstream->getDbId()); + $this->view->obj = new Application_Model_Webstream($webstream); $this->view->action = "new"; $this->view->html = $this->view->render('webstream/webstream.phtml'); } @@ -68,7 +67,7 @@ class WebstreamController extends Zend_Controller_Action if ($webstream) { Application_Model_Library::changePlaylist($id, "stream"); } - $this->view->obj = new Application_Model_Webstream($webstream->getDbId()); + $this->view->obj = new Application_Model_Webstream($webstream); $this->view->action = "edit"; $this->view->html = $this->view->render('webstream/webstream.phtml'); } From 4349dbe4fdd47f3b70fe68695701fb36784237cf Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 16:26:31 -0400 Subject: [PATCH 11/13] Added special handling for wav --- .../media-monitor2/media/monitor/metadata.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index bde17bc45..aef5f8793 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -3,6 +3,9 @@ import mutagen import math import os import copy +import wave +import contextlib +from collections import namedtuple from mutagen.easymp4 import EasyMP4KeyError from media.monitor.exceptions import BadSongFile @@ -43,15 +46,19 @@ airtime2mutagen = { class FakeMutagen(dict): """ - Need this fake mutagen object so that airtime_special functions return a - proper default value instead of throwing an exceptions for files that - mutagen doesn't recognize + Need this fake mutagen object so that airtime_special functions + return a proper default value instead of throwing an exceptions for + files that mutagen doesn't recognize """ + FakeInfo = namedtuple('FakeInfo','length bitrate') def __init__(self,path): self.path = path - self.mime = True - self.info = True + self.mime = [] + self.info = FakeMutagen.FakeInfo(0.0, '') dict.__init__(self) + def set_length(self,l): + old_bitrate = self.info.bitrate + self.info = FakeMutagen.FakeInfo(l, old_bitrate) # Some airtime attributes are special because they must use the mutagen object # itself to calculate the value that they need. The lambda associated with each @@ -204,6 +211,15 @@ class Metadata(Loggable): self.logger.info("Could not get special key %s for %s" % (special_key, fpath)) self.logger.info(str(e)) + + # Hickity Hackity for .wav files. Properly do this later + if mmp.extension(fpath) == 'wav': + with contextlib.closing(wave.open(fpath,'r')) as f: + frames=f.getnframes() + rate=f.getframerate() + duration=frames/float(rate) + full_mutagen.set_length(duration) + # Finally, we "normalize" all the metadata here: self.__metadata = mmp.normalized_metadata(self.__metadata, fpath) # Now we must load the md5: From 8e0f0033095cfce6c4ba93b4dbfab686baa2d943 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Thu, 13 Sep 2012 16:28:38 -0400 Subject: [PATCH 12/13] Formatting --- python_apps/media-monitor2/media/monitor/metadata.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index aef5f8793..c8749d48d 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -215,9 +215,9 @@ class Metadata(Loggable): # Hickity Hackity for .wav files. Properly do this later if mmp.extension(fpath) == 'wav': with contextlib.closing(wave.open(fpath,'r')) as f: - frames=f.getnframes() - rate=f.getframerate() - duration=frames/float(rate) + frames = f.getnframes() + rate = f.getframerate() + duration = frames/float(rate) full_mutagen.set_length(duration) # Finally, we "normalize" all the metadata here: From 3a0907b93ba496c1c8f0121e58491a0c858d3587 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Thu, 13 Sep 2012 16:47:45 -0400 Subject: [PATCH 13/13] CC-4428: Make sure to remove *_datatable column on upgrade from cc_pref -fixed --- install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql b/install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql index ab129de75..71f59a40d 100644 --- a/install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql +++ b/install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql @@ -1,6 +1,9 @@ DELETE FROM cc_pref WHERE keystr = 'system_version'; INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '2.2.0'); +--DELETE user column order prefs, since the number of columns has increased in 2.2 +DELETE FROM cc_pref where keystr = 'library_datatable'; +DELETE FROM cc_pref where keystr = 'timeline_datatable'; INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s1_name', 'Airtime!', 'string'); INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s2_name', '', 'string');