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/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'); } diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index de81f04f7..eb538f57f 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) @@ -83,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 = << $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 +832,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/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'); diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index 22968fcf0..c8749d48d 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 @@ -41,6 +44,22 @@ airtime2mutagen = { "MDATA_KEY_COPYRIGHT" : "copyright", } +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 + """ + FakeInfo = namedtuple('FakeInfo','length bitrate') + def __init__(self,path): + self.path = path + 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 # key should attempt to extract the corresponding value from the mutagen object @@ -178,14 +197,29 @@ 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 = FakeMutagen(fpath) 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 as e: + 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: 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)