Merge branch 'devel' of dev.sourcefabric.org:airtime into devel
This commit is contained in:
commit
899c94eb67
8 changed files with 52 additions and 38 deletions
|
@ -8,8 +8,7 @@ import media.monitor.pure as mmp
|
||||||
|
|
||||||
class MMConfig(object):
|
class MMConfig(object):
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path): raise NoConfigFile(path)
|
||||||
raise NoConfigFile(path)
|
|
||||||
self.cfg = ConfigObj(path)
|
self.cfg = ConfigObj(path)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
|
@ -29,6 +28,10 @@ class MMConfig(object):
|
||||||
def save(self): self.cfg.write()
|
def save(self): self.cfg.write()
|
||||||
|
|
||||||
def last_ran(self):
|
def last_ran(self):
|
||||||
|
"""
|
||||||
|
Returns the last time media monitor was ran by looking at the time when
|
||||||
|
the file at 'index_path' was modified
|
||||||
|
"""
|
||||||
return mmp.last_modified(self.cfg['index_path'])
|
return mmp.last_modified(self.cfg['index_path'])
|
||||||
|
|
||||||
# Remove this after debugging...
|
# Remove this after debugging...
|
||||||
|
|
|
@ -46,7 +46,6 @@ class EventContractor(Loggable):
|
||||||
self.store[ evt.path ] = evt
|
self.store[ evt.path ] = evt
|
||||||
return True # We actually added something, hence we return true.
|
return True # We actually added something, hence we return true.
|
||||||
|
|
||||||
# events are unregistered automatically no need to screw around with them
|
|
||||||
def __unregister(self, evt):
|
def __unregister(self, evt):
|
||||||
try: del self.store[evt.path]
|
try: del self.store[evt.path]
|
||||||
except Exception as e: self.unexpected_exception(e)
|
except Exception as e: self.unexpected_exception(e)
|
||||||
|
|
|
@ -110,8 +110,7 @@ class Metadata(Loggable):
|
||||||
"""
|
"""
|
||||||
Writes 'md' metadata into 'path' through mutagen
|
Writes 'md' metadata into 'path' through mutagen
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path): raise BadSongFile(path)
|
||||||
raise BadSongFile(path)
|
|
||||||
song_file = mutagen.File(path, easy=True)
|
song_file = mutagen.File(path, easy=True)
|
||||||
for airtime_k, airtime_v in md.iteritems():
|
for airtime_k, airtime_v in md.iteritems():
|
||||||
if airtime_k in airtime2mutagen:
|
if airtime_k in airtime2mutagen:
|
||||||
|
@ -120,7 +119,6 @@ class Metadata(Loggable):
|
||||||
song_file[ airtime2mutagen[airtime_k] ] = unicode(airtime_v)
|
song_file[ airtime2mutagen[airtime_k] ] = unicode(airtime_v)
|
||||||
song_file.save()
|
song_file.save()
|
||||||
|
|
||||||
|
|
||||||
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")
|
||||||
|
|
|
@ -17,6 +17,9 @@ supported_extensions = [u"mp3", u"ogg", u"oga"]
|
||||||
#supported_extensions = [u"mp3", u"ogg", u"oga", u"flac", u"aac", u"bwf"]
|
#supported_extensions = [u"mp3", u"ogg", u"oga", u"flac", u"aac", u"bwf"]
|
||||||
unicode_unknown = u'unknown'
|
unicode_unknown = u'unknown'
|
||||||
|
|
||||||
|
path_md = ['MDATA_KEY_TITLE', 'MDATA_KEY_CREATOR', 'MDATA_KEY_SOURCE',
|
||||||
|
'MDATA_KEY_TRACKNUMBER', 'MDATA_KEY_BITRATE']
|
||||||
|
|
||||||
class LazyProperty(object):
|
class LazyProperty(object):
|
||||||
"""
|
"""
|
||||||
meant to be used for lazy evaluation of an object attribute.
|
meant to be used for lazy evaluation of an object attribute.
|
||||||
|
@ -163,15 +166,23 @@ def apply_rules_dict(d, rules):
|
||||||
if k in d: new_d[k] = rule(d[k])
|
if k in d: new_d[k] = rule(d[k])
|
||||||
return new_d
|
return new_d
|
||||||
|
|
||||||
|
def default_to_f(dictionary, keys, default, condition):
|
||||||
|
new_d = copy.deepcopy(dictionary)
|
||||||
|
for k in keys:
|
||||||
|
if condition(dictionary=new_d, key=k): new_d[k] = default
|
||||||
|
return new_d
|
||||||
|
|
||||||
def default_to(dictionary, keys, default):
|
def default_to(dictionary, keys, default):
|
||||||
"""
|
"""
|
||||||
Checks if the list of keys 'keys' exists in 'dictionary'. If not then it
|
Checks if the list of keys 'keys' exists in 'dictionary'. If not then it
|
||||||
returns a new dictionary with all those missing keys defaults to 'default'
|
returns a new dictionary with all those missing keys defaults to 'default'
|
||||||
"""
|
"""
|
||||||
new_d = copy.deepcopy(dictionary)
|
cnd = lambda dictionary, key: key not in dictionary
|
||||||
for k in keys:
|
return default_to_f(dictionary, keys, default, cnd)
|
||||||
if not (k in new_d): new_d[k] = default
|
#new_d = copy.deepcopy(dictionary)
|
||||||
return new_d
|
#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):
|
||||||
"""
|
"""
|
||||||
|
@ -223,8 +234,6 @@ def normalized_metadata(md, original_path):
|
||||||
'MDATA_KEY_MIME' : lambda x: x.replace('-','/'),
|
'MDATA_KEY_MIME' : lambda x: x.replace('-','/'),
|
||||||
'MDATA_KEY_BPM' : lambda x: x[0:8],
|
'MDATA_KEY_BPM' : lambda x: x[0:8],
|
||||||
}
|
}
|
||||||
path_md = ['MDATA_KEY_TITLE', 'MDATA_KEY_CREATOR', 'MDATA_KEY_SOURCE',
|
|
||||||
'MDATA_KEY_TRACKNUMBER', 'MDATA_KEY_BITRATE']
|
|
||||||
# note that we could have saved a bit of code by rewriting new_md using
|
# note that we could have saved a bit of code by rewriting new_md using
|
||||||
# defaultdict(lambda x: "unknown"). But it seems to be too implicit and
|
# defaultdict(lambda x: "unknown"). But it seems to be too implicit and
|
||||||
# could possibly lead to subtle bugs down the road. Plus the following
|
# could possibly lead to subtle bugs down the road. Plus the following
|
||||||
|
@ -233,8 +242,9 @@ 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 = remove_whitespace(new_md)
|
||||||
new_md = default_to(dictionary=new_md, keys=path_md,
|
new_md = default_to(dictionary=new_md, keys=path_md,
|
||||||
default=unicode_unknown)
|
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
|
||||||
|
@ -251,13 +261,13 @@ def normalized_metadata(md, original_path):
|
||||||
# be set to the original path of the file for airtime recorded shows
|
# be set to the original path of the file for airtime recorded shows
|
||||||
# (before it was "organized"). We will skip this procedure for now
|
# (before it was "organized"). We will skip this procedure for now
|
||||||
# because it's not clear why it was done
|
# because it's not clear why it was done
|
||||||
return remove_whitespace(new_md)
|
return new_md
|
||||||
|
|
||||||
def organized_path(old_path, root_path, normal_md):
|
def organized_path(old_path, root_path, orig_md):
|
||||||
"""
|
"""
|
||||||
old_path - path where file is store at the moment <= maybe not necessary?
|
old_path - path where file is store at the moment <= maybe not necessary?
|
||||||
root_path - the parent directory where all organized files go
|
root_path - the parent directory where all organized files go
|
||||||
normal_md - original meta data of the file as given by mutagen AFTER being
|
orig_md - original meta data of the file as given by mutagen AFTER being
|
||||||
normalized
|
normalized
|
||||||
return value: new file path
|
return value: new file path
|
||||||
"""
|
"""
|
||||||
|
@ -265,6 +275,8 @@ def organized_path(old_path, root_path, normal_md):
|
||||||
ext = extension(old_path)
|
ext = extension(old_path)
|
||||||
# The blocks for each if statement look awfully similar. Perhaps there is a
|
# The blocks for each if statement look awfully similar. Perhaps there is a
|
||||||
# way to simplify this code
|
# way to simplify this code
|
||||||
|
normal_md = default_to_f(orig_md, path_md, unicode_unknown,
|
||||||
|
lambda dictionary, key: len(dictionary[key]) == 0)
|
||||||
if is_airtime_recorded(normal_md):
|
if is_airtime_recorded(normal_md):
|
||||||
fname = u'%s-%s-%s.%s' % ( normal_md['MDATA_KEY_YEAR'],
|
fname = u'%s-%s-%s.%s' % ( normal_md['MDATA_KEY_YEAR'],
|
||||||
normal_md['MDATA_KEY_TITLE'],
|
normal_md['MDATA_KEY_TITLE'],
|
||||||
|
@ -321,8 +333,7 @@ def get_system_locale(locale_path='/etc/default/locale'):
|
||||||
try:
|
try:
|
||||||
config = ConfigObj(locale_path)
|
config = ConfigObj(locale_path)
|
||||||
return config
|
return config
|
||||||
except Exception as e:
|
except Exception as e: raise FailedToSetLocale(locale_path,cause=e)
|
||||||
raise FailedToSetLocale(locale_path,cause=e)
|
|
||||||
else: raise ValueError("locale path '%s' does not exist. \
|
else: raise ValueError("locale path '%s' does not exist. \
|
||||||
permissions issue?" % locale_path)
|
permissions issue?" % locale_path)
|
||||||
|
|
||||||
|
@ -336,8 +347,7 @@ def configure_locale(config):
|
||||||
if default_locale[1] is None:
|
if default_locale[1] is None:
|
||||||
lang = config.get('LANG')
|
lang = config.get('LANG')
|
||||||
new_locale = lang
|
new_locale = lang
|
||||||
else:
|
else: new_locale = default_locale
|
||||||
new_locale = default_locale
|
|
||||||
locale.setlocale(locale.LC_ALL, new_locale)
|
locale.setlocale(locale.LC_ALL, new_locale)
|
||||||
reload(sys)
|
reload(sys)
|
||||||
sys.setdefaultencoding("UTF-8")
|
sys.setdefaultencoding("UTF-8")
|
||||||
|
@ -394,7 +404,7 @@ def sub_path(directory,f):
|
||||||
the paths.
|
the paths.
|
||||||
"""
|
"""
|
||||||
normalized = normpath(directory)
|
normalized = normpath(directory)
|
||||||
common = os.path.commonprefix([ directory, normpath(f) ])
|
common = os.path.commonprefix([ normalized, normpath(f) ])
|
||||||
return common == normalized
|
return common == normalized
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import copy
|
import copy
|
||||||
import traceback
|
|
||||||
|
|
||||||
from media.monitor.handler import ReportHandler
|
from media.monitor.handler import ReportHandler
|
||||||
from media.monitor.log import Loggable
|
from media.monitor.log import Loggable
|
||||||
|
@ -47,9 +46,7 @@ class RequestSync(threading.Thread,Loggable):
|
||||||
except BadSongFile as e:
|
except BadSongFile as e:
|
||||||
self.logger.info("This should never occur anymore!!!")
|
self.logger.info("This should never occur anymore!!!")
|
||||||
self.logger.info("Bad song file: '%s'" % e.path)
|
self.logger.info("Bad song file: '%s'" % e.path)
|
||||||
except Exception as e:
|
except Exception as e: self.unexpected_exception( e )
|
||||||
self.logger.info("An evil exception occured")
|
|
||||||
self.logger.error( traceback.format_exc() )
|
|
||||||
def make_req():
|
def make_req():
|
||||||
self.apiclient.send_media_monitor_requests( packed_requests )
|
self.apiclient.send_media_monitor_requests( packed_requests )
|
||||||
for try_index in range(0,self.retries):
|
for try_index in range(0,self.retries):
|
||||||
|
@ -104,14 +101,12 @@ class WatchSyncer(ReportHandler,Loggable):
|
||||||
def __init__(self, signal, chunking_number = 100, timeout=15):
|
def __init__(self, signal, chunking_number = 100, timeout=15):
|
||||||
self.timeout = float(timeout)
|
self.timeout = float(timeout)
|
||||||
self.chunking_number = int(chunking_number)
|
self.chunking_number = int(chunking_number)
|
||||||
self.__reset_queue()
|
|
||||||
# Even though we are not blocking on the http requests, we are still
|
|
||||||
# trying to send the http requests in order
|
|
||||||
self.__requests = []
|
|
||||||
self.request_running = False
|
self.request_running = False
|
||||||
# we don't actually use this "private" instance variable anywhere
|
|
||||||
self.__current_thread = None
|
self.__current_thread = None
|
||||||
|
self.__requests = []
|
||||||
self.contractor = EventContractor()
|
self.contractor = EventContractor()
|
||||||
|
self.__reset_queue()
|
||||||
|
|
||||||
tc = TimeoutWatcher(self, self.timeout)
|
tc = TimeoutWatcher(self, self.timeout)
|
||||||
tc.daemon = True
|
tc.daemon = True
|
||||||
tc.start()
|
tc.start()
|
||||||
|
|
|
@ -122,6 +122,8 @@ def main(global_config, api_client_config, log_config,
|
||||||
|
|
||||||
# Launch the toucher that updates the last time when the script was
|
# Launch the toucher that updates the last time when the script was
|
||||||
# ran every n seconds.
|
# ran every n seconds.
|
||||||
|
# TODO : verify that this does not interfere with bootstrapping because the
|
||||||
|
# toucher thread might update the last_ran variable too fast
|
||||||
tt = ToucherThread(path=config['index_path'],
|
tt = ToucherThread(path=config['index_path'],
|
||||||
interval=int(config['touch_interval']))
|
interval=int(config['touch_interval']))
|
||||||
|
|
||||||
|
|
|
@ -60,4 +60,11 @@ class TestMMP(unittest.TestCase):
|
||||||
self.assertRaises( ValueError, lambda : mmp.file_md5('/bull/shit/path') )
|
self.assertRaises( ValueError, lambda : mmp.file_md5('/bull/shit/path') )
|
||||||
self.assertTrue( m1 == mmp.file_md5(p) )
|
self.assertTrue( m1 == mmp.file_md5(p) )
|
||||||
|
|
||||||
|
def test_sub_path(self):
|
||||||
|
f1 = "/home/testing/123.mp3"
|
||||||
|
d1 = "/home/testing"
|
||||||
|
d2 = "/home/testing/"
|
||||||
|
self.assertTrue( mmp.sub_path(d1, f1) )
|
||||||
|
self.assertTrue( mmp.sub_path(d2, f1) )
|
||||||
|
|
||||||
if __name__ == '__main__': unittest.main()
|
if __name__ == '__main__': unittest.main()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue