Added free audio samples from OpSound, removed old audio samples referring

to LiveSupport.  Added License information for the audio samples.

Added a working silence file.

Pypo:
Changed the Campcaster API client to ignore playlists that have already past.

Updated "cleanup()" function with Jonas fixes.

Cleanup up a lot of print statements, converted to debug statements.

Added missing data from the Campcaster JSON export.
This commit is contained in:
paul.baranowski 2010-11-24 17:57:55 -05:00
parent 8e7e0226e6
commit 10caa36c70
6 changed files with 257 additions and 190 deletions

30
3rd_party/php/Zend/LICENSE vendored Normal file
View File

@ -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.

View File

@ -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

View File

@ -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 #

View File

@ -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
"""

Binary file not shown.

View File

@ -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;