sintonia/python_apps/media-monitor2/media/monitor/manager.py

149 lines
5.8 KiB
Python

import pyinotify
from media.monitor.log import Loggable
from media.monitor.listeners import StoreWatchListener, OrganizeListener
from media.monitor.organizer import Organizer
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
"""
all_signals = set(['add_watch', 'remove_watch'])
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(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(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 = {}
# The following set isn't really necessary anymore. should be
# removed...
self.watched_directories = set([])
def watch_signal(self):
return self.watch_listener.self.signal
def __remove_watch(self,path):
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,listener):
wd = self.wm.add_watch(path, pyinotify.ALL_EVENTS, rec=True, auto_add=True,
proc_fun=listener)
self.__wd_path[path] = wd.values()[0]
def __create_organizer(self, target_path):
"""
private constructor for organizer so that we don't have to repeat
adding the channel/signal as a parameter to the original constructor
every time
"""
return Organizer(channel=self.organize_channel,target_path=target_path)
def get_organize_path(self):
"""
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
"""
# 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'])
self.organize['organize_path'] = new_path
# 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.__add_watch(new_path, self.organize['organize_listener'])
organize_path = property(get_organize_path, set_organize_path)
def get_store_path(self):
"""
returns the store_path (should be accessed through the property usually)
"""
return self.organize['store_path']
def set_store_path(self,new_path):
"""
set the directory where organized files go to
"""
self.__remove_watch(self.organize['store_path'])
self.organize['store_path'] = new_path
self.organize['organizer'] = self.__create_organizer(new_path)
# flush all the files in the new store_directory. this is done so that
# new files are added to the database. Note that we are not responsible
# 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.__add_watch(new_path, self.watch_listener)
store_path = property(get_store_path, set_store_path)
def has_watch(self, path):
"""
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 those that
are being monitored by media monitor for airtime in 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)
else:
self.logger.info("Adding watched directory: '%s'" % new_dir)
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
"""
if self.has_watch(watch_dir):
self.logger.info("Removing watched directory: '%s'", watch_dir)
self.__remove_watch(watch_dir)
else:
self.logger.info("'%s' is not being watched, hence cannot be removed"
% watch_dir)
def pyinotify(self):
return pyinotify.Notifier(self.wm)
def loop(self):
"""
block until we receive pyinotify events
"""
pyinotify.Notifier(self.wm).loop()
#import asyncore
#pyinotify.AsyncNotifier(self.wm).loop()
#asyncore.loop()