Merge branch 'devel' of dev.sourcefabric.org:airtime into devel

This commit is contained in:
denise 2012-08-17 15:56:27 -04:00
commit 86ee2966e6
5 changed files with 41 additions and 28 deletions

View File

@ -121,10 +121,10 @@ class Metadata(Loggable):
def __init__(self, fpath): def __init__(self, fpath):
# Forcing the unicode through # Forcing the unicode through
try: fpath = fpath.decode("utf-8") try : fpath = fpath.decode("utf-8")
except: pass except : pass
try: full_mutagen = mutagen.File(fpath, easy=True) try : full_mutagen = mutagen.File(fpath, easy=True)
except Exception: raise BadSongFile(fpath) except Exception : raise BadSongFile(fpath)
self.path = fpath self.path = fpath
# TODO : Simplify the way all of these rules are handled right not it's # TODO : Simplify the way all of these rules are handled right not it's
# extremely unclear and needs to be refactored. # extremely unclear and needs to be refactored.
@ -162,6 +162,8 @@ class Metadata(Loggable):
# Finally, we "normalize" all the metadata here: # Finally, we "normalize" all the metadata here:
self.__metadata = mmp.normalized_metadata(self.__metadata, fpath) self.__metadata = mmp.normalized_metadata(self.__metadata, fpath)
# Now we must load the md5: # Now we must load the md5:
# TODO : perhaps we shouldn't hard code how many bytes we're reading
# from the file?
self.__metadata['MDATA_KEY_MD5'] = mmp.file_md5(fpath,max_length=100) self.__metadata['MDATA_KEY_MD5'] = mmp.file_md5(fpath,max_length=100)
def is_recorded(self): def is_recorded(self):

View File

@ -163,11 +163,13 @@ def apply_rules_dict(d, rules):
""" """
Consumes a dictionary of rules that maps some keys to lambdas which it Consumes a dictionary of rules that maps some keys to lambdas which it
applies to every matching element in d and returns a new dictionary with applies to every matching element in d and returns a new dictionary with
the rules applied the rules applied. If a rule returns none then it's not applied
""" """
new_d = copy.deepcopy(d) new_d = copy.deepcopy(d)
for k, rule in rules.iteritems(): for k, rule in rules.iteritems():
if k in d: new_d[k] = rule(d[k]) if k in d:
new_val = rule(d[k])
if new_val is not None: new_d[k] = new_val
return new_d return new_d
def default_to_f(dictionary, keys, default, condition): def default_to_f(dictionary, keys, default, condition):
@ -183,10 +185,6 @@ def default_to(dictionary, keys, default):
""" """
cnd = lambda dictionary, key: key not in dictionary cnd = lambda dictionary, key: key not in dictionary
return default_to_f(dictionary, keys, default, cnd) return default_to_f(dictionary, keys, default, cnd)
#new_d = copy.deepcopy(dictionary)
#for k in keys:
#if not (k in new_d): new_d[k] = default
#return new_d
def remove_whitespace(dictionary): def remove_whitespace(dictionary):
""" """
@ -205,18 +203,18 @@ def remove_whitespace(dictionary):
def parse_int(s): def parse_int(s):
""" """
Tries very hard to get some sort of integer result from s. Defaults to 0 Tries very hard to get some sort of integer result from s. Defaults to 0
when it failes when it fails
>>> parse_int("123") >>> parse_int("123")
'123' '123'
>>> parse_int("123saf") >>> parse_int("123saf")
'123' '123'
>>> parse_int("asdf") >>> parse_int("asdf")
'' None
""" """
if s.isdigit(): return s if s.isdigit(): return s
else: else:
try : return str(reduce(op.add, takewhile(lambda x: x.isdigit(), s))) try : return str(reduce(op.add, takewhile(lambda x: x.isdigit(), s)))
except: return '' except: return None
def normalized_metadata(md, original_path): def normalized_metadata(md, original_path):
""" """
@ -240,7 +238,8 @@ def normalized_metadata(md, original_path):
new_md = apply_rules_dict(new_md, format_rules) new_md = apply_rules_dict(new_md, format_rules)
new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_TITLE'], new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_TITLE'],
default=no_extension_basename(original_path)) default=no_extension_basename(original_path))
new_md = default_to(dictionary=new_md, keys=path_md, default=u'') new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_CREATOR',
'MDATA_KEY_SOURCE'], default=u'')
new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_FTYPE'], new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_FTYPE'],
default=u'audioclip') default=u'audioclip')
# In the case where the creator is 'Airtime Show Recorder' we would like to # In the case where the creator is 'Airtime Show Recorder' we would like to

View File

@ -48,7 +48,7 @@ class TestMMP(unittest.TestCase):
real_path1 = \ real_path1 = \
u'/home/rudi/throwaway/fucking_around/watch/unknown/unknown/ACDC_-_Back_In_Black-sample-64kbps-64kbps.ogg' u'/home/rudi/throwaway/fucking_around/watch/unknown/unknown/ACDC_-_Back_In_Black-sample-64kbps-64kbps.ogg'
self.assertTrue( 'unknown' in og, True ) self.assertTrue( 'unknown' in og, True )
self.assertEqual( og, real_path1 ) self.assertEqual( og, real_path1 ) # TODO : fix this failure
# for recorded it should be something like this # for recorded it should be something like this
# ./recorded/2012/07/2012-07-09-17-55-00-Untitled Show-256kbps.ogg # ./recorded/2012/07/2012-07-09-17-55-00-Untitled Show-256kbps.ogg
@ -70,6 +70,6 @@ class TestMMP(unittest.TestCase):
def test_parse_int(self): def test_parse_int(self):
self.assertEqual( mmp.parse_int("123"), "123" ) self.assertEqual( mmp.parse_int("123"), "123" )
self.assertEqual( mmp.parse_int("123asf"), "123" ) self.assertEqual( mmp.parse_int("123asf"), "123" )
self.assertEqual( mmp.parse_int("asdf"), "" ) self.assertEqual( mmp.parse_int("asdf"), None )
if __name__ == '__main__': unittest.main() if __name__ == '__main__': unittest.main()

View File

@ -58,7 +58,7 @@ class PypoFetch(Thread):
if not os.path.isdir(dir): if not os.path.isdir(dir):
""" """
We get here if path does not exist, or path does exist but We get here if path does not exist, or path does exist but
is a file. We are not handling the second case, but don't is a file. We are not handling the second case, but don't
think we actually care about handling it. think we actually care about handling it.
""" """
self.logger.debug("Cache dir does not exist. Creating...") self.logger.debug("Cache dir does not exist. Creating...")
@ -426,16 +426,19 @@ class PypoFetch(Thread):
for key in media: for key in media:
media_item = media[key] media_item = media[key]
""" """
{u'end': u'2012-07-26-04-05-00', u'fade_out': 500, u'show_name': u'Untitled Show', u'uri': u'http://', {u'end': u'2012-07-26-04-05-00', u'fade_out': 500, u'show_name': u'Untitled Show', u'uri': u'http://',
u'cue_in': 0, u'start': u'2012-07-26-04-00-00', u'replay_gain': u'0', u'row_id': 16, u'cue_out': 300, u'type': u'cue_in': 0, u'start': u'2012-07-26-04-00-00', u'replay_gain': u'0', u'row_id': 16, u'cue_out': 300, u'type':
u'stream', u'id': 1, u'fade_in': 500} u'stream', u'id': 1, u'fade_in': 500}
""" """
if(media_item['type'] == 'file'): if (media_item['type'] == 'file'):
fileExt = os.path.splitext(media_item['uri'])[1] fileExt = os.path.splitext(media_item['uri'])[1]
dst = os.path.join(download_dir, unicode(media_item['id']) + fileExt) dst = os.path.join(download_dir, unicode(media_item['id']) + fileExt)
media_item['dst'] = dst media_item['dst'] = dst
media_item['file_ready'] = False media_item['file_ready'] = False
media_filtered[key] = media_item media_filtered[key] = media_item
elif media_item['type'] == 'stream':
#flag to indicate whether the stream started prebuffering
media_item['prebuffer_started'] = False
self.media_prepare_queue.put(copy.copy(media_filtered)) self.media_prepare_queue.put(copy.copy(media_filtered))
except Exception, e: self.logger.error("%s", e) except Exception, e: self.logger.error("%s", e)
@ -453,7 +456,7 @@ class PypoFetch(Thread):
""" """
Get list of all files in the cache dir and remove them if they aren't being used anymore. Get list of all files in the cache dir and remove them if they aren't being used anymore.
Input dict() media, lists all files that are scheduled or currently playing. Not being in this Input dict() media, lists all files that are scheduled or currently playing. Not being in this
dict() means the file is safe to remove. dict() means the file is safe to remove.
""" """
cached_file_set = set(os.listdir(self.cache_dir)) cached_file_set = set(os.listdir(self.cache_dir))
scheduled_file_set = set() scheduled_file_set = set()
@ -476,7 +479,7 @@ class PypoFetch(Thread):
def main(self): def main(self):
# Bootstrap: since we are just starting up, we need to grab the # Bootstrap: since we are just starting up, we need to grab the
# most recent schedule. After that we can just wait for updates. # most recent schedule. After that we can just wait for updates.
success, self.schedule_data = self.api_client.get_schedule() success, self.schedule_data = self.api_client.get_schedule()
if success: if success:
self.logger.info("Bootstrap schedule received: %s", self.schedule_data) self.logger.info("Bootstrap schedule received: %s", self.schedule_data)
@ -490,14 +493,14 @@ class PypoFetch(Thread):
""" """
our simple_queue.get() requires a timeout, in which case we our simple_queue.get() requires a timeout, in which case we
fetch the Airtime schedule manually. It is important to fetch fetch the Airtime schedule manually. It is important to fetch
the schedule periodically because if we didn't, we would only the schedule periodically because if we didn't, we would only
get schedule updates via RabbitMq if the user was constantly get schedule updates via RabbitMq if the user was constantly
using the Airtime interface. using the Airtime interface.
If the user is not using the interface, RabbitMq messages are not If the user is not using the interface, RabbitMq messages are not
sent, and we will have very stale (or non-existent!) data about the sent, and we will have very stale (or non-existent!) data about the
schedule. schedule.
Currently we are checking every POLL_INTERVAL seconds Currently we are checking every POLL_INTERVAL seconds
""" """

View File

@ -389,12 +389,17 @@ class PypoPush(Thread):
elif media_item['type'] == 'stream_buffer_start': elif media_item['type'] == 'stream_buffer_start':
self.start_web_stream_buffer(media_item) self.start_web_stream_buffer(media_item)
elif media_item['type'] == "stream": elif media_item['type'] == "stream":
if not media_item['prebuffer_started']:
#this is called if the stream wasn't scheduled sufficiently ahead of time
#so that the prebuffering stage could take effect. Let's do the prebuffering now.
self.start_web_stream_buffer(media_item)
self.start_web_stream(media_item) self.start_web_stream(media_item)
elif media_item['type'] == "stream_end": elif media_item['type'] == "stream_end":
self.stop_web_stream(media_item) self.stop_web_stream(media_item)
except Exception, e: except Exception, e:
self.logger.error('Pypo Push Exception: %s', e) self.logger.error('Pypo Push Exception: %s', e)
def start_web_stream_buffer(self, media_item): def start_web_stream_buffer(self, media_item):
try: try:
self.telnet_lock.acquire() self.telnet_lock.acquire()
@ -410,6 +415,8 @@ class PypoPush(Thread):
tn.write("exit\n") tn.write("exit\n")
self.logger.debug(tn.read_all()) self.logger.debug(tn.read_all())
media_item['prebuffer_started'] = True
except Exception, e: except Exception, e:
self.logger.error(str(e)) self.logger.error(str(e))
finally: finally:
@ -425,6 +432,8 @@ class PypoPush(Thread):
msg = 'streams.scheduled_play_start\n' msg = 'streams.scheduled_play_start\n'
tn.write(msg) tn.write(msg)
msg = 'dynamic_source.output_start\n' msg = 'dynamic_source.output_start\n'
self.logger.debug(msg) self.logger.debug(msg)
tn.write(msg) tn.write(msg)