diff --git a/python_apps/media-monitor2/media/monitor/listeners.py b/python_apps/media-monitor2/media/monitor/listeners.py index 0d3ff5be6..db1c1cb7b 100644 --- a/python_apps/media-monitor2/media/monitor/listeners.py +++ b/python_apps/media-monitor2/media/monitor/listeners.py @@ -27,6 +27,13 @@ from media.monitor.events import OrganizeFile, NewFile, DeleteFile # because the md5 signature will change... +# Note: Because of the way classes that inherit from pyinotify.ProcessEvent +# interact with constructors. you should only instantiate objects from them +# using keyword arguments. For example: +# Wrong: OrganizeListener('watch_signal') <= wrong +# Right: OrganizeListener(signal='watch_signal') <= right + + class BaseListener(object): def my_init(self, signal): self.signal = signal @@ -63,4 +70,11 @@ class StoreWatchListener(BaseListener, pyinotify.ProcessEvent): def process_delete(self, event): dispatcher.send(signal=self.signal, sender=self, event=DeleteFile(event)) + def flush_events(self, path): + """ + walk over path and send a NewFile event for every file in this directory + """ + # TODO : implement me + pass + diff --git a/python_apps/media-monitor2/media/monitor/manager.py b/python_apps/media-monitor2/media/monitor/manager.py index bc16f84f5..a3ef787ca 100644 --- a/python_apps/media-monitor2/media/monitor/manager.py +++ b/python_apps/media-monitor2/media/monitor/manager.py @@ -11,34 +11,44 @@ class Manager(Loggable): WatchManager from pyinotify """ all_signals = set(['add_watch', 'remove_watch']) - # TODO : get rid of config object being passed? It's not actually being - # used - def __init__(self,config): + def __init__(self): self.wm = pyinotify.WatchManager() # These two instance variables are assumed to be constant self.watch_channel = 'watch' self.organize_channel = 'organize' - self.watch_listener = StoreWatchListener(self.watch_channel) + self.watch_listener = StoreWatchListener(signal=self.watch_channel) self.organize = { 'organize_path' : None, 'store_path' : None, # This guy doesn't need to be changed, always the same. # Gets hooked by wm to different directories - 'organize_listener' : OrganizeListener(self.organize_channel), + 'organize_listener' : OrganizeListener(signal=self.organize_channel), # Also stays the same as long as its target, the directory # which the "organized" files go to, isn't changed. 'organizer' : None, } + # A private mapping path => watch_descriptor + # we use the same dictionary for organize, watch, store wd events. + # this is a little hacky because we are unable to have multiple wd's + # on the same path. + self.__wd_path = {} self.watched_directories = set([]) def __remove_watch(self,path): - old_watch = self.wm.get_wd(path) - if old_watch: # only delete if dir is actually being watched - self.rm_watch(old_watch, rec=True) + if path in self.__wd_path: # only delete if dir is actually being watched + wd = self.__wd_path[path] + self.wm.rm_watch(wd, rec=True) + del(self.__wd_path[path]) + + def __add_watch(self,path,wd): + self.__wd_path[path] = wd.values()[0] def __create_organizer(self, target_path): return Organizer(channel=self.organize_channel,target_path=target_path) + def get_organize_path(self): + return self.organize['organize_path'] + def set_organize_path(self, new_path): # if we are already organizing a particular directory we remove the # watch from it first before organizing another directory @@ -47,8 +57,14 @@ class Manager(Loggable): # the OrganizeListener instance will walk path and dispatch an organize # event for every file in that directory self.organize['organize_listener'].flush_events(new_path) - self.wm.add_watch(new_path, pyinotify.ALL_EVENTS, rec=True, auto_add=True, + wd = self.wm.add_watch(new_path, pyinotify.ALL_EVENTS, rec=True, auto_add=True, proc_fun=self.organize['organize_listener']) + self.__add_watch(new_path, wd) + + organize_path = property(get_organize_path, set_organize_path) + + def get_store_path(self): + return self.organize['store_path'] def set_store_path(self,new_path): """set the directory where organized files go to""" @@ -60,8 +76,14 @@ class Manager(Loggable): # for removing songs in the old store directory from the database # we assume that this is already done for us. self.watch_listener.flush_events(new_path) - self.wm.add_watch(new_path, pyinotify.ALL_EVENTS, rec=True, auto_add=True, + wd = self.wm.add_watch(new_path, pyinotify.ALL_EVENTS, rec=True, auto_add=True, proc_fun=self.watch_listener) + self.__add_watch(new_path, wd) + + store_path = property(get_store_path, set_store_path) + + def has_watch(self, path): + return path in self.__wd_path def add_watch_directory(self, new_dir): if new_dir in self.watched_directories: @@ -69,9 +91,9 @@ class Manager(Loggable): already being watched" % new_dir) else: self.logger.info("Adding watched directory: '%s'" % new_dir) - self.watched_directories.add(new_dir) - self.wm.add_watch(new_dir, pyinotify.ALL_EVENTS, rec=True, auto_add=True, + wd = self.wm.add_watch(new_dir, pyinotify.ALL_EVENTS, rec=True, auto_add=True, proc_fun=self.watch_listener) + self.__add_watch(new_dir, wd) def remove_watch_directory(self, watch_dir): if watch_dir in self.watched_directories: diff --git a/python_apps/media-monitor2/media/monitor/organizer.py b/python_apps/media-monitor2/media/monitor/organizer.py index 8be7d764b..fd02ead83 100644 --- a/python_apps/media-monitor2/media/monitor/organizer.py +++ b/python_apps/media-monitor2/media/monitor/organizer.py @@ -16,7 +16,7 @@ class Organizer(ReportHandler,Loggable): def __init__(self, channel, target_path): self.channel = channel self.target_path = target_path - super(Organizer, self).__init__(signal=self.channel.signal) + super(Organizer, self).__init__(signal=self.channel) def handle(self, sender, event): """Intercept events where a new file has been added to the organize directory and place it in the correct path (starting with self.target_path)""" @@ -29,9 +29,4 @@ class Organizer(ReportHandler,Loggable): # probably general error in mmp.magic.move... except Exception as e: self.report_problem_file(event=event, exception=e) - def flush_events(self, path): - """organize the whole directory at path. (pretty much by doing what - handle does to every file""" - # TODO : implement me - pass diff --git a/python_apps/media-monitor2/tests/test_manager.py b/python_apps/media-monitor2/tests/test_manager.py new file mode 100644 index 000000000..4b000e5b5 --- /dev/null +++ b/python_apps/media-monitor2/tests/test_manager.py @@ -0,0 +1,28 @@ +import unittest +from media.monitor.manager import Manager + +class TestManager(unittest.TestCase): + def setUp(self): + self.opath = "/home/rudi/Airtime/python_apps/media-monitor2/tests/" + self.ppath = "/home/rudi/Airtime/python_apps/media-monitor2/media/" + + def test_init(self): + man = Manager() + self.assertTrue( len(man.watched_directories) == 0 ) + self.assertTrue( man.watch_channel is not None ) + self.assertTrue( man.organize_channel is not None ) + + def test_organize_path(self): + man = Manager() + man.set_organize_path( self.opath ) + self.assertEqual( man.organize_path, self.opath ) + man.set_organize_path( self.ppath ) + self.assertEqual( man.organize_path, self.ppath ) + + def test_store_path(self): + man = Manager() + man.set_store_path( self.opath ) + self.assertEqual( man.store_path, self.opath ) + + +if __name__ == '__main__': unittest.main()