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

This commit is contained in:
denise 2012-08-21 14:08:36 -04:00
commit f52cb81c64
6 changed files with 95 additions and 71 deletions

View File

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

View File

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

View File

@ -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<show>\w+)-(?P<date>\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)

View File

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

View File

@ -94,7 +94,6 @@ try:
#liq_path DNE, which is OK.
pass
os.symlink(liq_path, symlink_path)
else:
print " * Liquidsoap binary not found!"

View File

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