From 36138120127ed83a2b3861ce6c211faa8075cde2 Mon Sep 17 00:00:00 2001 From: "paul.baranowski" Date: Mon, 29 Nov 2010 18:34:22 -0500 Subject: [PATCH] Pypo fixes and improvements General: * Moved pypo author info into one file * Added two database columns in schedule table: "schedule_group_played" and "media_item_played". API clients: * Created get_liquidsoap_data() function which allows you to give arbitrary data to liquidsoap. * Added documentation * Renamed functions to make it more obvious what is happening pypo_cli: * Got rid of more constants that were not needed * Created function set_export_source() to reduce code repetition * Separated the downloading of the schedule from tracking what has been played. The tracking info is now kept in a separate file. This fixes the major bug that the playlist keeps restarting for the first minute of playback. * converted more print statements to debug statements pypoTester: * Now uses samples from the audio_samples directory, and schedules two audio clips back-to-back. --- 3rd_party/pypo/api_clients/api_client.py | 274 ++++++++----- 3rd_party/pypo/config.cfg | 137 ++++--- 3rd_party/pypo/push.py | 2 - 3rd_party/pypo/pypo_cli.py | 364 ++++++++---------- 3rd_party/pypo/pypo_dls.py | 8 +- 3rd_party/pypo/pypo_notify.py | 285 +++++++------- 3rd_party/pypo/scripts/cue_file.py | 2 - .../pypo/scripts/include_dynamic_vars.liq | 7 +- 3rd_party/pypo/scripts/include_notify.liq | 13 +- 3rd_party/pypo/scripts/ls_config.liq.dist | 3 - 3rd_party/pypo/scripts/ls_cue.liq | 2 +- 3rd_party/pypo/util/cue_file.py | 2 - api/get_media.php | 8 - api/notify_media_item_start_play.php | 37 ++ api/notify_schedule_group_play.php | 35 ++ api/schedule.php | 7 - backend/Schedule.php | 33 +- backend/propel-db/build/sql/schema.sql | 2 + backend/propel-db/schema.xml | 2 + backend/tests/pypoTester.php | 19 +- 20 files changed, 699 insertions(+), 543 deletions(-) create mode 100644 api/notify_media_item_start_play.php create mode 100644 api/notify_schedule_group_play.php diff --git a/3rd_party/pypo/api_clients/api_client.py b/3rd_party/pypo/api_clients/api_client.py index cdb997c9e..59fd22e48 100644 --- a/3rd_party/pypo/api_clients/api_client.py +++ b/3rd_party/pypo/api_clients/api_client.py @@ -1,6 +1,15 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +############################################################################### +# This file holds the implementations for all the API clients. +# +# If you want to develop a new client, here are some suggestions: +# Get the fetch methods working first, then the push, then the liquidsoap notifier. +# You will probably want to create a script on your server side to automatically +# schedule a playlist one minute from the current time. +############################################################################### + import sys import time import urllib @@ -22,49 +31,82 @@ def api_client_factory(config): class ApiClientInterface: - # This is optional. + # 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 check_version(self): - nil + pass - # Required. + # 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, [] - # Required. + # Implementation: Required + # + # Called from: fetch loop + # # This downloads the media from the server. def get_media(self, src, dst): - nil + pass - # This is optional. - # You dont actually have to implement this function for the liquidsoap playout to work. - def update_scheduled_item(self, item_id, value): - nil + # Implementation: optional + # + # Called from: push loop + # + # Tell server that the scheduled *playlist* has started. + def notify_scheduled_item_start_playing(self, pkey, schedule): + pass - # This is optional. + # Implementation: optional # You dont actually have to implement this function for the liquidsoap playout to work. - def update_start_playing(self, playlist_type, export_source, media_id, playlist_id, transmission_id): - nil + # + # 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): - nil + 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): - nil + pass + #def get_media_type(self, playlist): # nil +################################################################################ +# Campcaster API Client +################################################################################ + class CampcasterApiClient(ApiClientInterface): def __init__(self, config): self.config = config - #self.api_auth = api_auth def __get_campcaster_version(self): logger = logging.getLogger() @@ -109,7 +151,6 @@ class CampcasterApiClient(ApiClientInterface): def test(self): logger = logging.getLogger() status, items = self.get_schedule('2010-01-01-00-00-00', '2011-01-01-00-00-00') - #print items schedule = items["playlists"] logger.debug("Number of playlists found: %s", str(len(schedule))) count = 1 @@ -117,12 +158,10 @@ class CampcasterApiClient(ApiClientInterface): logger.debug("Playlist #%s",str(count)) count+=1 #logger.info("found playlist at %s", pkey) - #print pkey playlist = schedule[pkey] for item in playlist["medias"]: filename = urlparse(item["uri"]) filename = filename.query[5:] - #print filename self.get_media(item["uri"], filename) @@ -142,6 +181,7 @@ class CampcasterApiClient(ApiClientInterface): print 'pypo is compatible with this version of Campcaster.' print + def get_schedule(self, start=None, end=None): logger = logging.getLogger() @@ -183,26 +223,26 @@ class CampcasterApiClient(ApiClientInterface): 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 @@ -220,51 +260,58 @@ class CampcasterApiClient(ApiClientInterface): logger.error("%s", e) - def update_scheduled_item(self, item_id, value): - pass - #logger = logging.getLogger() - # - #url = self.config["base_url"] + self.config["api_base"] + self.config["update_item_url"] - # - #try: - # response = urllib.urlopen(url, self.api_auth) - # response = json.read(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 - %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 += "&api_key=" + self.config["api_key"] + logger.debug(url) + + try: + response = urllib.urlopen(url) + response = json.read(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 - - def update_start_playing(self, playlist_type, export_source, media_id, playlist_id, transmission_id): - pass - #logger = logging.getLogger() - # - #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) - # response = json.read(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 - %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.read(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 += "&api_key=" + self.config["api_key"] + logger.debug(url) + response = urllib.urlopen(url) + response = json.read(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): @@ -287,6 +334,17 @@ class CampcasterApiClient(ApiClientInterface): # #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.write(data) + return data + ################################################################################ @@ -327,7 +385,7 @@ class ObpApiClient(): def get_obp_version(self): - logger = logging.getLogger("ObpApiClient.get_obp_version") + logger = logging.getLogger() # lookup OBP version url = self.config["base_url"] + self.config["api_base"]+ self.config["version_url"] @@ -369,7 +427,7 @@ class ObpApiClient(): def get_schedule(self, start=None, end=None): - logger = logging.getLogger("CampcasterApiClient.get_schedule") + logger = logging.getLogger() """ calculate start/end time range (format: YYYY-DD-MM-hh-mm-ss,YYYY-DD-MM-hh-mm-ss) @@ -421,13 +479,15 @@ class ObpApiClient(): logger.error("%s", e) - def update_scheduled_item(self, item_id, value): - logger = logging.getLogger("ObpApiClient.update_scheduled_item") - # lookup OBP version - + """ + 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(item_id)) - url = url.replace("%%played%%", str(value)) + url = url.replace("%%item_id%%", str(schedule[pkey]["id"])) + url = url.replace("%%played%%", "1") try: response = urllib.urlopen(url, self.api_auth) @@ -442,9 +502,18 @@ class ObpApiClient(): return response - - def update_start_playing(self, playlist_type, export_source, media_id, playlist_id, transmission_id): - logger = logging.getLogger("ApiClient.update_scheduled_item") + """ + 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)) @@ -470,7 +539,7 @@ class ObpApiClient(): def generate_range_dp(self): - logger = logging.getLogger("ObpApiClient.generate_range_dp") + logger = logging.getLogger() url = self.config["base_url"] + self.config["api_base"] + self.config["generate_range_url"] @@ -486,5 +555,20 @@ class ObpApiClient(): 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.write(data) + return data + diff --git a/3rd_party/pypo/config.cfg b/3rd_party/pypo/config.cfg index 64652ca17..26ec732cd 100644 --- a/3rd_party/pypo/config.cfg +++ b/3rd_party/pypo/config.cfg @@ -14,67 +14,12 @@ api_client = "campcaster" # _include_ trailing slash !! # ############################################ cache_dir = '/opt/pypo/cache/' -schedule_dir = '/opt/pypo/cache/schedule' file_dir = '/opt/pypo/files/' tmp_dir = '/opt/pypo/tmp/' -############################################ -# API path & co # -############################################ -# Value needed to access the API -api_key = 'AAA' - # Hostname base_url = 'http://localhost/' -################################################################################ -# Generic Config - if you are creating a new API client, define these values # -################################################################################ -# Path to the base of the API -#api_base = '' - -# URL to get the version number of the API -#version_url = '' - -# Schedule export path. -# %%from%% - starting date/time in the form YYYY-MM-DD-hh-mm -# %%to%% - starting date/time in the form YYYY-MM-DD-hh-mm -#export_url = '' - -# Update whether an item has been played. -# %%item_id%% -# %%played%% -#update_item_url = '' - -# Update whether an item is currently playing. -#update_start_playing_url = '' - -# ??? -#generate_range_url = '' - - -##################### -# Campcaster Config # -##################### -api_base = 'campcaster/' -version_url = 'api/api_version.php?api_key=%%api_key%%' -export_url = 'api/schedule.php?from=%%from%%&to=%%to%%&api_key=%%api_key%%' -update_item_url = 'api/schedule.php?item_id=%%item_id%%&played=%%played%%' -update_start_playing_url = 'api/update_start_playing.php?playlist_type=%%playlist_type%%&export_source=%%export_source%%&media_id=%%media_id%%&playlist_id=%%playlist_id%%&transmission_id=%%transmission_id%%' -generate_range_url = 'api/generate_range_dp.php' - - -############## -# OBP config # -############## -#base_url = 'http://localhost/' -#api_base = '' -#version_url = 'api/pypo/status/json' -#update_item_url = 'api/pypo/update_shedueled_item/$$item_id%%?played=%%played%%' -#update_start_playing_url = 'api/pypo/update_start_playing/?playlist_type=%%playlist_type%%&export_source=%%export_source%%&media_id=%%media_id%%&playlist_id=%%playlist_id%%&transmission_id=%%transmission_id%%' -#generate_range_url = 'api/pypo/generate_range_dp/' - - ############################################ # Liquidsoap settings # ############################################ @@ -87,10 +32,90 @@ ls_port = '1234' prepare_ahead = 24 #in hours cache_for = 24 #how long to hold the cache, in hours -poll_interval = 10 # in seconds +# Poll interval in seconds. +# +# This is how often the poll script downloads new schedules and files from the +# server. +# +# For production use, this number depends on whether you plan on making any +# last-minute changes to your schedule. This number should be set to half of +# the time you expect to "lock-in" your schedule. So if your schedule is set +# 24 hours in advance, this can be set to poll every 12 hours. +# +poll_interval = 10 # in seconds. + + +# Push interval in seconds. +# +# This is how often the push script checks whether it has something new to +# push to liquidsoap. +# +# It's hard to imagine a situation where this should be more than 1 second. +# push_interval = 1 # in seconds # 'pre' or 'otf'. 'pre' cues while playlist preparation # while 'otf' (on the fly) cues while loading into ls # (needs the post_processor patch) cue_style = 'pre' + + +################################################################################ +# Uncomment *one of the sets* of values from the API clients below, and comment +# out all the others. +################################################################################ + +##################### +# Campcaster Config # +##################### +# Value needed to access the API +api_key = 'AAA' + +# Path to the base of the API +api_base = 'campcaster/' + +# URL to get the version number of the server API +version_url = 'api/api_version.php?api_key=%%api_key%%' + +# Schedule export path. +# %%from%% - starting date/time in the form YYYY-MM-DD-hh-mm +# %%to%% - starting date/time in the form YYYY-MM-DD-hh-mm +export_url = 'api/schedule.php?from=%%from%%&to=%%to%%&api_key=%%api_key%%' + +# Update whether a schedule group has begun playing. +update_item_url = 'api/notify_schedule_group_play.php?schedule_id=%%schedule_id%%' + +# Update whether an audio clip is currently playing. +update_start_playing_url = 'api/notify_media_item_start_play.php?media_id=%%media_id%%&schedule_id=%%schedule_id%%' + +# ??? +generate_range_url = 'api/generate_range_dp.php' + + +############## +# OBP config # +############## +# Value needed to access the API +#api_key = 'AAA' + +#base_url = 'http://localhost/' + +# Path to the base of the API +#api_base = '' + +# URL to get the version number of the server API +#version_url = 'api/pypo/status/json' + +# Schedule export path. +# %%from%% - starting date/time in the form YYYY-MM-DD-hh-mm +# %%to%% - starting date/time in the form YYYY-MM-DD-hh-mm + +# Update whether an item has been played. +#update_item_url = 'api/pypo/update_shedueled_item/$$item_id%%?played=%%played%%' + +# Update whether an item is currently playing. +#update_start_playing_url = 'api/pypo/mod/medialibrary/?playlist_type=%%playlist_type%%&export_source=%%export_source%%&media_id=%%media_id%%&playlist_id=%%playlist_id%%&transmission_id=%%transmission_id%%' + +# ??? +#generate_range_url = 'api/pypo/generate_range_dp/' + diff --git a/3rd_party/pypo/push.py b/3rd_party/pypo/push.py index 50cde484b..a3be669f8 100755 --- a/3rd_party/pypo/push.py +++ b/3rd_party/pypo/push.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# author Jonas Ohrstrom - """ Python part of radio playout (pypo) diff --git a/3rd_party/pypo/pypo_cli.py b/3rd_party/pypo/pypo_cli.py index 21ec292d7..003711b74 100755 --- a/3rd_party/pypo/pypo_cli.py +++ b/3rd_party/pypo/pypo_cli.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# author Jonas Ohrstrom - """ Python part of radio playout (pypo) @@ -19,7 +17,7 @@ tracks over and over again. Attention & ToDos - liquidsoap does not like mono files! So we have to make sure that only files with - 2 channels are fed to LS + 2 channels are fed to LiquidSoap (solved: current = audio_to_stereo(current) - maybe not with ultimate performance) @@ -74,14 +72,14 @@ parser = OptionParser(usage=usage) parser.add_option("-v", "--compat", help="Check compatibility with server API version", default=False, action="store_true", dest="check_compat") parser.add_option("-t", "--test", help="Do a test to make sure everything is working properly.", default=False, action="store_true", dest="test") -parser.add_option("-f", "--fetch-scheduler", help="Fetch from scheduler - scheduler (loop, interval in config file)", default=False, action="store_true", dest="fetch_scheduler") -parser.add_option("-p", "--push-scheduler", help="Push scheduler to Liquidsoap (loop, interval in config file)", default=False, action="store_true", dest="push_scheduler") +parser.add_option("-f", "--fetch-scheduler", help="Fetch the schedule from server. This is a polling process that runs forever.", default=False, action="store_true", dest="fetch_scheduler") +parser.add_option("-p", "--push-scheduler", help="Push the schedule to Liquidsoap. This is a polling process that runs forever.", default=False, action="store_true", dest="push_scheduler") parser.add_option("-F", "--fetch-daypart", help="Fetch from daypart - scheduler (loop, interval in config file)", default=False, action="store_true", dest="fetch_daypart") parser.add_option("-P", "--push-daypart", help="Push daypart to Liquidsoap (loop, interval in config file)", default=False, action="store_true", dest="push_daypart") parser.add_option("-b", "--cleanup", help="Cleanup", default=False, action="store_true", dest="cleanup") -parser.add_option("-j", "--jingles", help="Get new jingles from obp, comma separated list if jingle-id's as argument", metavar="LIST") +#parser.add_option("-j", "--jingles", help="Get new jingles from server, comma separated list if jingle-id's as argument", metavar="LIST") parser.add_option("-c", "--check", help="Check the cached schedule and exit", default=False, action="store_true", dest="check") # parse options @@ -93,32 +91,19 @@ logging.config.fileConfig("logging.cfg") # loading config file try: config = ConfigObj('config.cfg') - CACHE_DIR = config['cache_dir'] - FILE_DIR = config['file_dir'] - TMP_DIR = config['tmp_dir'] - API_BASE = config['api_base'] - API_KEY = config['api_key'] POLL_INTERVAL = float(config['poll_interval']) PUSH_INTERVAL = float(config['push_interval']) LS_HOST = config['ls_host'] LS_PORT = config['ls_port'] - #PREPARE_AHEAD = config['prepare_ahead'] - CACHE_FOR = config['cache_for'] - CUE_STYLE = config['cue_style'] - #print config except Exception, e: print 'Error loading config file: ', e sys.exit() -#TIME = time.localtime(time.time()) -TIME = (2010, 6, 26, 15, 33, 23, 2, 322, 0) - class Global: def __init__(self): print def selfcheck(self): - self.api_auth = urllib.urlencode({'api_key': API_KEY}) self.api_client = api_client.api_client_factory(config) self.api_client.check_version() @@ -128,18 +113,23 @@ class Global: """ class Playout: def __init__(self): - self.file_dir = FILE_DIR - self.tmp_dir = TMP_DIR - self.api_auth = urllib.urlencode({'api_key': API_KEY}) self.api_client = api_client.api_client_factory(config) self.cue_file = CueFile() - self.silence_file = self.file_dir + 'basic/silence.mp3' - - # set initial state + self.silence_file = config["file_dir"] + 'basic/silence.mp3' + self.push_ahead = 15 self.range_updated = False + def test_api(self): self.api_client.test() + + + def set_export_source(self, export_source): + self.export_source = export_source + self.cache_dir = config["cache_dir"] + self.export_source + '/' + self.schedule_file = self.cache_dir + 'schedule.pickle' + self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle" + """ Fetching part of pypo @@ -155,9 +145,7 @@ class Playout: """ logger = logging.getLogger() - self.export_source = export_source - self.cache_dir = CACHE_DIR + self.export_source + '/' - self.schedule_file = self.cache_dir + 'schedule' + self.set_export_source(export_source) try: os.mkdir(self.cache_dir) except Exception, e: pass @@ -185,10 +173,10 @@ class Playout: except Exception, e: logger.error("%s", e) # prepare the playlists - if CUE_STYLE == 'pre': - try: self.prepare_playlists_cue(self.export_source) + if config["cue_style"] == 'pre': + try: self.prepare_playlists_cue() except Exception, e: logger.error("%s", e) - elif CUE_STYLE == 'otf': + elif config["cue_style"] == 'otf': try: self.prepare_playlists(self.export_source) except Exception, e: logger.error("%s", e) @@ -210,10 +198,10 @@ class Playout: tnow = time.localtime(time.time()) - if(tnow[3] == 16): + if (tnow[3] == 16): self.range_updated = False - if(tnow[3] == 17 and self.range_updated == False): + if (tnow[3] == 17 and self.range_updated == False): try: print self.api_client.generate_range_dp() logger.info("daypart updated") @@ -248,18 +236,11 @@ class Playout: playlist folder. file is eg 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3 """ - def prepare_playlists_cue(self, export_source): + def prepare_playlists_cue(self): logger = logging.getLogger() - self.export_source = export_source - - try: - schedule_file = open(self.schedule_file, "r") - schedule = pickle.load(schedule_file) - schedule_file.close() - except Exception, e: - logger.error("%s", e) - schedule = None + # Load schedule from disk + schedule = self.load_schedule() # Dont do anything if schedule is empty if (not schedule): @@ -271,7 +252,6 @@ class Playout: try: for pkey in scheduleKeys: logger.info("found playlist at %s", pkey) - #print pkey playlist = schedule[pkey] # create playlist directory @@ -282,18 +262,20 @@ class Playout: ls_playlist = ''; - print '*****************************************' - print 'pkey: ' + str(pkey) - print 'cached at : ' + self.cache_dir + str(pkey) - print 'subtype: ' + str(playlist['subtype']) - print 'played: ' + str(playlist['played']) - print 'schedule id: ' + str(playlist['schedule_id']) - print 'duration: ' + str(playlist['duration']) - print 'source id: ' + str(playlist['x_ident']) - print '*****************************************' + logger.debug('*****************************************') + logger.debug('pkey: ' + str(pkey)) + logger.debug('cached at : ' + self.cache_dir + str(pkey)) + logger.debug('subtype: ' + str(playlist['subtype'])) + logger.debug('played: ' + str(playlist['played'])) + logger.debug('schedule id: ' + str(playlist['schedule_id'])) + logger.debug('duration: ' + str(playlist['duration'])) + logger.debug('source id: ' + str(playlist['x_ident'])) + logger.debug('*****************************************') + + # Creating an API call like the next two lines would make this more flexible + # mediaType = api_client.get_media_type(playlist) + # if (mediaType == PYPO_MEDIA_SKIP): - #mediaType = api_client.get_media_type(playlist) - #if (mediaType == PYPO_MEDIA_SKIP): if int(playlist['played']) == 1: logger.info("playlist %s already played / sent to liquidsoap, so will ignore it", pkey) @@ -401,7 +383,6 @@ class Playout: if True == os.access(dst, os.R_OK): # check filesize (avoid zero-byte files) - #print 'waiting: ' + dst try: fsize = os.path.getsize(dst) except Exception, e: logger.error("%s", e) @@ -445,15 +426,11 @@ class Playout: else: if os.path.isfile(dst): logger.debug("file already in cache: %s", dst) - #print 'cached' else: logger.debug("try to download and cue %s", media['uri']) - #print '***' - dst_tmp = self.tmp_dir + "".join([random.choice(string.letters) for i in xrange(10)]) + '.mp3' - #print dst_tmp - #print '***' + dst_tmp = config["tmp_dir"] + "".join([random.choice(string.letters) for i in xrange(10)]) + '.mp3' self.api_client.get_media(media['uri'], dst_tmp) # cue @@ -509,7 +486,7 @@ class Playout: logger.debug("try to copy and cue %s", media['uri']) print '***' - dst_tmp = self.tmp_dir + "".join([random.choice(string.letters) for i in xrange(10)]) + dst_tmp = config["tmp_dir"] + "".join([random.choice(string.letters) for i in xrange(10)]) print dst_tmp print '***' @@ -550,10 +527,8 @@ class Playout: """ logger = logging.getLogger() - self.export_source = export_source - self.cache_dir = CACHE_DIR + self.export_source + '/' - self.schedule_file = self.cache_dir + 'schedule' - offset = 3600 * int(CACHE_FOR) + self.set_export_source(export_source) + offset = 3600 * int(config["cache_for"]) now = time.time() for r, d, f in os.walk(self.cache_dir): @@ -588,128 +563,109 @@ class Playout: def push(self, export_source): logger = logging.getLogger() - self.export_source = export_source - self.cache_dir = CACHE_DIR + self.export_source + '/' - self.schedule_file = self.cache_dir + 'schedule' + self.set_export_source(export_source) - self.push_ahead = 15 + #try: + # dummy = self.schedule + # logger.debug('schedule already loaded') + #except Exception, e: + # self.schedule = self.push_init(self.export_source) + + self.schedule = self.load_schedule() + playedItems = self.load_schedule_tracker() - try: - dummy = self.schedule - logger.debug('schedule already loaded') - except Exception, e: - self.schedule = self.push_init(self.export_source) - - self.schedule = self.push_init(self.export_source) - - - """ - I'm quite sure that this could be achieved in a much more elegant way in python... - """ - tcomming = time.localtime(time.time() + self.push_ahead) tnow = time.localtime(time.time()) str_tnow = "%04d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4]) - str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5]) - + str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5]) str_tcomming = "%04d-%02d-%02d-%02d-%02d" % (tcomming[0], tcomming[1], tcomming[2], tcomming[3], tcomming[4]) str_tcomming_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcomming[0], tcomming[1], tcomming[2], tcomming[3], tcomming[4], tcomming[5]) - #print '--' - #print str_tnow_s + ' now' - #print str_tcomming_s + ' comming' - playnow = None if self.schedule == None: - print 'unable to loop schedule - maybe write in progress' - print 'will try in next loop' + logger.warn('Unable to loop schedule - maybe write in progress?') + logger.warn('Will try again in next loop.') else: - for pkey in self.schedule: - + for pkey in self.schedule: if pkey[0:16] == str_tcomming: logger.debug('Preparing to push playlist scheduled at: %s', pkey) playlist = self.schedule[pkey] - - if int(playlist['played']) != 1: - #print '!!!!!!!!!!!!!!!!!!!' - #print 'MATCH' - - """ - ok we have a match, replace the current playlist and - force liquidsoap to refresh - Add a 'played' state to the list in schedule, so it is not called again - in the next push loop - """ + playedFlag = (pkey in playedItems) and playedItems[pkey].get("played", 0) + logger.debug("PLAYED FLAG: " + str(playedFlag)) + if not playedFlag: + # We have a match, replace the current playlist and + # force liquidsoap to refresh. ptype = playlist['subtype'] - - try: - user_id = playlist['user_id'] - playlist_id = playlist['id'] - transmission_id = playlist['schedule_id'] - - except Exception, e: - playlist_id = 0 - user_id = 0 - transmission_id = 0 - print e - - #print 'Playlist id:', - - if (self.push_liquidsoap(pkey, ptype, user_id, playlist_id, transmission_id, self.push_ahead) == 1): + + if (self.push_liquidsoap(pkey, self.schedule, ptype) == 1): logger.debug("Pushed to liquidsoap, updating 'played' status.") - self.schedule[pkey]['played'] = 1 - """ - Call api to update schedule states and - write changes back to cache file - """ - self.api_client.update_scheduled_item(int(playlist['schedule_id']), 1) - schedule_file = open(self.schedule_file, "w") - pickle.dump(self.schedule, schedule_file) - schedule_file.close() - logger.debug("Wrote schedule to disk") - - #else: - # print 'Nothing to do...' + # Marked the current playlist as 'played' in the schedule tracker + # so it is not called again in the next push loop. + # Write changes back to tracker file. + playedItems[pkey] = playlist + playedItems[pkey]['played'] = 1 + schedule_tracker = open(self.schedule_tracker_file, "w") + pickle.dump(playedItems, schedule_tracker) + schedule_tracker.close() + logger.debug("Wrote schedule to disk: "+str(playedItems)) + + # Call API to update schedule states + logger.debug("Doing callback to server to update 'played' status.") + self.api_client.notify_scheduled_item_start_playing(pkey, self.schedule) - def push_init(self, export_source): + def load_schedule(self): logger = logging.getLogger() + schedule = None - self.export_source = export_source - self.cache_dir = CACHE_DIR + self.export_source + '/' - self.schedule_file = self.cache_dir + 'schedule' - - # load the schedule from cache - logger.debug('loading schedule from cache...') - try: - schedule_file = open(self.schedule_file, "r") - schedule = pickle.load(schedule_file) - schedule_file.close() - - except Exception, e: - logger.error('%s', e) - schedule = None + # create the file if it doesnt exist + if (not os.path.exists(self.schedule_file)): + logger.debug('creating file ' + self.schedule_file) + open(self.schedule_file, 'w').close() + else: + # load the schedule from cache + logger.debug('loading schedule file '+self.schedule_file) + try: + schedule_file = open(self.schedule_file, "r") + schedule = pickle.load(schedule_file) + schedule_file.close() + + except Exception, e: + logger.error('%s', e) return schedule - def push_liquidsoap(self, pkey, ptype, user_id, playlist_id, transmission_id, push_ahead): + def load_schedule_tracker(self): logger = logging.getLogger() + playedItems = dict() + + # create the file if it doesnt exist + if (not os.path.exists(self.schedule_tracker_file)): + logger.debug('creating file ' + self.schedule_tracker_file) + schedule_tracker = open(self.schedule_tracker_file, 'w') + pickle.dump(playedItems, schedule_tracker) + schedule_tracker.close() + else: + try: + logger.debug('loading schedule tracker file '+ self.schedule_tracker_file) + schedule_tracker = open(self.schedule_tracker_file, "r") + playedItems = pickle.load(schedule_tracker) + schedule_tracker.close() + except Exception, e: + logger.error('Unable to load schedule tracker file: %s', e) + + return playedItems + - #self.export_source = export_source - - self.push_ahead = push_ahead - - self.cache_dir = CACHE_DIR + self.export_source + '/' - self.schedule_file = self.cache_dir + 'schedule' - + def push_liquidsoap(self, pkey, schedule, ptype): + logger = logging.getLogger() + src = self.cache_dir + str(pkey) + '/list.lsp' - logger.debug(src) - try: if True == os.access(src, os.R_OK): logger.debug('OK - Can read playlist file') @@ -724,8 +680,7 @@ class Playout: if (int(ptype) == 6): tn.write("live_in.start") tn.write("\n") - - + if (int(ptype) < 5): for line in pl_file.readlines(): logger.debug(line.strip()) @@ -741,19 +696,11 @@ class Playout: logger.debug('sending "flip"') tn = telnetlib.Telnet(LS_HOST, 1234) - """ - Pass some extra information to liquidsoap - """ - logger.debug('user_id: %s', user_id) - logger.debug('playlist_id: %s', playlist_id) - logger.debug('transmission_id: %s', transmission_id) - logger.debug('ptype: %s', ptype) - - tn.write("vars.user_id %s\n" % user_id) - tn.write("vars.playlist_id %s\n" % playlist_id) - tn.write("vars.transmission_id %s\n" % transmission_id) - tn.write("vars.playlist_type %s\n" % ptype) - + # 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: "+liquidsoap_data) + tn.write("vars.pypo_data "+liquidsoap_data+"\n") + # if(int(ptype) < 5): # tn.write(self.export_source + '.flip') # tn.write("\n") @@ -761,7 +708,7 @@ class Playout: tn.write(self.export_source + '.flip') tn.write("\n") - if(int(ptype) == 6): + if (int(ptype) == 6): tn.write("live.active 1") tn.write("\n") else: @@ -772,7 +719,7 @@ class Playout: tn.write("exit\n") - logger.debug(tn.read_all()) + tn.read_all() status = 1 except Exception, e: logger.error('%s', e) @@ -857,38 +804,38 @@ class Playout: """ Updates the jingles. Give comma separated list of jingle tracks. + + NOTE: commented out because it needs to be converted to use the API client. - Paul """ - def update_jingles(self, options): - print 'jingles' - - jingle_list = string.split(options, ',') - print jingle_list - for media_id in jingle_list: - # api path maybe should not be hard-coded - src = API_BASE + 'api/pypo/get_media/' + str(media_id) - print src - # include the hourly jungles for the moment - dst = "%s%s/%s.mp3" % (self.file_dir, 'jingles/hourly', str(media_id)) - print dst - - try: - print '** urllib auth with: ', - print self.api_auth - opener = urllib.URLopener() - opener.retrieve (src, dst, False, self.api_auth) - logger.info("downloaded %s to %s", src, dst) - except Exception, e: - print e - logger.error("%s", e) + #def update_jingles(self, options): + # print 'jingles' + # + # jingle_list = string.split(options, ',') + # print jingle_list + # for media_id in jingle_list: + # # api path maybe should not be hard-coded + # src = API_BASE + 'api/pypo/get_media/' + str(media_id) + # print src + # # include the hourly jungles for the moment + # dst = "%s%s/%s.mp3" % (config["file_dir"], 'jingles/hourly', str(media_id)) + # print dst + # + # try: + # print '** urllib auth with: ', + # print self.api_auth + # opener = urllib.URLopener() + # opener.retrieve (src, dst, False, self.api_auth) + # logger.info("downloaded %s to %s", src, dst) + # except Exception, e: + # print e + # logger.error("%s", e) def check_schedule(self, export_source): logger = logging.getLogger() - self.export_source = export_source - self.cache_dir = CACHE_DIR + self.export_source + '/' - self.schedule_file = self.cache_dir + 'schedule' - + self.set_export_source(export_source) + try: schedule_file = open(self.schedule_file, "r") schedule = pickle.load(schedule_file) @@ -898,11 +845,8 @@ class Playout: logger.error("%s", e) schedule = None - #for pkey in schedule: for pkey in sorted(schedule.iterkeys()): - playlist = schedule[pkey] - print '*****************************************' print '\033[0;32m%s %s\033[m' % ('scheduled at:', str(pkey)) print 'cached at : ' + self.cache_dir + str(pkey) @@ -911,7 +855,6 @@ class Playout: print 'schedule id: ' + str(playlist['schedule_id']) print 'duration: ' + str(playlist['duration']) print 'source id: ' + str(playlist['x_ident']) - print '-----------------------------------------' for media in playlist['medias']: @@ -921,7 +864,6 @@ class Playout: if __name__ == '__main__': - print print '###########################################' print '# *** pypo *** #' @@ -997,11 +939,11 @@ while run == True: time.sleep(PUSH_INTERVAL) - while options.jingles: - try: po.update_jingles(options.jingles) - except Exception, e: - print e - sys.exit() + #while options.jingles: + # try: po.update_jingles(options.jingles) + # except Exception, e: + # print e + # sys.exit() while options.check: @@ -1011,7 +953,7 @@ while run == True: sys.exit() while options.cleanup: - try: po.cleanup() + try: po.cleanup('scheduler') except Exception, e: print e sys.exit() diff --git a/3rd_party/pypo/pypo_dls.py b/3rd_party/pypo/pypo_dls.py index 6ed030ea3..c20176137 100755 --- a/3rd_party/pypo/pypo_dls.py +++ b/3rd_party/pypo/pypo_dls.py @@ -1,21 +1,18 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# author Jonas Ohrstrom - """ Python part of radio playout (pypo) This function acts as a gateway between liquidsoap and the obp-api. -Mainliy used to tell the plattform what pypo/LS does. +Mainliy used to tell the platform what pypo/LS does. Main case: - whenever Liquidsoap starts playing a new track, its on_metadata callback calls a function in liquidsoap (notify(m)) which then calls the python script here with the currently starting filename as parameter - this python script takes this parameter, tries to extract the actual - media id from it, and then calls back to obp via api to tell about - + media id from it, and then calls back to API to tell it about it. """ @@ -171,7 +168,6 @@ class Notify: if __name__ == '__main__': - print print '#########################################' print '# *** pypo *** #' diff --git a/3rd_party/pypo/pypo_notify.py b/3rd_party/pypo/pypo_notify.py index 402b55453..cf542c105 100755 --- a/3rd_party/pypo/pypo_notify.py +++ b/3rd_party/pypo/pypo_notify.py @@ -1,21 +1,18 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# author Jonas Ohrstrom - """ Python part of radio playout (pypo) -This function acts as a gateway between liquidsoap and the obp-api. -Mainliy used to tell the plattform what pypo/LS does. +This function acts as a gateway between liquidsoap and the server API. +Mainly used to tell the platform what pypo/liquidsoap does. Main case: - whenever LS starts playing a new track, its on_metadata callback calls - a function in ls (notify(m)) which then calls the pythin script here + a function in ls (notify(m)) which then calls the python script here with the currently starting filename as parameter - this python script takes this parameter, tries to extract the actual - media id from it, and then calls back to obp via api to tell about - + media id from it, and then calls back to the API to tell about it about it. """ @@ -52,14 +49,14 @@ usage = "%prog [options]" + " - notification gateway" parser = OptionParser(usage=usage) # Options -#parser.add_option("-p", "--playing", help="Tell daddy what is playing right now", dest="playing", default=False, metavar=False) -parser.add_option("-p", "--playing", help="Tell daddy what is playing right now", default=False, action="store_true", dest="playing") -parser.add_option("-t", "--playlist-type", help="Tell daddy what is playing right now", metavar="playlist_type") -parser.add_option("-M", "--media-id", help="Tell daddy what is playing right now", metavar="media_id") -parser.add_option("-U", "--user-id", help="Tell daddy what is playing right now", metavar="user_id") -parser.add_option("-P", "--playlist-id", help="Tell daddy what is playing right now", metavar="playlist_id") -parser.add_option("-T", "--transmission-id", help="Tell daddy what is playing right now", metavar="transmission_id") -parser.add_option("-E", "--export-source", help="Tell daddy what is playing right now", metavar="export_source") +parser.add_option("-d", "--data", help="Pass JSON data from liquidsoap into this script.", metavar="data") +#parser.add_option("-p", "--playing", help="Tell server what is playing right now.", default=False, action="store_true", dest="playing") +#parser.add_option("-t", "--playlist-type", help="", metavar="playlist_type") +parser.add_option("-m", "--media-id", help="ID of the file that is currently playing.", metavar="media_id") +#parser.add_option("-U", "--user-id", help="", metavar="user_id") +#parser.add_option("-P", "--playlist-id", help="", metavar="playlist_id") +#parser.add_option("-T", "--transmission-id", help="", metavar="transmission_id") +#parser.add_option("-E", "--export-source", help="", metavar="export_source") # parse options (options, args) = parser.parse_args() @@ -70,10 +67,6 @@ logging.config.fileConfig("logging.cfg") # loading config file try: config = ConfigObj('config.cfg') - TMP_DIR = config['tmp_dir'] - BASE_URL = config['base_url'] - API_BASE = BASE_URL + 'mod/medialibrary/' - API_KEY = config['api_key'] except Exception, e: print 'error: ', e @@ -85,144 +78,156 @@ class Global: print def selfcheck(self): - - self.api_auth = urllib.urlencode({'api_key': API_KEY}) - self.api_client = api_client.api_client_factory(config) - self.api_client.check_version() + pass + #self.api_client = api_client.api_client_factory(config) + #self.api_client.check_version() class Notify: def __init__(self): - self.tmp_dir = TMP_DIR - self.api_auth = urllib.urlencode({'api_key': API_KEY}) self.api_client = api_client.api_client_factory(config) - self.dls_client = DlsClient('127.0.0.128', 50008, 'myusername', 'mypass') + #self.dls_client = DlsClient('127.0.0.128', 50008, 'myusername', 'mypass') - def start_playing(self, options): - logger = logging.getLogger("start_playing") - tnow = time.localtime(time.time()) + def notify_media_start_playing(self, data, media_id): + logger = logging.getLogger() + #tnow = time.localtime(time.time()) - #print options - - print '#################################################' - print '# calling obp to tell about what\'s playing #' - print '#################################################' - - if int(options.playlist_type) < 5: - print 'seems to be a playlist' - - try: - media_id = int(options.media_id) - except Exception, e: - media_id = 0 - - response = self.api_client.update_start_playing(options.playlist_type, options.export_source, media_id, options.playlist_id, options.transmission_id) - - print response - - if int(options.playlist_type) == 6: - print 'seems to be a couchcast' - - try: - media_id = int(options.media_id) - except Exception, e: - media_id = 0 - - response = self.api_client.update_start_playing(options.playlist_type, options.export_source, media_id, options.playlist_id, options.transmission_id) - - print response - - sys.exit() + logger.debug('#################################################') + logger.debug('# Calling server to update about what\'s playing #') + logger.debug('#################################################') + logger.debug('data = '+ str(data)) + #print 'options.data = '+ options.data + #data = json.read(options.data) + response = self.api_client.notify_media_item_start_playing(data, media_id) + logger.debug("Response: "+str(response)) + #def start_playing(self, options): + # logger = logging.getLogger("start_playing") + # tnow = time.localtime(time.time()) + # + # #print options + # + # logger.debug('#################################################') + # logger.debug('# Calling server to update about what\'s playing #') + # logger.debug('#################################################') + # + # if int(options.playlist_type) < 5: + # logger.debug('seems to be a playlist') + # + # try: + # media_id = int(options.media_id) + # except Exception, e: + # media_id = 0 + # + # response = self.api_client.update_start_playing(options.playlist_type, options.export_source, media_id, options.playlist_id, options.transmission_id) + # + # logger.debug(response) + # + # if int(options.playlist_type) == 6: + # logger.debug('seems to be a couchcast') + # + # try: + # media_id = int(options.media_id) + # except Exception, e: + # media_id = 0 + # + # response = self.api_client.update_start_playing(options.playlist_type, options.export_source, media_id, options.playlist_id, options.transmission_id) + # + # logger.debug(response) + # + # sys.exit() - def start_playing_legacy(self, options): - logger = logging.getLogger("start_playing") - tnow = time.localtime(time.time()) - - print '#################################################' - print '# calling obp to tell about what\'s playing #' - print '#################################################' - - path = options - - print - print path - print - - if 'pl_id' in path: - print 'seems to be a playlist' - type = 'playlist' - id = path[5:] - - elif 'text' in path: - print 'seems to be a playlist' - type = 'text' - id = path[4:] - print id - - else: - print 'seems to be a single track (media)' - type = 'media' - try: - file = path.split("/")[-1:][0] - if file.find('_cue_') > 0: - id = file.split("_cue_")[0] - else: - id = file.split(".")[-2:][0] - - except Exception, e: - #print e - id = False - - try: - id = id - except Exception, e: - #print e - id = False - - print - print type + " id: ", - print id - - - response = self.api_client.update_start_playing(type, id, self.export_source, path) - - print 'DONE' - - try: - txt = response['txt'] - print '#######################################' - print txt - print '#######################################' - #self.dls_client.set_txt(txt) - - except Exception, e: - print e + #def start_playing_legacy(self, options): + # logger = logging.getLogger("start_playing") + # tnow = time.localtime(time.time()) + # + # print '#################################################' + # print '# Calling server to update about what\'s playing #' + # print '#################################################' + # + # path = options + # + # print + # print path + # print + # + # if 'pl_id' in path: + # print 'seems to be a playlist' + # type = 'playlist' + # id = path[5:] + # + # elif 'text' in path: + # print 'seems to be a playlist' + # type = 'text' + # id = path[4:] + # print id + # + # else: + # print 'seems to be a single track (media)' + # type = 'media' + # try: + # file = path.split("/")[-1:][0] + # if file.find('_cue_') > 0: + # id = file.split("_cue_")[0] + # else: + # id = file.split(".")[-2:][0] + # + # except Exception, e: + # #print e + # id = False + # + # try: + # id = id + # except Exception, e: + # #print e + # id = False + # + # print + # print type + " id: ", + # print id + # + # + # response = self.api_client.update_start_playing(type, id, self.export_source, path) + # + # print 'DONE' + # + # try: + # txt = response['txt'] + # print '#######################################' + # print txt + # print '#######################################' + # #self.dls_client.set_txt(txt) + # + # except Exception, e: + # print e if __name__ == '__main__': - print print '#########################################' print '# *** pypo *** #' print '# pypo notification gateway #' print '#########################################' - print # initialize g = Global() - g.selfcheck() - n = Notify() - - -run = True -while run == True: - logger = logging.getLogger("pypo notify") - - if options.playing: - try: n.start_playing(options) - except Exception, e: - print e - sys.exit() - - sys.exit() + logger = logging.getLogger() + #if options.playing: + # try: n.start_playing(options) + # except Exception, e: + # print e + # sys.exit() + if not options.data: + print "NOTICE: 'data' command-line argument not given." + sys.exit() + + if not options.media_id: + print "NOTICE: 'media_id' command-line argument not given." + sys.exit() + + try: + g.selfcheck() + n = Notify() + n.notify_media_start_playing(options.data, options.media_id) + except Exception, e: + print e diff --git a/3rd_party/pypo/scripts/cue_file.py b/3rd_party/pypo/scripts/cue_file.py index c52dd7a58..9d9215903 100755 --- a/3rd_party/pypo/scripts/cue_file.py +++ b/3rd_party/pypo/scripts/cue_file.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# author Jonas Ohrstrom - """ cue script that gets called by liquidsoap if a file in the playlist diff --git a/3rd_party/pypo/scripts/include_dynamic_vars.liq b/3rd_party/pypo/scripts/include_dynamic_vars.liq index c62c868a0..54e285d4b 100644 --- a/3rd_party/pypo/scripts/include_dynamic_vars.liq +++ b/3rd_party/pypo/scripts/include_dynamic_vars.liq @@ -6,7 +6,11 @@ playlist_id = ref '0' user_id = ref '0' transmission_id = ref '0' playlist_type = ref '0' +pypo_data = ref '0' +def set_pypo_data(s) + pypo_data := s +end def set_user_id(s) user_id := s @@ -29,4 +33,5 @@ end server.register(namespace="vars", "user_id", fun (s) -> begin set_user_id(s) "Done!" end) server.register(namespace="vars", "playlist_id", fun (s) -> begin set_playlist_id(s) "Done!" end) server.register(namespace="vars", "transmission_id", fun (s) -> begin set_transmission_id(s) "Done!" end) -server.register(namespace="vars", "playlist_type", fun (s) -> begin set_playlist_type(s) "Done!" end) \ No newline at end of file +server.register(namespace="vars", "playlist_type", fun (s) -> begin set_playlist_type(s) "Done!" end) +server.register(namespace="vars", "pypo_data", fun (s) -> begin set_pypo_data(s) "Done!" end) \ No newline at end of file diff --git a/3rd_party/pypo/scripts/include_notify.liq b/3rd_party/pypo/scripts/include_notify.liq index 8dc125907..765ab4168 100644 --- a/3rd_party/pypo/scripts/include_notify.liq +++ b/3rd_party/pypo/scripts/include_notify.liq @@ -4,10 +4,10 @@ def notify(m) - print('user_id: #{!user_id}') - print('playlist_id: #{!playlist_id}') - print('transmission_id: #{!transmission_id}') - print('playlist_type: #{!playlist_type}') +# print('user_id: #{!user_id}') +# print('playlist_id: #{!playlist_id}') +# print('transmission_id: #{!transmission_id}') +# print('playlist_type: #{!playlist_type}') if !playlist_type=='5' then print('livesession') @@ -20,8 +20,9 @@ def notify(m) end if !playlist_type=='0' or !playlist_type=='1' or !playlist_type=='2' or !playlist_type=='3' or !playlist_type=='4' then - print('playlist') - system("./notify.sh --playing --playlist-type=#{!playlist_type} --media-id=#{m['media_id']} --export-source=#{m['export_source']}") + print('include_notify.liq: notify on playlist') + #system("./notify.sh --playing --playlist-type=#{!playlist_type} --media-id=#{m['media_id']} --export-source=#{m['export_source']}") + system("./notify.sh --data='#{!pypo_data}' --media-id=#{m['media_id']}") end end \ No newline at end of file diff --git a/3rd_party/pypo/scripts/ls_config.liq.dist b/3rd_party/pypo/scripts/ls_config.liq.dist index e584df463..0dbec3f53 100644 --- a/3rd_party/pypo/scripts/ls_config.liq.dist +++ b/3rd_party/pypo/scripts/ls_config.liq.dist @@ -2,9 +2,6 @@ # liquidsoap config file # ########################################### -# author Jonas Ohrstrom - - # this file is specific to the obp # installation. eg it assumes that there are # two instances of LS running diff --git a/3rd_party/pypo/scripts/ls_cue.liq b/3rd_party/pypo/scripts/ls_cue.liq index d12e46825..28a551d94 100644 --- a/3rd_party/pypo/scripts/ls_cue.liq +++ b/3rd_party/pypo/scripts/ls_cue.liq @@ -1,4 +1,4 @@ -# author Jonas Ohrstrom + # Register the cut protocol def cue_protocol(arg,delay) # The extraction program diff --git a/3rd_party/pypo/util/cue_file.py b/3rd_party/pypo/util/cue_file.py index 744e9d09a..4a9f1c4f2 100755 --- a/3rd_party/pypo/util/cue_file.py +++ b/3rd_party/pypo/util/cue_file.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# author Jonas Ohrstrom - import sys import shutil import random diff --git a/api/get_media.php b/api/get_media.php index 8b0ee9e04..0ae512bbc 100644 --- a/api/get_media.php +++ b/api/get_media.php @@ -1,6 +1,5 @@ getMessage()." ".$CC_DBC->getUserInfo()."\n"; - exit(1); -} -$CC_DBC->setFetchMode(DB_FETCHMODE_ASSOC); - $filename = $_GET["file"]; $file_id = substr($filename, 0, strpos($filename, ".")); if (ctype_alnum($file_id) && strlen($file_id) == 32) { diff --git a/api/notify_media_item_start_play.php b/api/notify_media_item_start_play.php new file mode 100644 index 000000000..6a415fbff --- /dev/null +++ b/api/notify_media_item_start_play.php @@ -0,0 +1,37 @@ +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; + } +} else { + echo json_encode(array("status"=>0, "message" => "Incorrect or non-numeric arguments given.")); +} +?> \ No newline at end of file diff --git a/api/notify_schedule_group_play.php b/api/notify_schedule_group_play.php new file mode 100644 index 000000000..7527892bb --- /dev/null +++ b/api/notify_schedule_group_play.php @@ -0,0 +1,35 @@ +exists()) { + $result = $sg->notifyGroupStartPlay(); + 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; + } +} else { + echo json_encode(array("status"=>0, "message"=>"Incorrect or non-numeric arguments given.")); + exit; +} +?> \ No newline at end of file diff --git a/api/schedule.php b/api/schedule.php index a004d57ca..2009cf4d8 100644 --- a/api/schedule.php +++ b/api/schedule.php @@ -1,6 +1,5 @@ getMessage()." ".$CC_DBC->getUserInfo()."\n"; - exit(1); -} -$CC_DBC->setFetchMode(DB_FETCHMODE_ASSOC); $from = $_GET["from"]; $to = $_GET["to"]; echo Schedule::ExportRangeAsJson($from, $to); diff --git a/backend/Schedule.php b/backend/Schedule.php index 9ee043684..fa80671f3 100644 --- a/backend/Schedule.php +++ b/backend/Schedule.php @@ -10,6 +10,21 @@ class ScheduleGroup { $this->groupId = $p_groupId; } + /** + * Return true if the schedule group exists in the DB. + * @return boolean + */ + public function exists() { + global $CC_CONFIG, $CC_DBC; + $sql = "SELECT COUNT(*) FROM ".$CC_CONFIG['scheduleTable'] + ." WHERE group_id=".$this->groupId; + $result = $CC_DBC->GetOne($sql); + if (PEAR::isError($result)) { + return $result; + } + return $result != "0"; + } + /** * Convert a date to an ID by stripping out all characters * and padding with zeros. @@ -204,6 +219,22 @@ class ScheduleGroup { // $sql = "UPDATE ".$CC_CONFIG["scheduleTable"]. " SET id=, starts=,ends=" } + public function notifyGroupStartPlay() { + global $CC_CONFIG, $CC_DBC; + $sql = "UPDATE ".$CC_CONFIG['scheduleTable'] + ." SET schedule_group_played=TRUE" + ." WHERE group_id=".$this->groupId; + return $CC_DBC->query($sql); + } + + public function notifyMediaItemStartPlay($p_fileId) { + global $CC_CONFIG, $CC_DBC; + $sql = "UPDATE ".$CC_CONFIG['scheduleTable'] + ." SET media_item_played=TRUE" + ." WHERE group_id=".$this->groupId + ." AND file_id=".pg_escape_string($p_fileId); + return $CC_DBC->query($sql); + } } class Schedule { @@ -464,7 +495,7 @@ class Schedule { $playlists[$pkey]['user_id'] = 0; $playlists[$pkey]['id'] = $dx["playlist_id"]; $playlists[$pkey]['start'] = Schedule::CcTimeToPypoTime($dx["start"]); - $playlists[$pkey]['end'] = Schedule::CcTimeToPypoTime($dx["end"]); + $playlists[$pkey]['end'] = Schedule::CcTimeToPypoTime($dx["end"]); } } diff --git a/backend/propel-db/build/sql/schema.sql b/backend/propel-db/build/sql/schema.sql index 401ce4dd3..38ae1b962 100644 --- a/backend/propel-db/build/sql/schema.sql +++ b/backend/propel-db/build/sql/schema.sql @@ -244,6 +244,8 @@ CREATE TABLE "cc_schedule" "fade_out" TIME default '00:00:00', "cue_in" TIME default '00:00:00', "cue_out" TIME default '00:00:00', + "schedule_group_played" BOOLEAN default 'f', + "media_item_played" BOOLEAN default 'f', PRIMARY KEY ("id") ); diff --git a/backend/propel-db/schema.xml b/backend/propel-db/schema.xml index 565137772..8741b649c 100644 --- a/backend/propel-db/schema.xml +++ b/backend/propel-db/schema.xml @@ -182,6 +182,8 @@ + + diff --git a/backend/tests/pypoTester.php b/backend/tests/pypoTester.php index 36925e923..4443b14c1 100644 --- a/backend/tests/pypoTester.php +++ b/backend/tests/pypoTester.php @@ -31,11 +31,26 @@ $pl = new Playlist(); $pl->create($playlistName); // Add a media clip -$mediaFile = StoredFile::findByOriginalName("test10001.mp3"); +$mediaFile = StoredFile::findByOriginalName("Manolo Camp - Morning Coffee.mp3"); if (is_null($mediaFile)) { echo "Adding test audio clip to the database.\n"; - $v = array("filepath" => __DIR__."/test10001.mp3"); + $v = array("filepath" => __DIR__."/../../audio_samples/OpSound/Manolo Camp - Morning Coffee.mp3"); $mediaFile = StoredFile::Insert($v); + if (PEAR::isError($mediaFile)) { + var_dump($mediaFile); + exit(); + } +} +$pl->addAudioClip($mediaFile->getId()); +$mediaFile = StoredFile::findByOriginalName("Peter Rudenko - Opening.mp3"); +if (is_null($mediaFile)) { + echo "Adding test audio clip to the database.\n"; + $v = array("filepath" => __DIR__."/../../audio_samples/OpSound/Peter Rudenko - Opening.mp3"); + $mediaFile = StoredFile::Insert($v); + if (PEAR::isError($mediaFile)) { + var_dump($mediaFile); + exit(); + } } $pl->addAudioClip($mediaFile->getId()); echo "done.\n";