From 6ef8d21e85c31e53587a01cf87694a37d022b4a3 Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 22 Feb 2011 23:02:56 -0500 Subject: [PATCH 1/3] -changed audio device out to disabled by default. --- pypo/scripts/ls_config.liq | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypo/scripts/ls_config.liq b/pypo/scripts/ls_config.liq index 3d09c7895..ef1caa198 100644 --- a/pypo/scripts/ls_config.liq +++ b/pypo/scripts/ls_config.liq @@ -43,6 +43,6 @@ icecast_url = "http://airtime.sourcefabric.org" icecast_description = "Airtime Radio!" icecast_genre = "genre" -output_sound_device = true +output_sound_device = false output_icecast_vorbis = true output_icecast_mp3 = true From 6b615b4e70bad1c96589fe09c0b1822ff3d430d1 Mon Sep 17 00:00:00 2001 From: martin Date: Wed, 23 Feb 2011 12:24:38 -0500 Subject: [PATCH 2/3] -small js fix for IE8 --- public/js/playlist/playlist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/playlist/playlist.js b/public/js/playlist/playlist.js index e4aced3d6..45d847848 100644 --- a/public/js/playlist/playlist.js +++ b/public/js/playlist/playlist.js @@ -247,7 +247,7 @@ function init() { width: 0, radius: 4 }, - name: 'light', // Use the default light style + name: 'light' // Use the default light style } }); } From dab39f391e5c695d9347ee857f0ca3a922fcd218 Mon Sep 17 00:00:00 2001 From: martin Date: Wed, 23 Feb 2011 17:03:27 -0500 Subject: [PATCH 3/3] -interface is now notified when a song starts playing. ON-AIR graphic only turns red after this notification. --- application/controllers/ApiController.php | 21 +- application/models/Schedule.php | 17 +- public/js/playlist/playlist.js | 12 +- pypo/api_clients/api_client.py | 1025 ++++++++++----------- pypo/logging.cfg | 3 +- pypo/pypo-cli.py | 14 +- pypo/pypo-notify.py | 2 +- pypo/scripts/ls_script.liq | 9 +- 8 files changed, 555 insertions(+), 548 deletions(-) diff --git a/application/controllers/ApiController.php b/application/controllers/ApiController.php index 1a46506b3..22c97fe2e 100644 --- a/application/controllers/ApiController.php +++ b/application/controllers/ApiController.php @@ -149,25 +149,12 @@ class ApiController extends Zend_Controller_Action $schedule_group_id = $this->_getParam("schedule_id"); $media_id = $this->_getParam("media_id"); - $f = StoredFile::RecallByGunid($media_id); + $result = Schedule::UpdateMediaPlayedStatus($media_id); - if (is_numeric($schedule_group_id)) { - $sg = new ScheduleGroup($schedule_group_id); - if ($sg->exists()) { - $result = $sg->notifyMediaItemStartPlay($f->getId()); - if (!PEAR::isError($result)) { - echo json_encode(array("status"=>1, "message"=>"")); - exit; - } else { - echo json_encode(array("status"=>0, "message"=>"DB Error:".$result->getMessage())); - exit; - } - } else { - echo json_encode(array("status"=>0, "message"=>"Schedule group does not exist: ".$schedule_group_id)); - exit; - } + if (!PEAR::isError($result)) { + echo json_encode(array("status"=>1, "message"=>"")); } else { - echo json_encode(array("status"=>0, "message" => "Incorrect or non-numeric arguments given.")); + echo json_encode(array("status"=>0, "message"=>"DB Error:".$result->getMessage())); } } diff --git a/application/models/Schedule.php b/application/models/Schedule.php index aedbadc0a..4ae0f5008 100644 --- a/application/models/Schedule.php +++ b/application/models/Schedule.php @@ -400,8 +400,8 @@ class Schedule { ." SUM(clip_length) AS clip_length," ." MIN(file_id) AS file_id, COUNT(*) as count," ." MIN(playlist_id) AS playlist_id, MIN(starts) AS starts, MAX(ends) AS ends" - ." FROM ".$CC_CONFIG["scheduleTable"] - ." LEFT JOIN ".$CC_CONFIG["playListTable"]." ON playlist_id = ".$CC_CONFIG["playListTable"].".id" + ." FROM $CC_CONFIG[scheduleTable]" + ." LEFT JOIN $CC_CONFIG[playListTable] ON playlist_id = $CC_CONFIG[playListTable].id" ." WHERE (starts >= TIMESTAMP '$p_fromDateTime') AND (ends <= TIMESTAMP '$p_toDateTime')" ." GROUP BY group_id" ." ORDER BY starts"; @@ -473,7 +473,7 @@ class Schedule { public static function Get_Scheduled_Item_Data($timeStamp, $timePeriod=0, $count = 0, $interval="0 hours"){ global $CC_CONFIG, $CC_DBC; - $sql = "SELECT DISTINCT pt.name, ft.track_title, ft.artist_name, ft.album_title, st.starts, st.ends, st.clip_length, st.group_id, show.name as show_name, st.instance_id" + $sql = "SELECT DISTINCT pt.name, ft.track_title, ft.artist_name, ft.album_title, st.starts, st.ends, st.clip_length, st.media_item_played, st.group_id, show.name as show_name, st.instance_id" ." FROM $CC_CONFIG[scheduleTable] st, $CC_CONFIG[filesTable] ft, $CC_CONFIG[playListTable] pt, $CC_CONFIG[showInstances] si, $CC_CONFIG[showTable] show" ." WHERE st.playlist_id = pt.id" ." AND st.file_id = ft.id" @@ -499,6 +499,14 @@ class Schedule { return $rows; } + public static function UpdateMediaPlayedStatus($id){ + global $CC_CONFIG, $CC_DBC; + $sql = "UPDATE ".$CC_CONFIG['scheduleTable'] + ." SET media_item_played=TRUE" + ." WHERE id=$id"; + return $CC_DBC->query($sql); + } + /** * Convert a time string in the format "YYYY-MM-DD HH:mm:SS" @@ -663,7 +671,8 @@ class Schedule { $cueOut = Schedule::WallTimeToMillisecs($item["cue_out"]); } $medias[] = array( - 'id' => $storedFile->getGunid(), //$item["file_id"], + 'row_id' => $item["id"], + 'id' => $storedFile->getGunid(), 'uri' => $uri, 'fade_in' => Schedule::WallTimeToMillisecs($item["fade_in"]), 'fade_out' => Schedule::WallTimeToMillisecs($item["fade_out"]), diff --git a/public/js/playlist/playlist.js b/public/js/playlist/playlist.js index 45d847848..434a19fdb 100644 --- a/public/js/playlist/playlist.js +++ b/public/js/playlist/playlist.js @@ -52,7 +52,9 @@ function newSongStart(){ nextSongPrepare = true; currentSong[0] = nextSongs.shift(); - notifySongStart(); + if (typeof notifySongStart == "function") + notifySongStart(); + } function nextShowStart(){ @@ -60,7 +62,8 @@ function nextShowStart(){ currentShow[0] = nextShow.shift(); //call function in nowplayingdatagrid.js - notifyShowStart(currentShow[0]); + if (typeof notifyShowStart == "function") + notifyShowStart(currentShow[0]); } /* Called every "uiUpdateInterval" mseconds. */ @@ -82,7 +85,10 @@ function updateProgressBarValue(){ songPercentDone = 0; currentSong = new Array(); } else { - $('#on-air-info').attr("class", "on-air-info on"); + if (currentSong[0].media_item_played == "t") + $('#on-air-info').attr("class", "on-air-info on"); + else + $('#on-air-info').attr("class", "on-air-info off"); $('#progress-show').attr("class", "progress-show"); } } else { diff --git a/pypo/api_clients/api_client.py b/pypo/api_clients/api_client.py index 0cc782c8e..4802a54ed 100644 --- a/pypo/api_clients/api_client.py +++ b/pypo/api_clients/api_client.py @@ -20,84 +20,84 @@ from urlparse import urlparse def api_client_factory(config): - if config["api_client"] == "airtime": - return AirTimeApiClient(config) - elif config["api_client"] == "obp": - return ObpApiClient(config) - else: - print 'API Client "'+config["api_client"]+'" not supported. Please check your config file.' - print - sys.exit() - + if config["api_client"] == "airtime": + return AirTimeApiClient(config) + elif config["api_client"] == "obp": + return ObpApiClient(config) + else: + print 'API Client "'+config["api_client"]+'" not supported. Please check your config file.' + print + sys.exit() + class ApiClientInterface: - # Implementation: optional - # - # Called from: beginning of all scripts - # - # Should exit the program if this version of pypo is not compatible with - # 3rd party software. - def is_server_compatible(self, verbose = True): - pass - - # Implementation: Required - # - # Called from: fetch loop - # - # This is the main method you need to implement when creating a new API client. - # start and end are for testing purposes. - # start and end are strings in the format YYYY-DD-MM-hh-mm-ss - def get_schedule(self, start=None, end=None): - return 0, [] - - # Implementation: Required - # - # Called from: fetch loop - # - # This downloads the media from the server. - def get_media(self, src, dst): - pass - - # Implementation: optional - # - # Called from: push loop - # - # Tell server that the scheduled *playlist* has started. - def notify_scheduled_item_start_playing(self, pkey, schedule): - pass - - # Implementation: optional - # You dont actually have to implement this function for the liquidsoap playout to work. - # - # Called from: pypo_notify.py - # - # This is a callback from liquidsoap, we use this to notify about the - # currently playing *song*. We get passed a JSON string which we handed to - # liquidsoap in get_liquidsoap_data(). - def notify_media_item_start_playing(self, data, media_id): - pass - - # Implementation: optional - # You dont actually have to implement this function for the liquidsoap playout to work. - def generate_range_dp(self): - pass + # Implementation: optional + # + # Called from: beginning of all scripts + # + # Should exit the program if this version of pypo is not compatible with + # 3rd party software. + def is_server_compatible(self, verbose = True): + pass + + # Implementation: Required + # + # Called from: fetch loop + # + # This is the main method you need to implement when creating a new API client. + # start and end are for testing purposes. + # start and end are strings in the format YYYY-DD-MM-hh-mm-ss + def get_schedule(self, start=None, end=None): + return 0, [] + + # Implementation: Required + # + # Called from: fetch loop + # + # This downloads the media from the server. + def get_media(self, src, dst): + pass + + # Implementation: optional + # + # Called from: push loop + # + # Tell server that the scheduled *playlist* has started. + def notify_scheduled_item_start_playing(self, pkey, schedule): + pass + + # Implementation: optional + # You dont actually have to implement this function for the liquidsoap playout to work. + # + # Called from: pypo_notify.py + # + # This is a callback from liquidsoap, we use this to notify about the + # currently playing *song*. We get passed a JSON string which we handed to + # liquidsoap in get_liquidsoap_data(). + def notify_media_item_start_playing(self, data, media_id): + pass + + # Implementation: optional + # You dont actually have to implement this function for the liquidsoap playout to work. + def generate_range_dp(self): + pass - # Implementation: optional - # - # Called from: push loop - # - # Return a dict of extra info you want to pass to liquidsoap - # You will be able to use this data in update_start_playing - def get_liquidsoap_data(self, pkey, schedule): - pass - - # Put here whatever tests you want to run to make sure your API is working - def test(self): - pass - - - #def get_media_type(self, playlist): - # nil + # Implementation: optional + # + # Called from: push loop + # + # Return a dict of extra info you want to pass to liquidsoap + # You will be able to use this data in update_start_playing + def get_liquidsoap_data(self, pkey, schedule): + pass + + # Put here whatever tests you want to run to make sure your API is working + def test(self): + pass + + + #def get_media_type(self, playlist): + # nil ################################################################################ # Airtime API Client @@ -105,255 +105,250 @@ class ApiClientInterface: class AirTimeApiClient(ApiClientInterface): - def __init__(self, config): - self.config = config + def __init__(self, config): + self.config = config - def __get_airtime_version(self, verbose = True): - logger = logging.getLogger() - url = self.config["base_url"] + self.config["api_base"] + self.config["version_url"] - url = url.replace("%%api_key%%", self.config["api_key"]) + def __get_airtime_version(self, verbose = True): + logger = logging.getLogger() + url = self.config["base_url"] + self.config["api_base"] + self.config["version_url"] + url = url.replace("%%api_key%%", self.config["api_key"]) - try: - logger.debug("Trying to contact %s", url) - response = urllib.urlopen(url) - data = response.read() - logger.debug("Data: %s", data) - response_json = json.loads(data) - version = response_json['version'] - logger.debug("Airtime Version %s detected", version) - except Exception, e: - try: - if e[1] == 401: - if (verbose): - print '#####################################' - print '# YOUR API KEY SEEMS TO BE INVALID:' - print '# ' + self.config["api_key"] - print '#####################################' - return False - except Exception, e: - pass + try: + logger.debug("Trying to contact %s", url) + response = urllib.urlopen(url) + data = response.read() + logger.debug("Data: %s", data) + response_json = json.loads(data) + version = response_json['version'] + logger.debug("Airtime Version %s detected", version) + except Exception, e: + try: + if e[1] == 401: + if (verbose): + print '#####################################' + print '# YOUR API KEY SEEMS TO BE INVALID:' + print '# ' + self.config["api_key"] + print '#####################################' + return False + except Exception, e: + pass - try: - if e[1] == 404: - if (verbose): - print '#####################################' - print '# Unable to contact the Airtime-API' - print '# ' + url - print '#####################################' - return False - except Exception, e: - pass + try: + if e[1] == 404: + if (verbose): + print '#####################################' + print '# Unable to contact the Airtime-API' + print '# ' + url + print '#####################################' + return False + except Exception, e: + pass - version = 0 - logger.error("Unable to detect Airtime Version - %s, Response: %s", e, response) + version = 0 + logger.error("Unable to detect Airtime Version - %s, Response: %s", e, response) - return version + return version - def test(self): - logger = logging.getLogger() - status, items = self.get_schedule('2010-01-01-00-00-00', '2011-01-01-00-00-00') - schedule = items["playlists"] - logger.debug("Number of playlists found: %s", str(len(schedule))) - count = 1 - for pkey in sorted(schedule.iterkeys()): - logger.debug("Playlist #%s",str(count)) - count+=1 - #logger.info("found playlist at %s", pkey) - playlist = schedule[pkey] - for item in playlist["medias"]: - filename = urlparse(item["uri"]) - filename = filename.query[5:] - self.get_media(item["uri"], filename) + def test(self): + logger = logging.getLogger() + status, items = self.get_schedule('2010-01-01-00-00-00', '2011-01-01-00-00-00') + schedule = items["playlists"] + logger.debug("Number of playlists found: %s", str(len(schedule))) + count = 1 + for pkey in sorted(schedule.iterkeys()): + logger.debug("Playlist #%s",str(count)) + count+=1 + #logger.info("found playlist at %s", pkey) + playlist = schedule[pkey] + for item in playlist["medias"]: + filename = urlparse(item["uri"]) + filename = filename.query[5:] + self.get_media(item["uri"], filename) - def is_server_compatible(self, verbose = True): - version = self.__get_airtime_version(verbose) - if (version == 0 or version == False): - if (verbose): - print 'Unable to get Airtime version number.' - print - return False - elif (version[0:4] != "1.6."): - if (verbose): - print 'Airtime version: ' + str(version) - print 'pypo not compatible with this version of Airtime.' - print - return False - else: - if (verbose): - print 'Airtime version: ' + str(version) - print 'pypo is compatible with this version of Airtime.' - print - return True + def is_server_compatible(self, verbose = True): + version = self.__get_airtime_version(verbose) + if (version == 0 or version == False): + if (verbose): + print 'Unable to get Airtime version number.' + print + return False + elif (version[0:4] != "1.6."): + if (verbose): + print 'Airtime version: ' + str(version) + print 'pypo not compatible with this version of Airtime.' + print + return False + else: + if (verbose): + print 'Airtime version: ' + str(version) + print 'pypo is compatible with this version of Airtime.' + print + return True - def get_schedule(self, start=None, end=None): - logger = logging.getLogger() - - """ - calculate start/end time range (format: YYYY-DD-MM-hh-mm-ss,YYYY-DD-MM-hh-mm-ss) - (seconds are ignored, just here for consistency) - """ - tnow = time.localtime(time.time()) - if (not start): - tstart = time.localtime(time.time() - 3600 * int(self.config["cache_for"])) - start = "%04d-%02d-%02d-%02d-%02d" % (tstart[0], tstart[1], tstart[2], tstart[3], tstart[4]) - - if (not end): - tend = time.localtime(time.time() + 3600 * int(self.config["prepare_ahead"])) - end = "%04d-%02d-%02d-%02d-%02d" % (tend[0], tend[1], tend[2], tend[3], tend[4]) - - range = {} - range['start'] = start - range['end'] = end - - # Construct the URL - export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"] - #logger.debug("Exporting schedule using URL: "+export_url) - - # Insert the start and end times into the URL - export_url = export_url.replace('%%api_key%%', self.config["api_key"]) - export_url = export_url.replace('%%from%%', range['start']) - export_url = export_url.replace('%%to%%', range['end']) - logger.info("Fetching schedule from %s", export_url) - - response = "" - status = 0 - try: - response_json = urllib.urlopen(export_url).read() - #logger.debug("%s", response_json) - response = json.loads(response_json) - #logger.info("export status %s", response['check']) - status = response['check'] - except Exception, e: - print e + def get_schedule(self, start=None, end=None): + logger = logging.getLogger() + + """ + calculate start/end time range (format: YYYY-DD-MM-hh-mm-ss,YYYY-DD-MM-hh-mm-ss) + (seconds are ignored, just here for consistency) + """ + tnow = time.localtime(time.time()) + if (not start): + tstart = time.localtime(time.time() - 3600 * int(self.config["cache_for"])) + start = "%04d-%02d-%02d-%02d-%02d" % (tstart[0], tstart[1], tstart[2], tstart[3], tstart[4]) + + if (not end): + tend = time.localtime(time.time() + 3600 * int(self.config["prepare_ahead"])) + end = "%04d-%02d-%02d-%02d-%02d" % (tend[0], tend[1], tend[2], tend[3], tend[4]) + + range = {} + range['start'] = start + range['end'] = end + + # Construct the URL + export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"] + #logger.debug("Exporting schedule using URL: "+export_url) + + # Insert the start and end times into the URL + export_url = export_url.replace('%%api_key%%', self.config["api_key"]) + export_url = export_url.replace('%%from%%', range['start']) + export_url = export_url.replace('%%to%%', range['end']) + logger.info("Fetching schedule from %s", export_url) + + response = "" + status = 0 + try: + response_json = urllib.urlopen(export_url).read() + #logger.debug("%s", response_json) + response = json.loads(response_json) + #logger.info("export status %s", response['check']) + status = response['check'] + except Exception, e: + print e - #schedule = response["playlists"] - #scheduleKeys = sorted(schedule.iterkeys()) - # - ## Remove all playlists that have passed current time - #try: - # tnow = time.localtime(time.time()) - # str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5]) - # toRemove = [] - # for pkey in scheduleKeys: - # if (str_tnow_s > schedule[pkey]['end']): - # toRemove.append(pkey) - # else: - # break - # #logger.debug("Remove keys: %s", toRemove) - # for index in toRemove: - # del schedule[index] - # #logger.debug("Schedule dict: %s", schedule) - #except Exception, e: - # logger.debug("'Ignore Past Playlists' feature not supported by API: %s", e) - #response["playlists"] = schedule + #schedule = response["playlists"] + #scheduleKeys = sorted(schedule.iterkeys()) + # + ## Remove all playlists that have passed current time + #try: + # tnow = time.localtime(time.time()) + # str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5]) + # toRemove = [] + # for pkey in scheduleKeys: + # if (str_tnow_s > schedule[pkey]['end']): + # toRemove.append(pkey) + # else: + # break + # #logger.debug("Remove keys: %s", toRemove) + # for index in toRemove: + # del schedule[index] + # #logger.debug("Schedule dict: %s", schedule) + #except Exception, e: + # logger.debug("'Ignore Past Playlists' feature not supported by API: %s", e) + #response["playlists"] = schedule - return status, response + return status, response - def get_media(self, src, dst): - logger = logging.getLogger() - - try: - src = src + "/api_key/" + self.config["api_key"] - logger.debug("try to download from %s to %s", src, dst) - # check if file exists already before downloading again - filename, headers = urllib.urlretrieve(src, dst) - - logger.info("downloaded %s to %s", src, dst) - except Exception, e: - logger.error("%s", e) + def get_media(self, src, dst): + logger = logging.getLogger() + + try: + src = src + "/api_key/" + self.config["api_key"] + logger.debug("try to download from %s to %s", src, dst) + # check if file exists already before downloading again + filename, headers = urllib.urlretrieve(src, dst) + + logger.info("downloaded %s to %s", src, dst) + except Exception, e: + logger.error("%s", e) - """ - Tell server that the scheduled *playlist* has started. - """ - def notify_scheduled_item_start_playing(self, pkey, schedule): - logger = logging.getLogger() - playlist = schedule[pkey] - schedule_id = playlist["schedule_id"] - url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] - url = url.replace("%%schedule_id%%", str(schedule_id)) - url = url.replace("%%api_key%%", self.config["api_key"]) - logger.debug(url) - - try: - response = urllib.urlopen(url) - response = json.loads(response.read()) - logger.info("API-Status %s", response['status']) - logger.info("API-Message %s", response['message']) - - except Exception, e: - logger.critical("Unable to connect - %s", e) - - return response - + """ + Tell server that the scheduled *playlist* has started. + """ + def notify_scheduled_item_start_playing(self, pkey, schedule): + logger = logging.getLogger() + playlist = schedule[pkey] + schedule_id = playlist["schedule_id"] + url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] + url = url.replace("%%schedule_id%%", str(schedule_id)) + url = url.replace("%%api_key%%", self.config["api_key"]) + logger.debug(url) + + try: + response = urllib.urlopen(url) + response = json.loads(response.read()) + logger.info("API-Status %s", response['status']) + logger.info("API-Message %s", response['message']) + + except Exception, e: + logger.critical("Unable to connect - %s", e) + + return response + - """ - This is a callback from liquidsoap, we use this to notify about the - currently playing *song*. We get passed a JSON string which we handed to - liquidsoap in get_liquidsoap_data(). - """ - def notify_media_item_start_playing(self, data, media_id): - logger = logging.getLogger() - response = '' - if (data[0] != '{'): - return response - try: - data = json.loads(data) - logger.debug(str(data)) - schedule_id = data["schedule_id"] - url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] - url = url.replace("%%media_id%%", str(media_id)) - url = url.replace("%%schedule_id%%", str(schedule_id)) - url = url.replace("%%api_key%%", self.config["api_key"]) - logger.debug(url) - response = urllib.urlopen(url) - response = json.loads(response.read()) - logger.info("API-Status %s", response['status']) - logger.info("API-Message %s", response['message']) - - except Exception, e: - logger.critical("Exception: %s", e) - - return response - - - def generate_range_dp(self): - pass - #logger = logging.getLogger() - # - #url = self.api_url + 'schedule/generate_range_dp.php' - # - #try: - # response = urllib.urlopen(url, self.api_auth) - # response = json.loads(response.read()) - # logger.debug("Trying to contact %s", url) - # logger.info("API-Status %s", response['status']) - # logger.info("API-Message %s", response['message']) - # - #except Exception, e: - # print e - # api_status = False - # logger.critical("Unable to handle the request - %s", e) - # - #return response + """ + This is a callback from liquidsoap, we use this to notify about the + currently playing *song*. We get passed a JSON string which we handed to + liquidsoap in get_liquidsoap_data(). + """ + def notify_media_item_start_playing(self, data, media_id): + logger = logging.getLogger() + response = '' + try: + schedule_id = data + url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] + url = url.replace("%%media_id%%", str(media_id)) + url = url.replace("%%schedule_id%%", str(schedule_id)) + url = url.replace("%%api_key%%", self.config["api_key"]) + logger.debug(url) + response = urllib.urlopen(url) + response = json.loads(response.read()) + logger.info("API-Status %s", response['status']) + logger.info("API-Message %s", response['message']) + + except Exception, e: + logger.critical("Exception: %s", e) + + return response + + + def generate_range_dp(self): + pass + #logger = logging.getLogger() + # + #url = self.api_url + 'schedule/generate_range_dp.php' + # + #try: + # response = urllib.urlopen(url, self.api_auth) + # response = json.loads(response.read()) + # logger.debug("Trying to contact %s", url) + # logger.info("API-Status %s", response['status']) + # logger.info("API-Message %s", response['message']) + # + #except Exception, e: + # print e + # api_status = False + # logger.critical("Unable to handle the request - %s", e) + # + #return response - def get_liquidsoap_data(self, pkey, schedule): - logger = logging.getLogger() - playlist = schedule[pkey] - data = dict() - try: - data["schedule_id"] = playlist['id'] - except Exception, e: - data["schedule_id"] = 0 - data = json.dumps(data) - return data - + def get_liquidsoap_data(self, pkey, schedule): + logger = logging.getLogger() + playlist = schedule[pkey] + data = dict() + try: + data["schedule_id"] = playlist['id'] + except Exception, e: + data["schedule_id"] = 0 + return data + - + ################################################################################ # OpenBroadcast API Client ################################################################################ @@ -364,222 +359,222 @@ OBP_MIN_VERSION = 2010100101 # required obp version class ObpApiClient(): - def __init__(self, config): - self.config = config - self.api_auth = urllib.urlencode({'api_key': self.config["api_key"]}) - - def is_server_compatible(self, verbose = True): - obp_version = self.get_obp_version() - - if obp_version == 0: - if (verbose): - print '#################################################' - print 'Unable to get OBP version. Is OBP up and running?' - print '#################################################' - print - return False - elif obp_version < OBP_MIN_VERSION: - if (verbose): - print 'OBP version: ' + str(obp_version) - print 'OBP min-version: ' + str(OBP_MIN_VERSION) - print 'pypo not compatible with this version of OBP' - print - return False - else: - if (verbose): - print 'OBP API: ' + str(API_BASE) - print 'OBP version: ' + str(obp_version) - print 'OBP min-version: ' + str(OBP_MIN_VERSION) - print 'pypo is compatible with this version of OBP' - print - return True - - - def get_obp_version(self): - logger = logging.getLogger() + def __init__(self, config): + self.config = config + self.api_auth = urllib.urlencode({'api_key': self.config["api_key"]}) + + def is_server_compatible(self, verbose = True): + obp_version = self.get_obp_version() + + if obp_version == 0: + if (verbose): + print '#################################################' + print 'Unable to get OBP version. Is OBP up and running?' + print '#################################################' + print + return False + elif obp_version < OBP_MIN_VERSION: + if (verbose): + print 'OBP version: ' + str(obp_version) + print 'OBP min-version: ' + str(OBP_MIN_VERSION) + print 'pypo not compatible with this version of OBP' + print + return False + else: + if (verbose): + print 'OBP API: ' + str(API_BASE) + print 'OBP version: ' + str(obp_version) + print 'OBP min-version: ' + str(OBP_MIN_VERSION) + print 'pypo is compatible with this version of OBP' + print + return True + + + def get_obp_version(self): + logger = logging.getLogger() - # lookup OBP version - url = self.config["base_url"] + self.config["api_base"]+ self.config["version_url"] - - try: - logger.debug("Trying to contact %s", url) - response = urllib.urlopen(url, self.api_auth) - response_json = json.loads(response.read()) - obp_version = int(response_json['version']) - logger.debug("OBP Version %s detected", obp_version) - - except Exception, e: - try: - if e[1] == 401: - print '#####################################' - print '# YOUR API KEY SEEMS TO BE INVALID' - print '# ' + self.config["api_auth"] - print '#####################################' - sys.exit() - - except Exception, e: - pass - - try: - if e[1] == 404: - print '#####################################' - print '# Unable to contact the OBP-API' - print '# ' + url - print '#####################################' - sys.exit() - - except Exception, e: - pass - - obp_version = 0 - logger.error("Unable to detect OBP Version - %s", e) - - return obp_version + # lookup OBP version + url = self.config["base_url"] + self.config["api_base"]+ self.config["version_url"] + + try: + logger.debug("Trying to contact %s", url) + response = urllib.urlopen(url, self.api_auth) + response_json = json.loads(response.read()) + obp_version = int(response_json['version']) + logger.debug("OBP Version %s detected", obp_version) + + except Exception, e: + try: + if e[1] == 401: + print '#####################################' + print '# YOUR API KEY SEEMS TO BE INVALID' + print '# ' + self.config["api_auth"] + print '#####################################' + sys.exit() + + except Exception, e: + pass + + try: + if e[1] == 404: + print '#####################################' + print '# Unable to contact the OBP-API' + print '# ' + url + print '#####################################' + sys.exit() + + except Exception, e: + pass + + obp_version = 0 + logger.error("Unable to detect OBP Version - %s", e) + + return obp_version - def get_schedule(self, start=None, end=None): - logger = logging.getLogger() - - """ - calculate start/end time range (format: YYYY-DD-MM-hh-mm-ss,YYYY-DD-MM-hh-mm-ss) - (seconds are ignored, just here for consistency) - """ - tnow = time.localtime(time.time()) - if (not start): - tstart = time.localtime(time.time() - 3600 * int(self.config["cache_for"])) - start = "%04d-%02d-%02d-%02d-%02d" % (tstart[0], tstart[1], tstart[2], tstart[3], tstart[4]) - - if (not end): - tend = time.localtime(time.time() + 3600 * int(self.config["prepare_ahead"])) - end = "%04d-%02d-%02d-%02d-%02d" % (tend[0], tend[1], tend[2], tend[3], tend[4]) - - range = {} - range['start'] = start - range['end'] = end - - # Construct the URL - export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"] - - # Insert the start and end times into the URL - export_url = export_url.replace('%%api_key%%', self.config["api_key"]) - export_url = export_url.replace('%%from%%', range['start']) - export_url = export_url.replace('%%to%%', range['end']) - logger.info("export from %s", export_url) - - response = "" - status = 0 - try: - response_json = urllib.urlopen(export_url).read() - logger.debug("%s", response_json) - response = json.loads(response_json) - logger.info("export status %s", response['check']) - status = response['check'] - except Exception, e: - print e + def get_schedule(self, start=None, end=None): + logger = logging.getLogger() + + """ + calculate start/end time range (format: YYYY-DD-MM-hh-mm-ss,YYYY-DD-MM-hh-mm-ss) + (seconds are ignored, just here for consistency) + """ + tnow = time.localtime(time.time()) + if (not start): + tstart = time.localtime(time.time() - 3600 * int(self.config["cache_for"])) + start = "%04d-%02d-%02d-%02d-%02d" % (tstart[0], tstart[1], tstart[2], tstart[3], tstart[4]) + + if (not end): + tend = time.localtime(time.time() + 3600 * int(self.config["prepare_ahead"])) + end = "%04d-%02d-%02d-%02d-%02d" % (tend[0], tend[1], tend[2], tend[3], tend[4]) + + range = {} + range['start'] = start + range['end'] = end + + # Construct the URL + export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"] + + # Insert the start and end times into the URL + export_url = export_url.replace('%%api_key%%', self.config["api_key"]) + export_url = export_url.replace('%%from%%', range['start']) + export_url = export_url.replace('%%to%%', range['end']) + logger.info("export from %s", export_url) + + response = "" + status = 0 + try: + response_json = urllib.urlopen(export_url).read() + logger.debug("%s", response_json) + response = json.loads(response_json) + logger.info("export status %s", response['check']) + status = response['check'] + except Exception, e: + print e - return status, response + return status, response - - def get_media(self, src, dest): - try: - print '** urllib auth with: ', - print self.api_auth - urllib.urlretrieve(src, dst, False, self.api_auth) - logger.info("downloaded %s to %s", src, dst) - except Exception, e: - logger.error("%s", e) + + def get_media(self, src, dest): + try: + print '** urllib auth with: ', + print self.api_auth + urllib.urlretrieve(src, dst, False, self.api_auth) + logger.info("downloaded %s to %s", src, dst) + except Exception, e: + logger.error("%s", e) - """ - Tell server that the scheduled *playlist* has started. - """ - def notify_scheduled_item_start_playing(self, pkey, schedule): - #def update_scheduled_item(self, item_id, value): - logger = logging.getLogger() - url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] - url = url.replace("%%item_id%%", str(schedule[pkey]["id"])) - url = url.replace("%%played%%", "1") - - try: - response = urllib.urlopen(url, self.api_auth) - response = json.loads(response.read()) - logger.info("API-Status %s", response['status']) - logger.info("API-Message %s", response['message']) - - except Exception, e: - print e - api_status = False - logger.critical("Unable to connect to the OBP API - %s", e) - - return response - - """ - This is a callback from liquidsoap, we use this to notify about the - currently playing *song*. We get passed a JSON string which we handed to - liquidsoap in get_liquidsoap_data(). - """ - def notify_media_item_start_playing(self, data, media_id): -# def update_start_playing(self, playlist_type, export_source, media_id, playlist_id, transmission_id): - logger = logging.getLogger() - playlist_type = data["playlist_type"] - export_source = data["export_source"] - playlist_id = data["playlist_id"] - transmission_id = data["transmission_id"] - - url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] - url = url.replace("%%playlist_type%%", str(playlist_type)) - url = url.replace("%%export_source%%", str(export_source)) - url = url.replace("%%media_id%%", str(media_id)) - url = url.replace("%%playlist_id%%", str(playlist_id)) - url = url.replace("%%transmission_id%%", str(transmission_id)) - print url - - try: - response = urllib.urlopen(url, self.api_auth) - response = json.loads(response.read()) - logger.info("API-Status %s", response['status']) - logger.info("API-Message %s", response['message']) - logger.info("TXT %s", response['str_dls']) - - except Exception, e: - print e - api_status = False - logger.critical("Unable to connect to the OBP API - %s", e) - - return response - - - def generate_range_dp(self): - logger = logging.getLogger() - - url = self.config["base_url"] + self.config["api_base"] + self.config["generate_range_url"] - - try: - response = urllib.urlopen(url, self.api_auth) - response = json.loads(response.read()) - logger.debug("Trying to contact %s", url) - logger.info("API-Status %s", response['status']) - logger.info("API-Message %s", response['message']) - - except Exception, e: - print e - api_status = False - logger.critical("Unable to handle the OBP API request - %s", e) - - return response + """ + Tell server that the scheduled *playlist* has started. + """ + def notify_scheduled_item_start_playing(self, pkey, schedule): + #def update_scheduled_item(self, item_id, value): + logger = logging.getLogger() + url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] + url = url.replace("%%item_id%%", str(schedule[pkey]["id"])) + url = url.replace("%%played%%", "1") + + try: + response = urllib.urlopen(url, self.api_auth) + response = json.loads(response.read()) + logger.info("API-Status %s", response['status']) + logger.info("API-Message %s", response['message']) + + except Exception, e: + print e + api_status = False + logger.critical("Unable to connect to the OBP API - %s", e) + + return response + + """ + This is a callback from liquidsoap, we use this to notify about the + currently playing *song*. We get passed a JSON string which we handed to + liquidsoap in get_liquidsoap_data(). + """ + def notify_media_item_start_playing(self, data, media_id): +# def update_start_playing(self, playlist_type, export_source, media_id, playlist_id, transmission_id): + logger = logging.getLogger() + playlist_type = data["playlist_type"] + export_source = data["export_source"] + playlist_id = data["playlist_id"] + transmission_id = data["transmission_id"] + + url = self.config["base_url"] + self.config["api_base"] + self.config["update_start_playing_url"] + url = url.replace("%%playlist_type%%", str(playlist_type)) + url = url.replace("%%export_source%%", str(export_source)) + url = url.replace("%%media_id%%", str(media_id)) + url = url.replace("%%playlist_id%%", str(playlist_id)) + url = url.replace("%%transmission_id%%", str(transmission_id)) + print url + + try: + response = urllib.urlopen(url, self.api_auth) + response = json.loads(response.read()) + logger.info("API-Status %s", response['status']) + logger.info("API-Message %s", response['message']) + logger.info("TXT %s", response['str_dls']) + + except Exception, e: + print e + api_status = False + logger.critical("Unable to connect to the OBP API - %s", e) + + return response + + + def generate_range_dp(self): + logger = logging.getLogger() + + url = self.config["base_url"] + self.config["api_base"] + self.config["generate_range_url"] + + try: + response = urllib.urlopen(url, self.api_auth) + response = json.loads(response.read()) + logger.debug("Trying to contact %s", url) + logger.info("API-Status %s", response['status']) + logger.info("API-Message %s", response['message']) + + except Exception, e: + print e + api_status = False + logger.critical("Unable to handle the OBP API request - %s", e) + + return response - def get_liquidsoap_data(self, pkey, schedule): - playlist = schedule[pkey] - data = dict() - data["ptype"] = playlist['subtype'] - try: - data["user_id"] = playlist['user_id'] - data["playlist_id"] = playlist['id'] - data["transmission_id"] = playlist['schedule_id'] - except Exception, e: - data["playlist_id"] = 0 - data["user_id"] = 0 - data["transmission_id"] = 0 - data = json.dumps(data) - return data - + def get_liquidsoap_data(self, pkey, schedule): + playlist = schedule[pkey] + data = dict() + data["ptype"] = playlist['subtype'] + try: + data["user_id"] = playlist['user_id'] + data["playlist_id"] = playlist['id'] + data["transmission_id"] = playlist['schedule_id'] + except Exception, e: + data["playlist_id"] = 0 + data["user_id"] = 0 + data["transmission_id"] = 0 + data = json.dumps(data) + return data + diff --git a/pypo/logging.cfg b/pypo/logging.cfg index 46f306e82..ee3cd2bee 100644 --- a/pypo/logging.cfg +++ b/pypo/logging.cfg @@ -18,7 +18,6 @@ qualname="process" propagate=0 - [handler_consoleHandler] class=StreamHandler level=DEBUG @@ -41,7 +40,7 @@ args=("./debug.log",) class=FileHandler level=DEBUG formatter=simpleFormatter -args=("/tmp/sessionlog_null.log",) +args=("/dev/null",) [formatter_simpleFormatter] diff --git a/pypo/pypo-cli.py b/pypo/pypo-cli.py index 4d03f7818..a2248c7e0 100755 --- a/pypo/pypo-cli.py +++ b/pypo/pypo-cli.py @@ -383,8 +383,10 @@ class Playout: fsize = 0 if fsize > 0: - pl_entry = 'annotate:export_source="%s",media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s":%s' % \ - (str(media['export_source']), media['id'], 0, str(float(media['fade_in']) / 1000), str(float(media['fade_out']) / 1000), dst) + pl_entry = \ + 'annotate:export_source="%s",media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s",schedule_table_id="%s":%s'\ + % (str(media['export_source']), media['id'], 0, str(float(media['fade_in']) / 1000), \ + str(float(media['fade_out']) / 1000), media['row_id'],dst) logger.debug(pl_entry) @@ -686,6 +688,14 @@ class Playout: time.sleep(sleep_time) tn = telnetlib.Telnet(LS_HOST, 1234) + + + # Get any extra information for liquidsoap (which will be sent back to us) + liquidsoap_data = self.api_client.get_liquidsoap_data(pkey, schedule) + logger.debug("Sending additional data to liquidsoap: "+str(liquidsoap_data["schedule_id"])) + + #Sending JSON string. Example: {"schedule_id":"13"} + tn.write("vars.pypo_data %s\n"%(str(liquidsoap_data["schedule_id"]))) for line in pl_file.readlines(): line = line.strip() diff --git a/pypo/pypo-notify.py b/pypo/pypo-notify.py index 88cc37d2d..a8262d884 100755 --- a/pypo/pypo-notify.py +++ b/pypo/pypo-notify.py @@ -80,7 +80,7 @@ class Notify: logger.debug('#################################################') logger.debug('data = '+ str(data)) response = self.api_client.notify_media_item_start_playing(data, media_id) - logger.debug("Response: "+str(response)) + logger.debug("Response: "+json.dumps(response)) if __name__ == '__main__': diff --git a/pypo/scripts/ls_script.liq b/pypo/scripts/ls_script.liq index 724358263..5d7ae465c 100644 --- a/pypo/scripts/ls_script.liq +++ b/pypo/scripts/ls_script.liq @@ -10,9 +10,10 @@ queue = audio_to_stereo(queue) pypo_data = ref '0' -#def notify(m) -# print("./notify.sh --data='#{!pypo_data}' --media-id=#{m['media_id']}") -#end +def notify(m) + system("./notify.sh --data='#{!pypo_data}' --media-id=#{m['schedule_table_id']}") + print("./notify.sh --data='#{!pypo_data}' --media-id=#{m['schedule_table_id']}") +end def crossfade(s) s = fade.in(type="log", s) @@ -28,7 +29,7 @@ default = rewrite_metadata([("artist","Airtime"), ("title", "offline")],default) s = fallback(track_sensitive=false, [queue, default]) -#s = on_metadata(notify, s) +s = on_metadata(notify, s) s = crossfade(s) if output_sound_device then