diff --git a/3rd_party/php/Zend/LICENSE b/3rd_party/php/Zend/LICENSE new file mode 100644 index 000000000..1d650c296 --- /dev/null +++ b/3rd_party/php/Zend/LICENSE @@ -0,0 +1,30 @@ +New BSD License + +Copyright (c) 2005-2010, Zend Technologies USA, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Zend Technologies USA, Inc. nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/3rd_party/pypo/api_clients/api_client.py b/3rd_party/pypo/api_clients/api_client.py index e69ead13e..cdb997c9e 100644 --- a/3rd_party/pypo/api_clients/api_client.py +++ b/3rd_party/pypo/api_clients/api_client.py @@ -164,26 +164,47 @@ class CampcasterApiClient(ApiClientInterface): # 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) + #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("export from %s", export_url) + logger.info("Fetching schedule from %s", export_url) response = "" status = 0 try: response_json = urllib.urlopen(export_url).read() - logger.debug("%s", response_json) + #logger.debug("%s", response_json) response = json.read(response_json) - logger.info("export status %s", response['check']) + #logger.info("export status %s", response['check']) status = response['check'] except Exception, e: print e - return status, response + 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 def get_media(self, src, dst): @@ -200,68 +221,71 @@ class CampcasterApiClient(ApiClientInterface): 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"] - - 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 + 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 def update_start_playing(self, playlist_type, export_source, media_id, playlist_id, transmission_id): - 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 + 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 def generate_range_dp(self): - logger = logging.getLogger() - - url = self.api_url + 'schedule/generate_range_dp.php' - - try: - response = urllib.urlopen(url, self.api_auth) - response = json.read(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 + pass + #logger = logging.getLogger() + # + #url = self.api_url + 'schedule/generate_range_dp.php' + # + #try: + # response = urllib.urlopen(url, self.api_auth) + # response = json.read(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 diff --git a/3rd_party/pypo/config.cfg b/3rd_party/pypo/config.cfg index 1cd5a8bde..64652ca17 100644 --- a/3rd_party/pypo/config.cfg +++ b/3rd_party/pypo/config.cfg @@ -16,7 +16,7 @@ api_client = "campcaster" cache_dir = '/opt/pypo/cache/' schedule_dir = '/opt/pypo/cache/schedule' file_dir = '/opt/pypo/files/' -tmp_dir = '/tmp/pypo/' +tmp_dir = '/opt/pypo/tmp/' ############################################ # API path & co # diff --git a/3rd_party/pypo/pypo_cli.py b/3rd_party/pypo/pypo_cli.py index b572608ec..21ec292d7 100755 --- a/3rd_party/pypo/pypo_cli.py +++ b/3rd_party/pypo/pypo_cli.py @@ -257,24 +257,28 @@ class Playout: schedule_file = open(self.schedule_file, "r") schedule = pickle.load(schedule_file) schedule_file.close() - except Exception, e: logger.error("%s", e) schedule = None # Dont do anything if schedule is empty if (not schedule): + logger.debug("Schedule is empty.") return - + + scheduleKeys = sorted(schedule.iterkeys()) + try: - for pkey in sorted(schedule.iterkeys()): + for pkey in scheduleKeys: logger.info("found playlist at %s", pkey) #print pkey playlist = schedule[pkey] # create playlist directory - try: os.mkdir(self.cache_dir + str(pkey)) - except Exception, e: pass + try: + os.mkdir(self.cache_dir + str(pkey)) + except Exception, e: + pass ls_playlist = ''; @@ -343,6 +347,7 @@ class Playout: print 'File is expected to be at: ' + self.silence_file logger.critical('File is expected to be at: %s', self.silence_file) sys.exit() + return ls_playlist def handle_live_cast(self, playlist, pkey, ls_playlist): @@ -370,6 +375,7 @@ class Playout: def handle_media_file(self, playlist, pkey, ls_playlist): """ This handles both remote and local files. + Returns an updated ls_playlist string. """ logger = logging.getLogger() for media in playlist['medias']: @@ -439,7 +445,7 @@ class Playout: else: if os.path.isfile(dst): logger.debug("file already in cache: %s", dst) - print 'cached' + #print 'cached' else: logger.debug("try to download and cue %s", media['uri']) @@ -451,9 +457,10 @@ class Playout: self.api_client.get_media(media['uri'], dst_tmp) # cue - print "STARTING CUE" - print self.cue_file.cue(dst_tmp, dst, float(media['cue_in']) / 1000, float(media['cue_out']) / 1000) - print "END CUE" + logger.debug("STARTING CUE") + debugDst = self.cue_file.cue(dst_tmp, dst, float(media['cue_in']) / 1000, float(media['cue_out']) / 1000) + logger.debug(debugDst) + logger.debug("END CUE") if True == os.access(dst, os.R_OK): try: fsize = os.path.getsize(dst) @@ -539,38 +546,41 @@ class Playout: def cleanup(self, export_source): """ Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR" - and deletes them. + and deletes them. """ 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) now = time.time() - for r, d, f in os.walk(CACHE_DIR): + for r, d, f in os.walk(self.cache_dir): for dir in d: - timestamp = os.path.getmtime(os.path.join(r, dir)) - - logger.debug( 'Folder "Age": %s - %s', round((((now - offset) - timestamp) / 60), 2), os.path.join(r, dir)) - - if now - offset > timestamp: - try: - logger.debug('trying to remove %s - timestamp: %s', os.path.join(r, dir), timestamp) - shutil.rmtree(os.path.join(r, dir)) - except Exception, e: - logger.error("%s", e) - pass - else: - logger.info('sucessfully removed %s', os.path.join(r, dir)) - - + try: + timestamp = time.mktime(time.strptime(dir, "%Y-%m-%d-%H-%M-%S")) + #logger.debug('dir : %s', (dir)) + #logger.debug('age : %s', (round((now - timestamp),1))) + #logger.debug('delete in : %ss', (round((offset - (now - timestamp)),1))) + #logger.debug('Folder "Age": %s - %s', round((((now - offset) - timestamp) / 60), 2), os.path.join(r, dir)) + + if (now - timestamp) > offset: + try: + logger.debug('trying to remove %s - timestamp: %s', os.path.join(r, dir), timestamp) + shutil.rmtree(os.path.join(r, dir)) + except Exception, e: + logger.error("%s", e) + pass + else: + logger.info('sucessfully removed %s', os.path.join(r, dir)) + except Exception, e: + print e + logger.error("%s", e) """ - The counterpart - the push loop periodically (minimal 1/2 of the playlist-grid) + The Push Loop - the push loop periodically (minimal 1/2 of the playlist-grid) checks if there is a playlist that should be scheduled at the current time. If yes, the temporary liquidsoap playlist gets replaced with the corresponding one, then liquidsoap is asked (via telnet) to reload and immediately play it. @@ -606,9 +616,9 @@ class Playout: 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' + #print '--' + #print str_tnow_s + ' now' + #print str_tcomming_s + ' comming' playnow = None @@ -618,15 +628,14 @@ class Playout: else: for pkey in self.schedule: - logger.debug('found playlist scheduled at: %s', pkey) - #if pkey[0:16] == str_tnow: 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' + #print '!!!!!!!!!!!!!!!!!!!' + #print 'MATCH' """ ok we have a match, replace the current playlist and @@ -647,9 +656,10 @@ class Playout: transmission_id = 0 print e - print 'Playlist id:', + #print 'Playlist id:', - if(self.push_liquidsoap(pkey, ptype, user_id, playlist_id, transmission_id, self.push_ahead) == 1): + if (self.push_liquidsoap(pkey, ptype, user_id, playlist_id, transmission_id, self.push_ahead) == 1): + logger.debug("Pushed to liquidsoap, updating 'played' status.") self.schedule[pkey]['played'] = 1 """ Call api to update schedule states and @@ -658,12 +668,11 @@ class Playout: 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() + schedule_file.close() + logger.debug("Wrote schedule to disk") #else: # print 'Nothing to do...' - - def push_init(self, export_source): @@ -699,11 +708,11 @@ class Playout: src = self.cache_dir + str(pkey) + '/list.lsp' - print src + logger.debug(src) try: if True == os.access(src, os.R_OK): - print 'OK - Can read' + logger.debug('OK - Can read playlist file') pl_file = open(src, "r") @@ -712,34 +721,33 @@ class Playout: """ tn = telnetlib.Telnet(LS_HOST, 1234) - - if(int(ptype) == 6): + if (int(ptype) == 6): tn.write("live_in.start") tn.write("\n") - if(int(ptype) < 5): + if (int(ptype) < 5): for line in pl_file.readlines(): - print line.strip() + logger.debug(line.strip()) tn.write(self.export_source + '.push %s' % (line.strip())) tn.write("\n") #time.sleep(0.1) tn.write("exit\n") - print tn.read_all() - print 'sleeping for %s s' % (self.push_ahead) + logger.debug(tn.read_all()) + logger.debug('sleeping for %s s' % (self.push_ahead)) time.sleep(self.push_ahead) - print 'sending "flip"' + logger.debug('sending "flip"') tn = telnetlib.Telnet(LS_HOST, 1234) """ Pass some extra information to liquidsoap """ - print 'user_id: %s' % user_id - print 'playlist_id: %s' % playlist_id - print 'transmission_id: %s' % transmission_id - print 'ptype: %s' % ptype + 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) @@ -764,7 +772,7 @@ class Playout: tn.write("exit\n") - print tn.read_all() + logger.debug(tn.read_all()) status = 1 except Exception, e: logger.error('%s', e) @@ -773,78 +781,78 @@ class Playout: return status - def push_liquidsoap_legacy(self, pkey, ptype, p_id, user_id): - logger = logging.getLogger() - logger.debug('trying to push %s to liquidsoap', pkey) - - self.export_source = export_source - self.cache_dir = CACHE_DIR + self.export_source + '/' - self.schedule_file = self.cache_dir + 'schedule' - - src = self.cache_dir + str(pkey) + '/list.lsp' - dst = self.cache_dir + 'current.lsp' - - print src - print dst - - print '*************' - print ptype - print '*************' - - if True == os.access(src, os.R_OK): - try: - shutil.copy2(src, dst) - logger.debug('copy %s to %s', src, dst) - """ - i know this could be wrapped, maybe later.. - """ - tn = telnetlib.Telnet(LS_HOST, 1234) - tn.write("\n") - tn.write("live_in.stop\n") - tn.write("stream_disable\n") - time.sleep(0.2) - tn.write("\n") - #tn.write("reload_current\n") - tn.write("current.reload\n") - time.sleep(0.2) - tn.write("skip_current\n") - - if(int(ptype) == 6): - """ - Couchcaster comming. Stop/Start live input to have ls re-read it's playlist - """ - print 'Couchcaster - switching to stream' - tn.write("live_in.start\n") - time.sleep(0.2) - tn.write("stream_enable\n") - - if(int(ptype) == 7): - """ - Recast comming. Start the live input - """ - print 'Recast - switching to stream' - tn.write("live_in.start\n") - time.sleep(0.2) - tn.write("stream_enable\n") - - """ - Pass some extra information to liquidsoap - """ - tn.write("pl.pl_id '%s'\n" % p_id) - tn.write("pl.user_id '%s'\n" % user_id) - tn.write("exit\n") - - print tn.read_all() - - status = 1 - - except Exception, e: - logger.error('%s', e) - status = 0 - else: - status = 0 - - return status + #def push_liquidsoap_legacy(self, pkey, ptype, p_id, user_id): + # logger = logging.getLogger() + # logger.debug('trying to push %s to liquidsoap', pkey) + # + # self.export_source = export_source + # self.cache_dir = CACHE_DIR + self.export_source + '/' + # self.schedule_file = self.cache_dir + 'schedule' + # + # src = self.cache_dir + str(pkey) + '/list.lsp' + # dst = self.cache_dir + 'current.lsp' + # + # print src + # print dst + # + # print '*************' + # print ptype + # print '*************' + # + # if True == os.access(src, os.R_OK): + # try: + # shutil.copy2(src, dst) + # logger.debug('copy %s to %s', src, dst) + # """ + # i know this could be wrapped, maybe later.. + # """ + # tn = telnetlib.Telnet(LS_HOST, 1234) + # tn.write("\n") + # tn.write("live_in.stop\n") + # tn.write("stream_disable\n") + # time.sleep(0.2) + # tn.write("\n") + # #tn.write("reload_current\n") + # tn.write("current.reload\n") + # time.sleep(0.2) + # tn.write("skip_current\n") + # + # if(int(ptype) == 6): + # """ + # Couchcaster comming. Stop/Start live input to have ls re-read it's playlist + # """ + # print 'Couchcaster - switching to stream' + # tn.write("live_in.start\n") + # time.sleep(0.2) + # tn.write("stream_enable\n") + # + # if(int(ptype) == 7): + # """ + # Recast comming. Start the live input + # """ + # print 'Recast - switching to stream' + # tn.write("live_in.start\n") + # time.sleep(0.2) + # tn.write("stream_enable\n") + # + # """ + # Pass some extra information to liquidsoap + # """ + # tn.write("pl.pl_id '%s'\n" % p_id) + # tn.write("pl.user_id '%s'\n" % user_id) + # tn.write("exit\n") + # + # print tn.read_all() + # + # status = 1 + # + # except Exception, e: + # logger.error('%s', e) + # status = 0 + # else: + # status = 0 + # + # return status """ diff --git a/3rd_party/pypo/scripts/silence.mp3 b/3rd_party/pypo/scripts/silence.mp3 index bb955c331..374dfe286 100644 Binary files a/3rd_party/pypo/scripts/silence.mp3 and b/3rd_party/pypo/scripts/silence.mp3 differ diff --git a/backend/Schedule.php b/backend/Schedule.php index 2d64e7281..9ee043684 100644 --- a/backend/Schedule.php +++ b/backend/Schedule.php @@ -461,6 +461,10 @@ class Schedule { $playlists[$pkey]['duration'] = $dx['clip_length']; $playlists[$pkey]['played'] = '0'; $playlists[$pkey]['schedule_id'] = $dx['group_id']; + $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"]); } } @@ -487,7 +491,8 @@ class Schedule { 'fade_out' => Schedule::WallTimeToMillisecs($item["fade_out"]), 'fade_cross' => 0, 'cue_in' => Schedule::WallTimeToMillisecs($item["cue_in"]), - 'cue_out' => $cueOut + 'cue_out' => $cueOut, + 'export_source' => 'scheduler' ); } $playlist['medias'] = $medias;