From ce313a5b22ca0cd3f8a1dbb3678661671b148642 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Mon, 5 Nov 2012 13:29:10 -0500 Subject: [PATCH 01/33] docstring formatting --- .../media-monitor2/media/monitor/manager.py | 98 +++++++------------ 1 file changed, 34 insertions(+), 64 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/manager.py b/python_apps/media-monitor2/media/monitor/manager.py index 548590f38..2c5512ec6 100644 --- a/python_apps/media-monitor2/media/monitor/manager.py +++ b/python_apps/media-monitor2/media/monitor/manager.py @@ -13,11 +13,10 @@ import media.monitor.pure as mmp class ManagerTimeout(threading.Thread,Loggable): - """ - The purpose of this class is to flush the organize directory every 3 - secnods. This used to be just a work around for cc-4235 but recently - became a permanent solution because it's "cheap" and reliable - """ + """ The purpose of this class is to flush the organize directory + every 3 secnods. This used to be just a work around for cc-4235 + but recently became a permanent solution because it's "cheap" and + reliable """ def __init__(self, manager, interval=1.5): # TODO : interval should be read from config and passed here instead # of just using the hard coded value @@ -30,11 +29,9 @@ class ManagerTimeout(threading.Thread,Loggable): self.manager.flush_organize() class Manager(Loggable): - """ - An abstraction over media monitors core pyinotify functions. These - include adding watched,store, organize directories, etc. Basically - composes over WatchManager from pyinotify - """ + """ An abstraction over media monitors core pyinotify functions. + These include adding watched,store, organize directories, etc. + Basically composes over WatchManager from pyinotify """ def __init__(self): self.wm = pyinotify.WatchManager() # These two instance variables are assumed to be constant @@ -76,23 +73,19 @@ class Manager(Loggable): # through dedicated handler objects. Because we must have access to a # manager instance. Hence we must slightly break encapsulation. def watch_move(self, watch_dir, sender=None): - """ - handle 'watch move' events directly sent from listener - """ + """ handle 'watch move' events directly sent from listener """ self.logger.info("Watch dir '%s' has been renamed (hence removed)" % watch_dir) self.remove_watch_directory(normpath(watch_dir)) def watch_signal(self): - """ - Return the signal string our watch_listener is reading events from - """ + """ Return the signal string our watch_listener is reading + events from """ return self.watch_listener.signal def __remove_watch(self,path): - """ - Remove path from being watched (first will check if 'path' is watched) - """ + """ Remove path from being watched (first will check if 'path' + is watched) """ # only delete if dir is actually being watched if path in self.__wd_path: wd = self.__wd_path[path] @@ -100,10 +93,8 @@ class Manager(Loggable): del(self.__wd_path[path]) def __add_watch(self,path,listener): - """ - Start watching 'path' using 'listener'. First will check if directory - is being watched before adding another watch - """ + """ Start watching 'path' using 'listener'. First will check if + directory is being watched before adding another watch """ self.logger.info("Attempting to add listener to path '%s'" % path) self.logger.info( 'Listener: %s' % str(listener) ) @@ -114,9 +105,8 @@ class Manager(Loggable): if wd: self.__wd_path[path] = wd.values()[0] def __create_organizer(self, target_path, recorded_path): - """ - creates an organizer at new destination path or modifies the old one - """ + """ creates an organizer at new destination path or modifies the + old one """ # TODO : find a proper fix for the following hack # We avoid creating new instances of organize because of the way # it interacts with pydispatch. We must be careful to never have @@ -134,23 +124,17 @@ class Manager(Loggable): recorded_path=recorded_path) def get_problem_files_path(self): - """ - returns the path where problem files should go - """ + """ returns the path where problem files should go """ return self.organize['problem_files_path'] def set_problem_files_path(self, new_path): - """ - Set the path where problem files should go - """ + """ Set the path where problem files should go """ self.organize['problem_files_path'] = new_path self.organize['problem_handler'] = \ ProblemFileHandler( PathChannel(signal='badfile',path=new_path) ) def get_recorded_path(self): - """ - returns the path of the recorded directory - """ + """ returns the path of the recorded directory """ return self.organize['recorded_path'] def set_recorded_path(self, new_path): @@ -160,17 +144,14 @@ class Manager(Loggable): self.__add_watch(new_path, self.watch_listener) def get_organize_path(self): - """ - returns the current path that is being watched for organization - """ + """ returns the current path that is being watched for + organization """ return self.organize['organize_path'] def set_organize_path(self, new_path): - """ - sets the organize path to be new_path. Under the current scheme there - is only one organize path but there is no reason why more cannot be - supported - """ + """ sets the organize path to be new_path. Under the current + scheme there is only one organize path but there is no reason + why more cannot be supported """ # if we are already organizing a particular directory we remove the # watch from it first before organizing another directory self.__remove_watch(self.organize['organize_path']) @@ -188,19 +169,15 @@ class Manager(Loggable): return self.organize['imported_path'] def set_imported_path(self,new_path): - """ - set the directory where organized files go to. - """ + """ set the directory where organized files go to. """ self.__remove_watch(self.organize['imported_path']) self.organize['imported_path'] = new_path self.__create_organizer( new_path, self.organize['recorded_path']) self.__add_watch(new_path, self.watch_listener) def change_storage_root(self, store): - """ - hooks up all the directories for you. Problem, recorded, imported, - organize. - """ + """ hooks up all the directories for you. Problem, recorded, + imported, organize. """ store_paths = mmp.expand_storage(store) # First attempt to make sure that all paths exist before adding any # watches @@ -217,18 +194,14 @@ class Manager(Loggable): mmp.create_dir(p) def has_watch(self, path): - """ - returns true if the path is being watched or not. Any kind of watch: - organize, store, watched. - """ + """ returns true if the path is being watched or not. Any kind + of watch: organize, store, watched. """ return path in self.__wd_path def add_watch_directory(self, new_dir): - """ - adds a directory to be "watched". "watched" directories are + """ adds a directory to be "watched". "watched" directories are those that are being monitored by media monitor for airtime in - this context and not directories pyinotify calls watched - """ + this context and not directories pyinotify calls watched """ if self.has_watch(new_dir): self.logger.info("Cannot add '%s' to watched directories. It's \ already being watched" % new_dir) @@ -237,9 +210,8 @@ class Manager(Loggable): self.__add_watch(new_dir, self.watch_listener) def remove_watch_directory(self, watch_dir): - """ - removes a directory from being "watched". Undoes add_watch_directory - """ + """ removes a directory from being "watched". Undoes + add_watch_directory """ if self.has_watch(watch_dir): self.logger.info("Removing watched directory: '%s'", watch_dir) self.__remove_watch(watch_dir) @@ -250,9 +222,7 @@ class Manager(Loggable): self.logger.info( self.__wd_path ) def loop(self): - """ - block until we receive pyinotify events - """ + """ block until we receive pyinotify events """ notifier = pyinotify.Notifier(self.wm) notifier.coalesce_events() notifier.loop() From c287e11bee8dc50a3129d5bef3d17b12755386e0 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Mon, 5 Nov 2012 14:02:55 -0500 Subject: [PATCH 02/33] moved setting up unicde into it's own routine --- python_apps/api_clients/api_client.py | 1 + python_apps/media-monitor2/mm2.py | 34 +++++++++++++++------------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index ac93aa4c6..633993903 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -389,3 +389,4 @@ class AirtimeApiClient(object): """ self.logger.info( self.notify_webstream_data.req( _post_data={'data':data}, media_id=str(media_id)).retry(5)) + diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 2abace71c..dc9cd160a 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -24,6 +24,24 @@ import media.monitor.pure as mmp from api_clients import api_client as apc +def setup_global(log): + """ setup unicode and other stuff """ + log.info("Attempting to set the locale...") + try: + mmp.configure_locale(mmp.get_system_locale()) + except FailedToSetLocale as e: + log.info("Failed to set the locale...") + sys.exit(1) + except FailedToObtainLocale as e: + log.info("Failed to obtain the locale form the default path: \ + '/etc/default/locale'") + sys.exit(1) + except Exception as e: + log.info("Failed to set the locale for unknown reason. \ + Logging exception.") + log.info(str(e)) + + def main(global_config, api_client_config, log_config, index_create_attempt=False): @@ -72,21 +90,7 @@ def main(global_config, api_client_config, log_config, if not os.path.exists(config['index_path']): log.info("Index file does not exist. Terminating") - log.info("Attempting to set the locale...") - - try: - mmp.configure_locale(mmp.get_system_locale()) - except FailedToSetLocale as e: - log.info("Failed to set the locale...") - sys.exit(1) - except FailedToObtainLocale as e: - log.info("Failed to obtain the locale form the default path: \ - '/etc/default/locale'") - sys.exit(1) - except Exception as e: - log.info("Failed to set the locale for unknown reason. \ - Logging exception.") - log.info(str(e)) + setup_global(log) watch_syncer = WatchSyncer(signal='watch', chunking_number=config['chunking_number'], From 3509cfc2128b9547915f9487c978504ac6924ffb Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Mon, 5 Nov 2012 14:03:46 -0500 Subject: [PATCH 03/33] formatting --- python_apps/media-monitor2/mm2.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index dc9cd160a..7e786722d 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -27,8 +27,7 @@ from api_clients import api_client as apc def setup_global(log): """ setup unicode and other stuff """ log.info("Attempting to set the locale...") - try: - mmp.configure_locale(mmp.get_system_locale()) + try: mmp.configure_locale(mmp.get_system_locale()) except FailedToSetLocale as e: log.info("Failed to set the locale...") sys.exit(1) From 83d4705bcadd079c88d2b1acccc47b72d33e83a2 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Mon, 5 Nov 2012 14:19:37 -0500 Subject: [PATCH 04/33] removed useless comments --- python_apps/media-monitor2/media/monitor/manager.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/manager.py b/python_apps/media-monitor2/media/monitor/manager.py index 2c5512ec6..e64d1c985 100644 --- a/python_apps/media-monitor2/media/monitor/manager.py +++ b/python_apps/media-monitor2/media/monitor/manager.py @@ -38,8 +38,6 @@ class Manager(Loggable): self.watch_channel = 'watch' self.organize_channel = 'organize' self.watch_listener = StoreWatchListener(signal = self.watch_channel) - # TODO : change this to a weak ref - # TODO : get rid of this hack once cc-4235 is fixed self.__timeout_thread = ManagerTimeout(self) self.__timeout_thread.daemon = True self.__timeout_thread.start() From a851d8dd7426b88712b2fca81d0ea81ecae0843b Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Mon, 5 Nov 2012 16:04:39 -0500 Subject: [PATCH 05/33] docstring format --- .../media-monitor2/media/monitor/config.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/config.py b/python_apps/media-monitor2/media/monitor/config.py index 3a7be8eb5..b06e00a84 100644 --- a/python_apps/media-monitor2/media/monitor/config.py +++ b/python_apps/media-monitor2/media/monitor/config.py @@ -12,25 +12,21 @@ class MMConfig(object): self.cfg = ConfigObj(path) def __getitem__(self, key): - """ - We always return a copy of the config item to prevent callers from - doing any modifications through the returned objects methods - """ + """ We always return a copy of the config item to prevent + callers from doing any modifications through the returned + objects methods """ return copy.deepcopy(self.cfg[key]) def __setitem__(self, key, value): - """ - We use this method not to allow anybody to mess around with config file - any settings made should be done through MMConfig's instance methods - """ + """ We use this method not to allow anybody to mess around with + config file any settings made should be done through MMConfig's + instance methods """ raise ConfigAccessViolation(key) def save(self): self.cfg.write() 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 - """ + """ 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']) From 6392c69e27abe91679898e78fe1dccdf14beb875 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Mon, 5 Nov 2012 16:10:59 -0500 Subject: [PATCH 06/33] Removed unused code --- python_apps/media-monitor2/mm2.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 7e786722d..9471f698b 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -151,9 +151,6 @@ Options: --log=<path> log config at <path> """ -def main_loop(): - while True: pass - if __name__ == '__main__': from docopt import docopt args = docopt(__doc__,version="mm1.99") From 66138c4bafc5f831216ac935ee879d69833cb79f Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Mon, 5 Nov 2012 16:11:35 -0500 Subject: [PATCH 07/33] removed references to gevent --- python_apps/media-monitor2/mm2.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 9471f698b..99d16f9f7 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -160,5 +160,4 @@ if __name__ == '__main__': sys.exit(0) print("Running mm1.99") main(args['--config'],args['--apiclient'],args['--log']) - #gevent.joinall([ gevent.spawn(main_loop) ]) From 110008e9091968837d0137e1b61a29851c248154 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 11:29:39 -0500 Subject: [PATCH 08/33] added saas shit --- .../media-monitor2/media/saas/__init__.py | 0 .../media/saas/airtimeinstance.py | 32 +++++++++++++++++++ .../media-monitor2/media/saas/thread.py | 19 +++++++++++ 3 files changed, 51 insertions(+) create mode 100644 python_apps/media-monitor2/media/saas/__init__.py create mode 100644 python_apps/media-monitor2/media/saas/airtimeinstance.py create mode 100644 python_apps/media-monitor2/media/saas/thread.py diff --git a/python_apps/media-monitor2/media/saas/__init__.py b/python_apps/media-monitor2/media/saas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python_apps/media-monitor2/media/saas/airtimeinstance.py b/python_apps/media-monitor2/media/saas/airtimeinstance.py new file mode 100644 index 000000000..ed37caa41 --- /dev/null +++ b/python_apps/media-monitor2/media/saas/airtimeinstance.py @@ -0,0 +1,32 @@ +import os + +from media.monitor.exceptions import NoConfigFile +from media.monitor.pure import LazyProperty +from media.monitor.config import MMConfig +from api_cients import AirtimeApiClient + +class AirtimeInstance(object): + """ AirtimeInstance is a class that abstracts away every airtime + instance by providing all the necessary objects required to interact + with the instance. ApiClient, configs, root_directory """ + + def __init__(self,name, root_path, config_paths): + """ name is an internal name only """ + for cfg in ['api_client','media_monitor', 'logging']: + if cfg not in config_paths: raise NoConfigFile(config_paths) + elif not os.path.exists(config_paths[cfg]): + raise NoConfigFile(config_paths[cfg]) + self.name = name + self.config_paths = config_paths + self.root_path = root_path + + def __str__(self): + return "%s,%s(%s)" % (self.name, self.root_path, self.config_paths) + + @LazyProperty + def api_client(self): + return AirtimeApiClient(config_path=self.config_paths['api_client']) + + @LazyProperty + def mm_config(self): + return MMConfig(self.config_paths['media_monitor']) diff --git a/python_apps/media-monitor2/media/saas/thread.py b/python_apps/media-monitor2/media/saas/thread.py new file mode 100644 index 000000000..b71c4a8fb --- /dev/null +++ b/python_apps/media-monitor2/media/saas/thread.py @@ -0,0 +1,19 @@ +import threading + +tc = threading.local() + +class InstanceThread(threading.Thread): + def __init__(self,user, *args, **kwargs): + super(InstanceThread, self).__init__(*args, **kwargs) + self._user = user + + def run(self): + tc._user = self._user + + def user(self): + return tc._user + +class InstanceInheritingThread(threading.Thread): + def __init__(self, *args, **kwargs): + super(InstanceInheritingThread, self).__init__(*args, **kwargs) + self.user = tc._user From 882a515caa4101ab009d16de1ef65f7cbcbc9027 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 17:44:55 -0500 Subject: [PATCH 09/33] refactored threads which know the user they belong to --- python_apps/media-monitor2/media/saas/thread.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/python_apps/media-monitor2/media/saas/thread.py b/python_apps/media-monitor2/media/saas/thread.py index b71c4a8fb..489ad35a8 100644 --- a/python_apps/media-monitor2/media/saas/thread.py +++ b/python_apps/media-monitor2/media/saas/thread.py @@ -2,18 +2,16 @@ import threading tc = threading.local() -class InstanceThread(threading.Thread): +class HasUser(object): + def user(self): + return self._user + +class InstanceThread(threading.Thread, HasUser): def __init__(self,user, *args, **kwargs): super(InstanceThread, self).__init__(*args, **kwargs) self._user = user - - def run(self): - tc._user = self._user - def user(self): - return tc._user - -class InstanceInheritingThread(threading.Thread): +class InstanceInheritingThread(threading.Thread, HasUser): def __init__(self, *args, **kwargs): + self._user = threading.current_thread().user() super(InstanceInheritingThread, self).__init__(*args, **kwargs) - self.user = tc._user From 9e7b8a6b28409b5a27db32c2f1420e6bab481917 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 17:45:08 -0500 Subject: [PATCH 10/33] added tests --- .../media-monitor2/tests/test_thread.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 python_apps/media-monitor2/tests/test_thread.py diff --git a/python_apps/media-monitor2/tests/test_thread.py b/python_apps/media-monitor2/tests/test_thread.py new file mode 100644 index 000000000..1638a60e3 --- /dev/null +++ b/python_apps/media-monitor2/tests/test_thread.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +import unittest +import time +from media.saas.thread import InstanceThread, InstanceInheritingThread + +# ugly but necessary for 2.7 +signal = False +signal2 = False + +class TestInstanceThread(unittest.TestCase): + def test_user_inject(self): + global signal + signal = False + u = "rudi" + class T(InstanceThread): + def run(me): + global signal + super(T, me).run() + signal = True + self.assertEquals(u, me.user()) + t = T(u, name="test_user_inject") + t.daemon = True + t.start() + time.sleep(0.2) + self.assertTrue(signal) + + def test_inheriting_thread(utest): + global signal2 + u = "testing..." + + class TT(InstanceInheritingThread): + def run(self): + global signal2 + utest.assertEquals(self.user(), u) + signal2 = True + + class T(InstanceThread): + def run(self): + super(T, self).run() + child_thread = TT(name="child thread") + child_thread.daemon = True + child_thread.start() + + parent_thread = T(u, name="Parent instance thread") + parent_thread.daemon = True + parent_thread.start() + + time.sleep(0.2) + utest.assertTrue(signal2) + + def test_different_user(utest): + u1, u2 = "ru", "di" + class T(InstanceThread): + def run(self): + super(T, self).run() + + for u in [u1, u2]: + t = T(u) + t.daemon = True + t.start() + utest.assertEquals(t.user(), u) + + +if __name__ == '__main__': unittest.main() From 13f59be21de8fa41a2b20f0b7626e4ec1e6dc0dc Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 18:16:33 -0500 Subject: [PATCH 11/33] typo --- python_apps/media-monitor2/media/saas/airtimeinstance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/media/saas/airtimeinstance.py b/python_apps/media-monitor2/media/saas/airtimeinstance.py index ed37caa41..293fc84c2 100644 --- a/python_apps/media-monitor2/media/saas/airtimeinstance.py +++ b/python_apps/media-monitor2/media/saas/airtimeinstance.py @@ -3,7 +3,7 @@ import os from media.monitor.exceptions import NoConfigFile from media.monitor.pure import LazyProperty from media.monitor.config import MMConfig -from api_cients import AirtimeApiClient +from api_clients import AirtimeApiClient class AirtimeInstance(object): """ AirtimeInstance is a class that abstracts away every airtime From 7e5ec6505b142a466296795c2b909705a23474c4 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 18:16:48 -0500 Subject: [PATCH 12/33] added tests for instance --- .../media-monitor2/tests/test_instance.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 python_apps/media-monitor2/tests/test_instance.py diff --git a/python_apps/media-monitor2/tests/test_instance.py b/python_apps/media-monitor2/tests/test_instance.py new file mode 100644 index 000000000..badaadf07 --- /dev/null +++ b/python_apps/media-monitor2/tests/test_instance.py @@ -0,0 +1,21 @@ +import unittest +from copy import deepcopy +from media.saas.airtimeinstance import AirtimeInstance, NoConfigFile + +class TestAirtimeInstance(unittest.TestCase): + def setUp(self): + self.cfg = { + 'api_client' : 'tests/test_instance.py', + 'media_monitor' : 'tests/test_instance.py', + 'logging' : 'tests/test_instance.py', + } + + def test_init_good(self): + AirtimeInstance("/root", self.cfg) + self.assertTrue(True) + + def test_init_bad(self): + cfg = deepcopy(self.cfg) + cfg['api_client'] = 'bs' + with self.assertRaises(NoConfigFile): + AirtimeInstance("/root", cfg) From ed00089a1a6df7ba6439ead4b600ecdb7e9e2acb Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 22:53:39 -0500 Subject: [PATCH 13/33] added user level method to get current user of thread --- python_apps/media-monitor2/media/saas/thread.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/python_apps/media-monitor2/media/saas/thread.py b/python_apps/media-monitor2/media/saas/thread.py index 489ad35a8..f2e5ae28d 100644 --- a/python_apps/media-monitor2/media/saas/thread.py +++ b/python_apps/media-monitor2/media/saas/thread.py @@ -1,10 +1,12 @@ import threading -tc = threading.local() +class UserlessThread(Exception): + def __str__(): + return "Current thread: %s is not an instance of InstanceThread \ + of InstanceInheritingThread" % str(threading.current_thread()) class HasUser(object): - def user(self): - return self._user + def user(self): return self._user class InstanceThread(threading.Thread, HasUser): def __init__(self,user, *args, **kwargs): @@ -15,3 +17,7 @@ class InstanceInheritingThread(threading.Thread, HasUser): def __init__(self, *args, **kwargs): self._user = threading.current_thread().user() super(InstanceInheritingThread, self).__init__(*args, **kwargs) + +def user(): + try: return threading.current_thread().user() + except AttributeError: raise UserlessThread() From 55f0462946a7e4050050d1572c619780a8e81c51 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 23:21:08 -0500 Subject: [PATCH 14/33] removed useless line of code --- python_apps/media-monitor2/media/saas/thread.py | 2 ++ python_apps/media-monitor2/mm2.py | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python_apps/media-monitor2/media/saas/thread.py b/python_apps/media-monitor2/media/saas/thread.py index f2e5ae28d..0b4ae079e 100644 --- a/python_apps/media-monitor2/media/saas/thread.py +++ b/python_apps/media-monitor2/media/saas/thread.py @@ -21,3 +21,5 @@ class InstanceInheritingThread(threading.Thread, HasUser): def user(): try: return threading.current_thread().user() except AttributeError: raise UserlessThread() + +def apc(): return user().api_client diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 99d16f9f7..b246a9491 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -124,8 +124,6 @@ def main(global_config, api_client_config, log_config, airtime_receiver.new_watch({ 'directory':watch_dir }, restart=True) else: log.info("Failed to add watch on %s" % str(watch_dir)) - bs = Bootstrapper( db=sdb, watch_signal='watch' ) - ed = EventDrainer(airtime_notifier.connection, interval=float(config['rmq_event_wait'])) From c222ac10f74e5793889d3c0d35ceb2f821a11e33 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 23:21:48 -0500 Subject: [PATCH 15/33] typo --- python_apps/media-monitor2/mm2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index b246a9491..8c1c35abb 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -48,7 +48,7 @@ def main(global_config, api_client_config, log_config, if not os.path.exists(cfg): raise NoConfigFile(cfg) # MMConfig is a proxy around ConfigObj instances. it does not allow # itself users of MMConfig instances to modify any config options - # directly through the dictionary. Users of this object muse use the + # directly through the dictionary. Users of this object must use the # correct methods designated for modification try: config = MMConfig(global_config) except NoConfigFile as e: From eaf0baa6b6501c88774ed9aa0e24d99be0531ca0 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 7 Nov 2012 23:22:25 -0500 Subject: [PATCH 16/33] removed useless code --- python_apps/media-monitor2/mm2.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 8c1c35abb..9d12ed32d 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -5,7 +5,6 @@ import logging import logging.config from media.monitor.manager import Manager -from media.monitor.bootstrap import Bootstrapper from media.monitor.log import get_logger, setup_logging from media.monitor.config import MMConfig from media.monitor.toucher import ToucherThread @@ -23,7 +22,6 @@ from std_err_override import LogWriter import media.monitor.pure as mmp from api_clients import api_client as apc - def setup_global(log): """ setup unicode and other stuff """ log.info("Attempting to set the locale...") @@ -41,7 +39,6 @@ def setup_global(log): log.info(str(e)) - def main(global_config, api_client_config, log_config, index_create_attempt=False): for cfg in [global_config, api_client_config]: @@ -100,8 +97,6 @@ def main(global_config, api_client_config, log_config, ReplayGainUpdater.start_reply_gain(apiclient) - sdb = AirtimeDB(apiclient) - manager = Manager() airtime_receiver = AirtimeMessageReceiver(config,manager) From 350d5dd6204ec7cec4d0e98bf6fdc8b9738b4e59 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Sat, 10 Nov 2012 09:24:26 -0500 Subject: [PATCH 17/33] refactored logging setup when mm is setup --- python_apps/media-monitor2/mm2.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 9d12ed32d..f78768201 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -22,6 +22,17 @@ from std_err_override import LogWriter import media.monitor.pure as mmp from api_clients import api_client as apc +def setup_logger(log_config, logpath): + logging.config.fileConfig(log_config) + #need to wait for Python 2.7 for this.. + #logging.captureWarnings(True) + logger = logging.getLogger() + LogWriter.override_std_err(logger) + logfile = unicode(logpath) + setup_logging(logfile) + log = get_logger() + return log + def setup_global(log): """ setup unicode and other stuff """ log.info("Attempting to set the locale...") @@ -55,18 +66,8 @@ def main(global_config, api_client_config, log_config, except Exception as e: print("Unknown error reading configuration file: '%s'" % global_config) print(str(e)) - - - logging.config.fileConfig(log_config) - - #need to wait for Python 2.7 for this.. - #logging.captureWarnings(True) - - logger = logging.getLogger() - LogWriter.override_std_err(logger) - logfile = unicode( config['logpath'] ) - setup_logging(logfile) - log = get_logger() + + log = setup_logger( log_config, config['logpath'] ) if not index_create_attempt: if not os.path.exists(config['index_path']): From a727f2bb1ecb632fa797ea6bb58ab5e834ba2b09 Mon Sep 17 00:00:00 2001 From: denise <denise@denise-DX4860sourcefabric.org> Date: Mon, 12 Nov 2012 14:28:50 -0500 Subject: [PATCH 18/33] CC-4638: Stream Settings -> Apache error when setting master source port to same port Airtime is running on -fixed --- .../application/forms/LiveStreamingPreferences.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/airtime_mvc/application/forms/LiveStreamingPreferences.php b/airtime_mvc/application/forms/LiveStreamingPreferences.php index c5b9d9aca..5a5c6fa49 100644 --- a/airtime_mvc/application/forms/LiveStreamingPreferences.php +++ b/airtime_mvc/application/forms/LiveStreamingPreferences.php @@ -150,13 +150,15 @@ class Application_Form_LiveStreamingPreferences extends Zend_Form_SubForm if ($master_harbor_input_port == $dj_harbor_input_port && $master_harbor_input_port != "") { $element = $this->getElement("dj_harbor_input_port"); $element->addError("You cannot use same port as Master DJ port."); + $isValid = false; } if ($master_harbor_input_port != "") { if (is_numeric($master_harbor_input_port)) { if ($master_harbor_input_port != Application_Model_StreamSetting::getMasterLiveStreamPort()) { $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); - $res = socket_bind($sock, 0, $master_harbor_input_port); - if (!$res) { + try { + socket_bind($sock, 0, $master_harbor_input_port); + } catch (Exception $e) { $element = $this->getElement("master_harbor_input_port"); $element->addError("Port '$master_harbor_input_port' is not available."); $isValid = false; @@ -171,8 +173,9 @@ class Application_Form_LiveStreamingPreferences extends Zend_Form_SubForm if (is_numeric($dj_harbor_input_port)) { if ($dj_harbor_input_port != Application_Model_StreamSetting::getDjLiveStreamPort()) { $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); - $res = socket_bind($sock, 0, $dj_harbor_input_port); - if (!$res) { + try { + socket_bind($sock, 0, $dj_harbor_input_port); + } catch (Exception $e) { $element = $this->getElement("dj_harbor_input_port"); $element->addError("Port '$dj_harbor_input_port' is not available."); $isValid = false; From 93ff2ce9f6c4c324945487ec0f413dfe5abca392 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Tue, 13 Nov 2012 17:32:42 -0500 Subject: [PATCH 19/33] removed extra import --- python_apps/media-monitor2/mm2.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index f78768201..0a3acfabd 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -8,7 +8,6 @@ from media.monitor.manager import Manager from media.monitor.log import get_logger, setup_logging from media.monitor.config import MMConfig from media.monitor.toucher import ToucherThread -from media.monitor.syncdb import AirtimeDB from media.monitor.exceptions import FailedToObtainLocale, \ FailedToSetLocale, \ NoConfigFile From f1effc37a98a2606ce8d67ebdc875bf0a4f986c1 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 14 Nov 2012 11:51:00 -0500 Subject: [PATCH 20/33] removed dependency of mm from replaygain --- .../media-monitor2/media/update/replaygainupdater.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python_apps/media-monitor2/media/update/replaygainupdater.py b/python_apps/media-monitor2/media/update/replaygainupdater.py index df48f09de..2f52c0a23 100644 --- a/python_apps/media-monitor2/media/update/replaygainupdater.py +++ b/python_apps/media-monitor2/media/update/replaygainupdater.py @@ -3,12 +3,11 @@ from threading import Thread import traceback import os import time +import logging from media.update import replaygain -from media.monitor.log import Loggable - -class ReplayGainUpdater(Thread, Loggable): +class ReplayGainUpdater(Thread): """ The purpose of the class is to query the server for a list of files which do not have a ReplayGain value calculated. This class will iterate over the @@ -30,6 +29,7 @@ class ReplayGainUpdater(Thread, Loggable): def __init__(self,apc): Thread.__init__(self) self.api_client = apc + self.logger = logging.getLogger() def main(self): raw_response = self.api_client.list_all_watched_dirs() From d5cacf4011995f68ad4e7e933b40294515c89f77 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 14 Nov 2012 14:43:10 -0500 Subject: [PATCH 21/33] moved replay gain to pypo for better saas performance --- .../media-monitor2/media/update/__init__.py | 0 .../media-monitor2/media/update/replaygain.py | 152 ------------------ .../media/update/replaygainupdater.py | 82 ---------- python_apps/media-monitor2/mm2.py | 3 +- python_apps/pypo/pypocli.py | 5 + 5 files changed, 6 insertions(+), 236 deletions(-) delete mode 100644 python_apps/media-monitor2/media/update/__init__.py delete mode 100644 python_apps/media-monitor2/media/update/replaygain.py delete mode 100644 python_apps/media-monitor2/media/update/replaygainupdater.py diff --git a/python_apps/media-monitor2/media/update/__init__.py b/python_apps/media-monitor2/media/update/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/python_apps/media-monitor2/media/update/replaygain.py b/python_apps/media-monitor2/media/update/replaygain.py deleted file mode 100644 index 5af7cd4a1..000000000 --- a/python_apps/media-monitor2/media/update/replaygain.py +++ /dev/null @@ -1,152 +0,0 @@ -from subprocess import Popen, PIPE -import re -import os -import sys -import shutil -import tempfile -import logging - - -logger = logging.getLogger() - -def get_process_output(command): - """ - Run subprocess and return stdout - """ - logger.debug(command) - p = Popen(command, shell=True, stdout=PIPE) - return p.communicate()[0].strip() - -def run_process(command): - """ - Run subprocess and return "return code" - """ - p = Popen(command, shell=True) - return os.waitpid(p.pid, 0)[1] - -def get_mime_type(file_path): - """ - Attempts to get the mime type but will return prematurely if the process - takes longer than 5 seconds. Note that this function should only be called - for files which do not have a mp3/ogg/flac extension. - """ - - return get_process_output("timeout 5 file -b --mime-type %s" % file_path) - -def duplicate_file(file_path): - """ - Makes a duplicate of the file and returns the path of this duplicate file. - """ - fsrc = open(file_path, 'r') - fdst = tempfile.NamedTemporaryFile(delete=False) - - logger.info("Copying %s to %s" % (file_path, fdst.name)) - - shutil.copyfileobj(fsrc, fdst) - - fsrc.close() - fdst.close() - - return fdst.name - -def get_file_type(file_path): - file_type = None - if re.search(r'mp3$', file_path, re.IGNORECASE): - file_type = 'mp3' - elif re.search(r'og(g|a)$', file_path, re.IGNORECASE): - file_type = 'vorbis' - elif re.search(r'flac$', file_path, re.IGNORECASE): - file_type = 'flac' - else: - mime_type = get_mime_type(file_path) - if 'mpeg' in mime_type: - file_type = 'mp3' - elif 'ogg' in mime_type: - file_type = 'vorbis' - elif 'flac' in mime_type: - file_type = 'flac' - - return file_type - - -def calculate_replay_gain(file_path): - """ - This function accepts files of type mp3/ogg/flac and returns a calculated - ReplayGain value in dB. - If the value cannot be calculated for some reason, then we default to 0 - (Unity Gain). - - http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification - """ - - try: - """ - Making a duplicate is required because the ReplayGain extraction utilities we use - make unwanted modifications to the file. - """ - - search = None - temp_file_path = duplicate_file(file_path) - - file_type = get_file_type(file_path) - nice_level = '15' - - if file_type: - if file_type == 'mp3': - if run_process("which mp3gain > /dev/null") == 0: - command = 'nice -n %s mp3gain -q "%s" 2> /dev/null' \ - % (nice_level, temp_file_path) - out = get_process_output(command) - search = re.search(r'Recommended "Track" dB change: (.*)', \ - out) - else: - logger.warn("mp3gain not found") - elif file_type == 'vorbis': - command = "which vorbisgain > /dev/null && which ogginfo > \ - /dev/null" - if run_process(command) == 0: - command = 'nice -n %s vorbisgain -q -f "%s" 2>/dev/null \ - >/dev/null' % (nice_level,temp_file_path) - run_process(command) - - out = get_process_output('ogginfo "%s"' % temp_file_path) - search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) - else: - logger.warn("vorbisgain/ogginfo not found") - elif file_type == 'flac': - if run_process("which metaflac > /dev/null") == 0: - - command = 'nice -n %s metaflac --add-replay-gain "%s"' \ - % (nice_level, temp_file_path) - run_process(command) - - command = 'nice -n %s metaflac \ - --show-tag=REPLAYGAIN_TRACK_GAIN "%s"' \ - % (nice_level, temp_file_path) - - out = get_process_output(command) - search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) - else: logger.warn("metaflac not found") - - except Exception, e: - logger.error(str(e)) - finally: - #no longer need the temp, file simply remove it. - try: os.remove(temp_file_path) - except: pass - - replay_gain = 0 - if search: - matches = search.groups() - if len(matches) == 1: - replay_gain = matches[0] - else: - logger.warn("Received more than 1 match in: '%s'" % str(matches)) - - return replay_gain - - -# Example of running from command line: -# python replay_gain.py /path/to/filename.mp3 -if __name__ == "__main__": - print calculate_replay_gain(sys.argv[1]) diff --git a/python_apps/media-monitor2/media/update/replaygainupdater.py b/python_apps/media-monitor2/media/update/replaygainupdater.py deleted file mode 100644 index 2f52c0a23..000000000 --- a/python_apps/media-monitor2/media/update/replaygainupdater.py +++ /dev/null @@ -1,82 +0,0 @@ -from threading import Thread - -import traceback -import os -import time -import logging - -from media.update import replaygain - -class ReplayGainUpdater(Thread): - """ - The purpose of the class is to query the server for a list of files which - do not have a ReplayGain value calculated. This class will iterate over the - list calculate the values, update the server and repeat the process until - the server reports there are no files left. - - This class will see heavy activity right after a 2.1->2.2 upgrade since 2.2 - introduces ReplayGain normalization. A fresh install of Airtime 2.2 will - see this class not used at all since a file imported in 2.2 will - automatically have its ReplayGain value calculated. - """ - - @staticmethod - def start_reply_gain(apc): - me = ReplayGainUpdater(apc) - me.daemon = True - me.start() - - def __init__(self,apc): - Thread.__init__(self) - self.api_client = apc - self.logger = logging.getLogger() - - def main(self): - raw_response = self.api_client.list_all_watched_dirs() - if 'dirs' not in raw_response: - self.logger.error("Could not get a list of watched directories \ - with a dirs attribute. Printing full request:") - self.logger.error( raw_response ) - return - - directories = raw_response['dirs'] - - for dir_id, dir_path in directories.iteritems(): - try: - # keep getting few rows at a time for current music_dir (stor - # or watched folder). - total = 0 - while True: - # return a list of pairs where the first value is the - # file's database row id and the second value is the - # filepath - files = self.api_client.get_files_without_replay_gain_value(dir_id) - processed_data = [] - for f in files: - full_path = os.path.join(dir_path, f['fp']) - processed_data.append((f['id'], replaygain.calculate_replay_gain(full_path))) - - try: - self.api_client.update_replay_gain_values(processed_data) - except Exception as e: self.unexpected_exception(e) - - if len(files) == 0: break - self.logger.info("Processed: %d songs" % total) - - except Exception, e: - self.logger.error(e) - self.logger.debug(traceback.format_exc()) - def run(self): - try: - while True: - self.logger.info("Runnning replaygain updater") - self.main() - # Sleep for 5 minutes in case new files have been added - time.sleep(60 * 5) - except Exception, e: - self.logger.error('ReplayGainUpdater Exception: %s', traceback.format_exc()) - self.logger.error(e) - -if __name__ == "__main__": - rgu = ReplayGainUpdater() - rgu.main() diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 0a3acfabd..2705ef0ee 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -15,7 +15,6 @@ from media.monitor.airtime import AirtimeNotifier, \ AirtimeMessageReceiver from media.monitor.watchersyncer import WatchSyncer from media.monitor.eventdrainer import EventDrainer -from media.update.replaygainupdater import ReplayGainUpdater from std_err_override import LogWriter import media.monitor.pure as mmp @@ -95,7 +94,7 @@ def main(global_config, api_client_config, log_config, apiclient = apc.AirtimeApiClient.create_right_config(log=log, config_path=api_client_config) - ReplayGainUpdater.start_reply_gain(apiclient) + #ReplayGainUpdater.start_reply_gain(apiclient) manager = Manager() diff --git a/python_apps/pypo/pypocli.py b/python_apps/pypo/pypocli.py index 1b51a13f8..ea8950d41 100644 --- a/python_apps/pypo/pypocli.py +++ b/python_apps/pypo/pypocli.py @@ -24,6 +24,8 @@ from recorder import Recorder from listenerstat import ListenerStat from pypomessagehandler import PypoMessageHandler +from media.update.replaygainupdater import ReplayGainUpdater + from configobj import ConfigObj # custom imports @@ -174,6 +176,9 @@ if __name__ == '__main__': sys.exit() api_client = api_client.AirtimeApiClient() + + ReplayGainUpdater.start_reply_gain(api_client) + api_client.register_component("pypo") pypoFetch_q = Queue() From 2e59eca1317f97f3f8e65e7e965a2dbf4dd92908 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 14 Nov 2012 14:43:33 -0500 Subject: [PATCH 22/33] moved files to pypo dir --- python_apps/pypo/media/__init__.py | 0 python_apps/pypo/media/update/__init__.py | 0 python_apps/pypo/media/update/replaygain.py | 152 ++++++++++++++++++ .../pypo/media/update/replaygainupdater.py | 82 ++++++++++ 4 files changed, 234 insertions(+) create mode 100644 python_apps/pypo/media/__init__.py create mode 100644 python_apps/pypo/media/update/__init__.py create mode 100644 python_apps/pypo/media/update/replaygain.py create mode 100644 python_apps/pypo/media/update/replaygainupdater.py diff --git a/python_apps/pypo/media/__init__.py b/python_apps/pypo/media/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python_apps/pypo/media/update/__init__.py b/python_apps/pypo/media/update/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python_apps/pypo/media/update/replaygain.py b/python_apps/pypo/media/update/replaygain.py new file mode 100644 index 000000000..5af7cd4a1 --- /dev/null +++ b/python_apps/pypo/media/update/replaygain.py @@ -0,0 +1,152 @@ +from subprocess import Popen, PIPE +import re +import os +import sys +import shutil +import tempfile +import logging + + +logger = logging.getLogger() + +def get_process_output(command): + """ + Run subprocess and return stdout + """ + logger.debug(command) + p = Popen(command, shell=True, stdout=PIPE) + return p.communicate()[0].strip() + +def run_process(command): + """ + Run subprocess and return "return code" + """ + p = Popen(command, shell=True) + return os.waitpid(p.pid, 0)[1] + +def get_mime_type(file_path): + """ + Attempts to get the mime type but will return prematurely if the process + takes longer than 5 seconds. Note that this function should only be called + for files which do not have a mp3/ogg/flac extension. + """ + + return get_process_output("timeout 5 file -b --mime-type %s" % file_path) + +def duplicate_file(file_path): + """ + Makes a duplicate of the file and returns the path of this duplicate file. + """ + fsrc = open(file_path, 'r') + fdst = tempfile.NamedTemporaryFile(delete=False) + + logger.info("Copying %s to %s" % (file_path, fdst.name)) + + shutil.copyfileobj(fsrc, fdst) + + fsrc.close() + fdst.close() + + return fdst.name + +def get_file_type(file_path): + file_type = None + if re.search(r'mp3$', file_path, re.IGNORECASE): + file_type = 'mp3' + elif re.search(r'og(g|a)$', file_path, re.IGNORECASE): + file_type = 'vorbis' + elif re.search(r'flac$', file_path, re.IGNORECASE): + file_type = 'flac' + else: + mime_type = get_mime_type(file_path) + if 'mpeg' in mime_type: + file_type = 'mp3' + elif 'ogg' in mime_type: + file_type = 'vorbis' + elif 'flac' in mime_type: + file_type = 'flac' + + return file_type + + +def calculate_replay_gain(file_path): + """ + This function accepts files of type mp3/ogg/flac and returns a calculated + ReplayGain value in dB. + If the value cannot be calculated for some reason, then we default to 0 + (Unity Gain). + + http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification + """ + + try: + """ + Making a duplicate is required because the ReplayGain extraction utilities we use + make unwanted modifications to the file. + """ + + search = None + temp_file_path = duplicate_file(file_path) + + file_type = get_file_type(file_path) + nice_level = '15' + + if file_type: + if file_type == 'mp3': + if run_process("which mp3gain > /dev/null") == 0: + command = 'nice -n %s mp3gain -q "%s" 2> /dev/null' \ + % (nice_level, temp_file_path) + out = get_process_output(command) + search = re.search(r'Recommended "Track" dB change: (.*)', \ + out) + else: + logger.warn("mp3gain not found") + elif file_type == 'vorbis': + command = "which vorbisgain > /dev/null && which ogginfo > \ + /dev/null" + if run_process(command) == 0: + command = 'nice -n %s vorbisgain -q -f "%s" 2>/dev/null \ + >/dev/null' % (nice_level,temp_file_path) + run_process(command) + + out = get_process_output('ogginfo "%s"' % temp_file_path) + search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) + else: + logger.warn("vorbisgain/ogginfo not found") + elif file_type == 'flac': + if run_process("which metaflac > /dev/null") == 0: + + command = 'nice -n %s metaflac --add-replay-gain "%s"' \ + % (nice_level, temp_file_path) + run_process(command) + + command = 'nice -n %s metaflac \ + --show-tag=REPLAYGAIN_TRACK_GAIN "%s"' \ + % (nice_level, temp_file_path) + + out = get_process_output(command) + search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) + else: logger.warn("metaflac not found") + + except Exception, e: + logger.error(str(e)) + finally: + #no longer need the temp, file simply remove it. + try: os.remove(temp_file_path) + except: pass + + replay_gain = 0 + if search: + matches = search.groups() + if len(matches) == 1: + replay_gain = matches[0] + else: + logger.warn("Received more than 1 match in: '%s'" % str(matches)) + + return replay_gain + + +# Example of running from command line: +# python replay_gain.py /path/to/filename.mp3 +if __name__ == "__main__": + print calculate_replay_gain(sys.argv[1]) diff --git a/python_apps/pypo/media/update/replaygainupdater.py b/python_apps/pypo/media/update/replaygainupdater.py new file mode 100644 index 000000000..2f52c0a23 --- /dev/null +++ b/python_apps/pypo/media/update/replaygainupdater.py @@ -0,0 +1,82 @@ +from threading import Thread + +import traceback +import os +import time +import logging + +from media.update import replaygain + +class ReplayGainUpdater(Thread): + """ + The purpose of the class is to query the server for a list of files which + do not have a ReplayGain value calculated. This class will iterate over the + list calculate the values, update the server and repeat the process until + the server reports there are no files left. + + This class will see heavy activity right after a 2.1->2.2 upgrade since 2.2 + introduces ReplayGain normalization. A fresh install of Airtime 2.2 will + see this class not used at all since a file imported in 2.2 will + automatically have its ReplayGain value calculated. + """ + + @staticmethod + def start_reply_gain(apc): + me = ReplayGainUpdater(apc) + me.daemon = True + me.start() + + def __init__(self,apc): + Thread.__init__(self) + self.api_client = apc + self.logger = logging.getLogger() + + def main(self): + raw_response = self.api_client.list_all_watched_dirs() + if 'dirs' not in raw_response: + self.logger.error("Could not get a list of watched directories \ + with a dirs attribute. Printing full request:") + self.logger.error( raw_response ) + return + + directories = raw_response['dirs'] + + for dir_id, dir_path in directories.iteritems(): + try: + # keep getting few rows at a time for current music_dir (stor + # or watched folder). + total = 0 + while True: + # return a list of pairs where the first value is the + # file's database row id and the second value is the + # filepath + files = self.api_client.get_files_without_replay_gain_value(dir_id) + processed_data = [] + for f in files: + full_path = os.path.join(dir_path, f['fp']) + processed_data.append((f['id'], replaygain.calculate_replay_gain(full_path))) + + try: + self.api_client.update_replay_gain_values(processed_data) + except Exception as e: self.unexpected_exception(e) + + if len(files) == 0: break + self.logger.info("Processed: %d songs" % total) + + except Exception, e: + self.logger.error(e) + self.logger.debug(traceback.format_exc()) + def run(self): + try: + while True: + self.logger.info("Runnning replaygain updater") + self.main() + # Sleep for 5 minutes in case new files have been added + time.sleep(60 * 5) + except Exception, e: + self.logger.error('ReplayGainUpdater Exception: %s', traceback.format_exc()) + self.logger.error(e) + +if __name__ == "__main__": + rgu = ReplayGainUpdater() + rgu.main() From fcfa5cceeee3c53d7c381f7ae67c71dc56fa4d45 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 14 Nov 2012 14:49:28 -0500 Subject: [PATCH 23/33] removed useless comment --- python_apps/media-monitor2/mm2.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 2705ef0ee..bee3d7818 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -94,8 +94,6 @@ def main(global_config, api_client_config, log_config, apiclient = apc.AirtimeApiClient.create_right_config(log=log, config_path=api_client_config) - #ReplayGainUpdater.start_reply_gain(apiclient) - manager = Manager() airtime_receiver = AirtimeMessageReceiver(config,manager) From dd139e5028c193edd4ff99d1f05ed5dd3e04a432 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 14 Nov 2012 16:50:34 -0500 Subject: [PATCH 24/33] added default value for exception class parameter --- python_apps/media-monitor2/media/monitor/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/media/monitor/exceptions.py b/python_apps/media-monitor2/media/monitor/exceptions.py index b7b834920..f7d022fb1 100644 --- a/python_apps/media-monitor2/media/monitor/exceptions.py +++ b/python_apps/media-monitor2/media/monitor/exceptions.py @@ -23,7 +23,7 @@ class FailedToObtainLocale(Exception): class CouldNotCreateIndexFile(Exception): """exception whenever index file cannot be created""" - def __init__(self, path, cause): + def __init__(self, path, cause=None): self.path = path self.cause = cause def __str__(self): return "Failed to create touch file '%s'" % self.path From ce4dbf00280e9ec56ba2cc1f62d0f57014e56c3b Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Wed, 14 Nov 2012 22:06:09 -0500 Subject: [PATCH 25/33] added launcher that should replace most of mm2.py --- .../media-monitor2/media/saas/launcher.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 python_apps/media-monitor2/media/saas/launcher.py diff --git a/python_apps/media-monitor2/media/saas/launcher.py b/python_apps/media-monitor2/media/saas/launcher.py new file mode 100644 index 000000000..fd7bacdc7 --- /dev/null +++ b/python_apps/media-monitor2/media/saas/launcher.py @@ -0,0 +1,75 @@ +import os + +from media.saas.thread import InstanceThread, user, apc +from media.monitor.log import Loggable +from media.monitor.exceptions import CouldNotCreateIndexFile +from media.monitor.toucher import ToucherThread +from media.monitor.airtime import AirtimeNotifier, \ + AirtimeMessageReceiver +from media.monitor.watchersyncer import WatchSyncer +from media.monitor.eventdrainer import EventDrainer +from media.monitor.manager import Manager + +class MM2(InstanceThread, Loggable): + + def index_create(self, index_create_attempt=False): + config = user().mm_config + if not index_create_attempt: + if not os.path.exists(config['index_path']): + self.log.info("Attempting to create index file:...") + try: + with open(config['index_path'], 'w') as f: f.write(" ") + except Exception as e: + self.log.info("Failed to create index file with exception: %s" \ + % str(e)) + else: + self.log.info("Created index file, reloading configuration:") + self.index_create(index_create_attempt=True) + else: + self.log.info("Already tried to create index. Will not try again ") + + if not os.path.exists(config['index_path']): + raise CouldNotCreateIndexFile(config['index_path']) + + def run(self): + self.index_create_attempt() + manager = Manager() + apiclient = apc() + config = user().mm_config + watch_syncer = WatchSyncer(signal='watch', + chunking_number=config['chunking_number'], + timeout=config['request_max_wait']) + airtime_receiver = AirtimeMessageReceiver(config,manager) + airtime_notifier = AirtimeNotifier(config, airtime_receiver) + + store = apiclient.setup_media_monitor() + + self.log.info( + "Initing with the following airtime response:%s" % str(store)) + + airtime_receiver.change_storage({ 'directory':store[u'stor'] }) + + for watch_dir in store[u'watched_dirs']: + if not os.path.exists(watch_dir): + # Create the watch_directory here + try: os.makedirs(watch_dir) + except Exception: + self.log.error("Could not create watch directory: '%s' \ + (given from the database)." % watch_dir) + if os.path.exists(watch_dir): + airtime_receiver.new_watch({ 'directory':watch_dir }, restart=True) + else: self.log.info("Failed to add watch on %s" % str(watch_dir)) + + ed = EventDrainer(airtime_notifier.connection, + interval=float(config['rmq_event_wait'])) + + # Launch the toucher that updates the last time when the script was + # 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'], + interval=int(config['touch_interval'])) + + apiclient.register_component('media-monitor') + + return manager.loop() From 7ed1f08e07f882c808cc5964ec4694345233d1ae Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:10:52 -0500 Subject: [PATCH 26/33] Added new launch script for mm. --- python_apps/media-monitor2/mm2.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index bee3d7818..4f36965a3 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -16,6 +16,8 @@ from media.monitor.airtime import AirtimeNotifier, \ from media.monitor.watchersyncer import WatchSyncer from media.monitor.eventdrainer import EventDrainer from std_err_override import LogWriter +from media.saas.launcher import MM2 +from media.saas.airtimeinstance import AirtimeInstance import media.monitor.pure as mmp from api_clients import api_client as apc @@ -47,8 +49,22 @@ def setup_global(log): Logging exception.") log.info(str(e)) +def main(global_config, api_client_config, log_config): + cfg = { + 'api_client' : api_client_config, + 'media_monitor' : global_config, + 'logging' : log_config, + } + ai = AirtimeInstance('hosted_install', '/', cfg) + log = setup_logger( log_config, ai.mm_config['logpath'] ) + setup_global(log) + apc.AirtimeApiClient.create_right_config(log=log, + config_path=api_client_config) + apc.AirtimeApiClient(api_client_config) + mm = MM2(ai) + mm.start() -def main(global_config, api_client_config, log_config, +def main2(global_config, api_client_config, log_config, index_create_attempt=False): for cfg in [global_config, api_client_config]: if not os.path.exists(cfg): raise NoConfigFile(cfg) From ab35263869cbc9cbc7353b000fa5af25f66bcebd Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:11:18 -0500 Subject: [PATCH 27/33] inject apc properly into AirtimeNotifier --- python_apps/media-monitor2/media/monitor/airtime.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/airtime.py b/python_apps/media-monitor2/media/monitor/airtime.py index b920ecd65..11436009a 100644 --- a/python_apps/media-monitor2/media/monitor/airtime.py +++ b/python_apps/media-monitor2/media/monitor/airtime.py @@ -15,7 +15,7 @@ from media.monitor.exceptions import DirectoryIsNotListed from media.monitor.bootstrap import Bootstrapper from media.monitor.listeners import FileMediator -from api_clients import api_client as apc +from media.saas.thread import apc class AirtimeNotifier(Loggable): """ @@ -98,7 +98,7 @@ class AirtimeMessageReceiver(Loggable): if (not directory_id) and (not directory): raise ValueError("You must provide either directory_id or \ directory") - sdb = AirtimeDB(apc.AirtimeApiClient.create_right_config()) + sdb = AirtimeDB(apc()) if directory : directory = os.path.normpath(directory) if directory_id == None : directory_id = sdb.to_id(directory) if directory == None : directory = sdb.to_directory(directory_id) From 15f4212360c4ec9eabe9ae148431b5ac0bd76766 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:11:48 -0500 Subject: [PATCH 28/33] make ManagerTimeout provide the correct AirtimeInstance object. --- python_apps/media-monitor2/media/monitor/manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/manager.py b/python_apps/media-monitor2/media/monitor/manager.py index e64d1c985..89141157e 100644 --- a/python_apps/media-monitor2/media/monitor/manager.py +++ b/python_apps/media-monitor2/media/monitor/manager.py @@ -1,5 +1,4 @@ import pyinotify -import threading import time from pydispatch import dispatcher @@ -9,10 +8,11 @@ from media.monitor.log import Loggable from media.monitor.listeners import StoreWatchListener, OrganizeListener from media.monitor.handler import ProblemFileHandler from media.monitor.organizer import Organizer +from media.saas.thread import InstanceInheritingThread import media.monitor.pure as mmp -class ManagerTimeout(threading.Thread,Loggable): +class ManagerTimeout(InstanceInheritingThread,Loggable): """ The purpose of this class is to flush the organize directory every 3 secnods. This used to be just a work around for cc-4235 but recently became a permanent solution because it's "cheap" and @@ -20,7 +20,7 @@ class ManagerTimeout(threading.Thread,Loggable): def __init__(self, manager, interval=1.5): # TODO : interval should be read from config and passed here instead # of just using the hard coded value - threading.Thread.__init__(self) + super(ManagerTimeout, self).__init__() self.manager = manager self.interval = interval def run(self): From fa66f33ffa74c89b3b0a3ca878320d4307a2ec1a Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:12:19 -0500 Subject: [PATCH 29/33] changed RequestSync to use injected apc and injected it in ThreadedRequestedSync. --- python_apps/media-monitor2/media/monitor/request.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/request.py b/python_apps/media-monitor2/media/monitor/request.py index a49c6bb25..e2c5fb5ef 100644 --- a/python_apps/media-monitor2/media/monitor/request.py +++ b/python_apps/media-monitor2/media/monitor/request.py @@ -1,14 +1,12 @@ # -*- coding: utf-8 -*- -import threading - from media.monitor.exceptions import BadSongFile from media.monitor.log import Loggable -import api_clients.api_client as ac +from media.saas.thread import apc, InstanceInheritingThread -class ThreadedRequestSync(threading.Thread, Loggable): +class ThreadedRequestSync(InstanceInheritingThread, Loggable): def __init__(self, rs): - threading.Thread.__init__(self) + super(ThreadedRequestSync, self).__init__() self.rs = rs self.daemon = True self.start() @@ -22,7 +20,7 @@ class RequestSync(Loggable): for some number of times """ @classmethod def create_with_api_client(cls, watcher, requests): - apiclient = ac.AirtimeApiClient.create_right_config() + apiclient = apc() self = cls(watcher, requests, apiclient) return self From 5073ced732bc0d1cfcfd512a7cf4a6d6d8dbec90 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:12:47 -0500 Subject: [PATCH 30/33] Made timeout watcher provide AirtimeInstnace --- python_apps/media-monitor2/media/monitor/watchersyncer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python_apps/media-monitor2/media/monitor/watchersyncer.py b/python_apps/media-monitor2/media/monitor/watchersyncer.py index d2df9ed3a..a913bb506 100644 --- a/python_apps/media-monitor2/media/monitor/watchersyncer.py +++ b/python_apps/media-monitor2/media/monitor/watchersyncer.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import threading import time import copy @@ -9,15 +8,16 @@ from media.monitor.exceptions import BadSongFile from media.monitor.eventcontractor import EventContractor from media.monitor.events import EventProxy from media.monitor.request import ThreadedRequestSync, RequestSync +from media.saas.thread import InstanceInheritingThread -class TimeoutWatcher(threading.Thread,Loggable): +class TimeoutWatcher(InstanceInheritingThread,Loggable): """ The job of this thread is to keep an eye on WatchSyncer and force a request whenever the requests go over time out """ def __init__(self, watcher, timeout=5): self.logger.info("Created timeout thread...") - threading.Thread.__init__(self) + super(TimeoutWatcher, self).__init__() self.watcher = watcher self.timeout = timeout From 4ac3efe228df28c0442ba9ed2b62e782d1ac1839 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:13:33 -0500 Subject: [PATCH 31/33] renamed typo log to logger. --- .../media-monitor2/media/saas/launcher.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/python_apps/media-monitor2/media/saas/launcher.py b/python_apps/media-monitor2/media/saas/launcher.py index fd7bacdc7..cd35f712e 100644 --- a/python_apps/media-monitor2/media/saas/launcher.py +++ b/python_apps/media-monitor2/media/saas/launcher.py @@ -16,23 +16,23 @@ class MM2(InstanceThread, Loggable): config = user().mm_config if not index_create_attempt: if not os.path.exists(config['index_path']): - self.log.info("Attempting to create index file:...") + self.logger.info("Attempting to create index file:...") try: with open(config['index_path'], 'w') as f: f.write(" ") except Exception as e: - self.log.info("Failed to create index file with exception: %s" \ + self.logger.info("Failed to create index file with exception: %s" \ % str(e)) else: - self.log.info("Created index file, reloading configuration:") + self.logger.info("Created index file, reloading configuration:") self.index_create(index_create_attempt=True) else: - self.log.info("Already tried to create index. Will not try again ") + self.logger.info("Already tried to create index. Will not try again ") if not os.path.exists(config['index_path']): raise CouldNotCreateIndexFile(config['index_path']) def run(self): - self.index_create_attempt() + self.index_create() manager = Manager() apiclient = apc() config = user().mm_config @@ -44,7 +44,7 @@ class MM2(InstanceThread, Loggable): store = apiclient.setup_media_monitor() - self.log.info( + self.logger.info( "Initing with the following airtime response:%s" % str(store)) airtime_receiver.change_storage({ 'directory':store[u'stor'] }) @@ -54,11 +54,11 @@ class MM2(InstanceThread, Loggable): # Create the watch_directory here try: os.makedirs(watch_dir) except Exception: - self.log.error("Could not create watch directory: '%s' \ + self.logger.error("Could not create watch directory: '%s' \ (given from the database)." % watch_dir) if os.path.exists(watch_dir): airtime_receiver.new_watch({ 'directory':watch_dir }, restart=True) - else: self.log.info("Failed to add watch on %s" % str(watch_dir)) + else: self.logger.info("Failed to add watch on %s" % str(watch_dir)) ed = EventDrainer(airtime_notifier.connection, interval=float(config['rmq_event_wait'])) @@ -72,4 +72,4 @@ class MM2(InstanceThread, Loggable): apiclient.register_component('media-monitor') - return manager.loop() + manager.loop() From 8a7535774c508a4877c3e787b6eba1ae2185692d Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:13:52 -0500 Subject: [PATCH 32/33] fixed import typo --- python_apps/media-monitor2/media/saas/airtimeinstance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/media/saas/airtimeinstance.py b/python_apps/media-monitor2/media/saas/airtimeinstance.py index 293fc84c2..2cb2176c5 100644 --- a/python_apps/media-monitor2/media/saas/airtimeinstance.py +++ b/python_apps/media-monitor2/media/saas/airtimeinstance.py @@ -3,7 +3,7 @@ import os from media.monitor.exceptions import NoConfigFile from media.monitor.pure import LazyProperty from media.monitor.config import MMConfig -from api_clients import AirtimeApiClient +from api_clients.api_client import AirtimeApiClient class AirtimeInstance(object): """ AirtimeInstance is a class that abstracts away every airtime From 0b0e96f49c719fba879d33b7241e9ab939b21e34 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@gmail.com> Date: Thu, 15 Nov 2012 12:46:57 -0500 Subject: [PATCH 33/33] removed old runner code --- python_apps/media-monitor2/mm2.py | 82 ------------------------------- 1 file changed, 82 deletions(-) diff --git a/python_apps/media-monitor2/mm2.py b/python_apps/media-monitor2/mm2.py index 4f36965a3..53e5f3c17 100644 --- a/python_apps/media-monitor2/mm2.py +++ b/python_apps/media-monitor2/mm2.py @@ -64,88 +64,6 @@ def main(global_config, api_client_config, log_config): mm = MM2(ai) mm.start() -def main2(global_config, api_client_config, log_config, - index_create_attempt=False): - for cfg in [global_config, api_client_config]: - if not os.path.exists(cfg): raise NoConfigFile(cfg) - # MMConfig is a proxy around ConfigObj instances. it does not allow - # itself users of MMConfig instances to modify any config options - # directly through the dictionary. Users of this object must use the - # correct methods designated for modification - try: config = MMConfig(global_config) - except NoConfigFile as e: - print("Cannot run mediamonitor2 without configuration file.") - print("Current config path: '%s'" % global_config) - sys.exit(1) - except Exception as e: - print("Unknown error reading configuration file: '%s'" % global_config) - print(str(e)) - - log = setup_logger( log_config, config['logpath'] ) - - if not index_create_attempt: - if not os.path.exists(config['index_path']): - log.info("Attempting to create index file:...") - try: - with open(config['index_path'], 'w') as f: f.write(" ") - except Exception as e: - log.info("Failed to create index file with exception: %s" \ - % str(e)) - else: - log.info("Created index file, reloading configuration:") - main( global_config, api_client_config, log_config, - index_create_attempt=True ) - else: - log.info("Already tried to create index. Will not try again ") - - if not os.path.exists(config['index_path']): - log.info("Index file does not exist. Terminating") - - setup_global(log) - - watch_syncer = WatchSyncer(signal='watch', - chunking_number=config['chunking_number'], - timeout=config['request_max_wait']) - - apiclient = apc.AirtimeApiClient.create_right_config(log=log, - config_path=api_client_config) - - manager = Manager() - - airtime_receiver = AirtimeMessageReceiver(config,manager) - airtime_notifier = AirtimeNotifier(config, airtime_receiver) - - store = apiclient.setup_media_monitor() - - log.info("Initing with the following airtime response:%s" % str(store)) - - airtime_receiver.change_storage({ 'directory':store[u'stor'] }) - - for watch_dir in store[u'watched_dirs']: - if not os.path.exists(watch_dir): - # Create the watch_directory here - try: os.makedirs(watch_dir) - except Exception as e: - log.error("Could not create watch directory: '%s' \ - (given from the database)." % watch_dir) - if os.path.exists(watch_dir): - airtime_receiver.new_watch({ 'directory':watch_dir }, restart=True) - else: log.info("Failed to add watch on %s" % str(watch_dir)) - - ed = EventDrainer(airtime_notifier.connection, - interval=float(config['rmq_event_wait'])) - - # Launch the toucher that updates the last time when the script was - # 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'], - interval=int(config['touch_interval'])) - - apiclient.register_component('media-monitor') - - return manager.loop() - __doc__ = """ Usage: mm2.py --config=<path> --apiclient=<path> --log=<path>