diff --git a/python_apps/media-monitor2/media/monitor/eventcontractor.py b/python_apps/media-monitor2/media/monitor/eventcontractor.py index 77a5045fb..7e444b3ea 100644 --- a/python_apps/media-monitor2/media/monitor/eventcontractor.py +++ b/python_apps/media-monitor2/media/monitor/eventcontractor.py @@ -59,4 +59,6 @@ class EventContractor(Loggable): try: del self.store[evt.path] except Exception as e: self.unexpected_exception(e) - self.logger.info("Unregistering. Left: '%d'" % len(self.store.keys())) + # the next line is commented out because it clutters up logging real + # bad + #self.logger.info("Unregistering. Left: '%d'" % len(self.store.keys())) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index a8c982b9a..5f222c4b4 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -105,10 +105,38 @@ class Metadata(Loggable): # little bit messy. Some of the handling is in m.m.pure while the rest is # here. Also interface is not very consistent + @staticmethod + def airtime_dict(d): + """ + Converts mutagen dictionary 'd' into airtime dictionary + """ + temp_dict = {} + for m_key, m_val in d.iteritems(): + # TODO : some files have multiple fields for the same metadata. + # genre is one example. In that case mutagen will return a list + # of values + assign_val = m_val[0] if isinstance(m_val, list) else m_val + temp_dict[ m_key ] = assign_val + airtime_dictionary = {} + for muta_k, muta_v in temp_dict.iteritems(): + # We must check if we can actually translate the mutagen key into + # an airtime key before doing the conversion + if muta_k in mutagen2airtime: + airtime_key = mutagen2airtime[muta_k] + # Apply truncation in the case where airtime_key is in our + # truncation table + muta_v = \ + truncate_to_length(muta_v, truncate_table[airtime_key])\ + if airtime_key in truncate_table else muta_v + airtime_dictionary[ airtime_key ] = muta_v + return airtime_dictionary + @staticmethod def write_unsafe(path,md): """ - Writes 'md' metadata into 'path' through mutagen + Writes 'md' metadata into 'path' through mutagen. Converts all + dictionary values to strings because mutagen will not write anything + else """ if not os.path.exists(path): raise BadSongFile(path) song_file = mutagen.File(path, easy=True) @@ -128,37 +156,13 @@ class Metadata(Loggable): self.path = fpath # TODO : Simplify the way all of these rules are handled right not it's # extremely unclear and needs to be refactored. - metadata = {} - # Load only the metadata avilable in mutagen into metdata - for k,v in full_mutagen.iteritems(): - # Special handling of attributes here - if isinstance(v, list): - # TODO : some files have multiple fields for the same metadata. - # genre is one example. In that case mutagen will return a list - # of values - metadata[k] = v[0] - #if len(v) == 1: metadata[k] = v[0] - #else: raise Exception("Unknown mutagen %s:%s" % (k,str(v))) - else: metadata[k] = v - self.__metadata = {} - # Start populating a dictionary of airtime metadata in __metadata - for muta_k, muta_v in metadata.iteritems(): - # We must check if we can actually translate the mutagen key into - # an airtime key before doing the conversion - if muta_k in mutagen2airtime: - airtime_key = mutagen2airtime[muta_k] - # Apply truncation in the case where airtime_key is in our - # truncation table - muta_v = \ - truncate_to_length(muta_v, truncate_table[airtime_key])\ - if airtime_key in truncate_table else muta_v - self.__metadata[ airtime_key ] = muta_v + self.__metadata = Metadata.airtime_dict(full_mutagen) # Now we extra the special values that are calculated from the mutagen # object itself: for special_key,f in airtime_special.iteritems(): new_val = f(full_mutagen) if new_val is not None: - self.__metadata[special_key] = f(full_mutagen) + self.__metadata[special_key] = new_val # Finally, we "normalize" all the metadata here: self.__metadata = mmp.normalized_metadata(self.__metadata, fpath) # Now we must load the md5: diff --git a/python_apps/media-monitor2/media/monitor/pure.py b/python_apps/media-monitor2/media/monitor/pure.py index 3f0b8bbe4..4d768b0f8 100644 --- a/python_apps/media-monitor2/media/monitor/pure.py +++ b/python_apps/media-monitor2/media/monitor/pure.py @@ -2,6 +2,7 @@ import copy import os import shutil +import re import sys import hashlib import locale @@ -228,34 +229,33 @@ def normalized_metadata(md, original_path): # Specific rules that are applied in a per attribute basis format_rules = { 'MDATA_KEY_TRACKNUMBER' : parse_int, - 'MDATA_KEY_BITRATE' : lambda x: str(int(x) / 1000) + "kbps", 'MDATA_KEY_FILEPATH' : lambda x: os.path.normpath(x), 'MDATA_KEY_MIME' : lambda x: x.replace('-','/'), 'MDATA_KEY_BPM' : lambda x: x[0:8], } - new_md = remove_whitespace(new_md) + new_md = remove_whitespace(new_md) # remove whitespace fields + # Format all the fields in format_rules new_md = apply_rules_dict(new_md, format_rules) - new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_TITLE'], - default=no_extension_basename(original_path)) - new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_CREATOR', - 'MDATA_KEY_SOURCE'], default=u'') + # set filetype to audioclip by default new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_FTYPE'], default=u'audioclip') - # In the case where the creator is 'Airtime Show Recorder' we would like to - # format the MDATA_KEY_TITLE slightly differently - # Note: I don't know why I'm doing a unicode string comparison here - # that part is copied from the original code + + # Try to parse bpm but delete the whole key if that fails + if 'MDATA_KEY_BPM' in new_md: + new_md['MDATA_KEY_BPM'] = parse_int(new_md['MDATA_KEY_BPM']) + if new_md['MDATA_KEY_BPM'] is None: + del new_md['MDATA_KEY_BPM'] + if is_airtime_recorded(new_md): - hour,minute,second,name = md['MDATA_KEY_TITLE'].split("-",3) - # We assume that MDATA_KEY_YEAR is always given for airtime recorded - # shows + hour,minute,second,name = new_md['MDATA_KEY_TITLE'].split("-",3) new_md['MDATA_KEY_TITLE'] = u'%s-%s-%s:%s:%s' % \ (name, new_md['MDATA_KEY_YEAR'], hour, minute, second) - # IMPORTANT: in the original code. MDATA_KEY_FILEPATH would also - # be set to the original path of the file for airtime recorded shows - # (before it was "organized"). We will skip this procedure for now - # because it's not clear why it was done + else: + # Read title from filename if it does not exist + new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_TITLE'], + default=no_extension_basename(original_path)) + return new_md def organized_path(old_path, root_path, orig_md): @@ -275,14 +275,21 @@ def organized_path(old_path, root_path, orig_md): else: return True # We set some metadata elements to a default "unknown" value because we use # these fields to create a path hence they cannot be empty + # Here "normal" means normalized only for organized path normal_md = default_to_f(orig_md, path_md, unicode_unknown, default_f) + if normal_md['MDATA_KEY_BITRATE']: + formatted = str(int(normal_md['MDATA_KEY_BITRATE']) / 1000) + normal_md['MDATA_KEY_BITRATE'] = formatted + 'kbps' + else: normal_md['MDATA_KEY_BITRATE'] = unicode_unknown if is_airtime_recorded(normal_md): - fname = u'%s-%s-%s.%s' % ( normal_md['MDATA_KEY_YEAR'], - normal_md['MDATA_KEY_TITLE'], - normal_md['MDATA_KEY_BITRATE'], ext ) - yyyy, mm, _ = normal_md['MDATA_KEY_YEAR'].split('-',3) - path = os.path.join(root_path, yyyy, mm) - filepath = os.path.join(path,fname) + title_re = re.match("(?P\w+)-(?P\d+-\d+-\d+-\d+:\d+:\d+)$", + normal_md['MDATA_KEY_TITLE']) + show_name, = title_re.group('show'), + date = title_re.group('date').replace(':','-') + yyyy, mm, _ = normal_md['MDATA_KEY_YEAR'].split('-',2) + fname_base = '%s-%s-%s.%s' % \ + (date, show_name, normal_md['MDATA_KEY_BITRATE'], ext) + filepath = os.path.join(root_path, yyyy, mm, fname_base) elif len(normal_md['MDATA_KEY_TRACKNUMBER']) == 0: fname = u'%s-%s.%s' % (normal_md['MDATA_KEY_TITLE'], normal_md['MDATA_KEY_BITRATE'], ext) diff --git a/python_apps/media-monitor2/tests/test_pure.py b/python_apps/media-monitor2/tests/test_pure.py index 611c42f60..e822f3cf5 100644 --- a/python_apps/media-monitor2/tests/test_pure.py +++ b/python_apps/media-monitor2/tests/test_pure.py @@ -2,6 +2,7 @@ import unittest import os import media.monitor.pure as mmp +from media.monitor.metadata import Metadata class TestMMP(unittest.TestCase): def setUp(self): @@ -34,23 +35,32 @@ class TestMMP(unittest.TestCase): for k in def_keys: self.assertEqual( sd[k], 'DEF' ) def test_normalized_metadata(self): - normal = mmp.normalized_metadata(self.md1,"") - self.assertTrue(hasattr(normal['MDATA_KEY_CREATOR'],'startswith')) - self.assertTrue('MDATA_KEY_CREATOR' in normal) - self.assertTrue('MDATA_KEY_SOURCE' in normal) + # Recorded show test first + orig = Metadata.airtime_dict({ + 'date': [u'2012-08-21'], + 'tracknumber': [u'2'], + 'title': [u'11-29-00-record'], + 'artist': [u'Airtime Show Recorder'] + }) + orga = Metadata.airtime_dict({ + 'date': [u'2012-08-21'], + 'tracknumber': [u'2'], + 'artist': [u'Airtime Show Recorder'], + 'title': [u'record-2012-08-21-11:29:00'] + }) + orga['MDATA_KEY_FTYPE'] = u'audioclip' + orig['MDATA_KEY_BITRATE'] = u'256000' + orga['MDATA_KEY_BITRATE'] = u'256kbps' - def test_organized_path(self): - o_path = '/home/rudi/throwaway/ACDC_-_Back_In_Black-sample-64kbps.ogg' - normal = mmp.normalized_metadata(self.md1,o_path) - og = mmp.organized_path(o_path, - '/home/rudi/throwaway/fucking_around/watch/', - normal) - real_path1 = \ - u'/home/rudi/throwaway/fucking_around/watch/unknown/unknown/ACDC_-_Back_In_Black-sample-64kbps-64kbps.ogg' - self.assertTrue( 'unknown' in og, True ) - self.assertEqual( og, real_path1 ) # TODO : fix this failure - # for recorded it should be something like this - # ./recorded/2012/07/2012-07-09-17-55-00-Untitled Show-256kbps.ogg + old_path = "/home/rudi/recorded/2012-08-21-11:29:00.ogg" + normalized = mmp.normalized_metadata(orig, old_path) + print(normalized) + self.assertEqual( orga, normalized ) + + organized_base_name = "2012-08-21-11-29-00-record-256kbps.ogg" + base = "/srv/airtime/stor/" + organized_path = mmp.organized_path(old_path,base, normalized) + self.assertEqual(os.path.basename(organized_path), organized_base_name) def test_file_md5(self): p = os.path.realpath(__file__) diff --git a/python_apps/pypo/install/pypo-initialize.py b/python_apps/pypo/install/pypo-initialize.py index b2a36d96e..ebf49a65c 100644 --- a/python_apps/pypo/install/pypo-initialize.py +++ b/python_apps/pypo/install/pypo-initialize.py @@ -94,7 +94,6 @@ try: #liq_path DNE, which is OK. pass - os.symlink(liq_path, symlink_path) else: print " * Liquidsoap binary not found!" diff --git a/python_apps/pypo/recorder.py b/python_apps/pypo/recorder.py index 00d51c010..de48afd1c 100644 --- a/python_apps/pypo/recorder.py +++ b/python_apps/pypo/recorder.py @@ -73,16 +73,18 @@ class ShowRecorder(Thread): else: filetype = "ogg"; - filepath = "%s%s.%s" % (config["base_recorded_files"], filename, filetype) + joined_path = os.path.join(config["base_recorded_files"], filename) + filepath = "%s.%s" % (joined_path, filetype) br = config["record_bitrate"] sr = config["record_samplerate"] - c = config["record_channels"] + c = config["record_channels"] ss = config["record_sample_size"] #-f:16,2,44100 #-b:256 - command = "ecasound -f:%s,%s,%s -i alsa -o %s,%s000 -t:%s" % (ss, c, sr, filepath, br, length) + command = "ecasound -f:%s,%s,%s -i alsa -o %s,%s000 -t:%s" % \ + (ss, c, sr, filepath, br, length) args = command.split(" ") self.logger.info("starting record")