cc-4105: rename watched subdirs barely works
This commit is contained in:
parent
632f2ab470
commit
1e7de08bf3
|
@ -2,8 +2,10 @@
|
||||||
import os
|
import os
|
||||||
import abc
|
import abc
|
||||||
from media.monitor.pure import LazyProperty
|
from media.monitor.pure import LazyProperty
|
||||||
|
import media.monitor.pure as mmp
|
||||||
from media.monitor.metadata import Metadata
|
from media.monitor.metadata import Metadata
|
||||||
from media.monitor.log import Loggable
|
from media.monitor.log import Loggable
|
||||||
|
from media.monitor.exceptions import BadSongFile
|
||||||
|
|
||||||
class PathChannel(object):
|
class PathChannel(object):
|
||||||
"""a dumb struct; python has no record types"""
|
"""a dumb struct; python has no record types"""
|
||||||
|
@ -56,13 +58,26 @@ class BaseEvent(Loggable):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Event. Path: %s" % self.__raw_event.pathname
|
return "Event. Path: %s" % self.__raw_event.pathname
|
||||||
|
|
||||||
|
def is_dir_event(self):
|
||||||
|
return self._raw_event.dir
|
||||||
|
|
||||||
# nothing to see here, please move along
|
# nothing to see here, please move along
|
||||||
def morph_into(self, evt):
|
def morph_into(self, evt):
|
||||||
|
"""
|
||||||
|
'Morphing' should preserve the self.cookie invariant. I.e. either
|
||||||
|
None -> None or int -> int
|
||||||
|
"""
|
||||||
self.logger.info("Morphing '%s' into '%s'" % (self.__class__.__name__,
|
self.logger.info("Morphing '%s' into '%s'" % (self.__class__.__name__,
|
||||||
evt.__class__.__name__))
|
evt.__class__.__name__))
|
||||||
self._raw_event = evt
|
self._raw_event = evt
|
||||||
self.path = evt.path
|
self.path = evt.path
|
||||||
self.__class__ = evt.__class__
|
self.__class__ = evt.__class__
|
||||||
|
return self
|
||||||
|
|
||||||
|
class FakePyinotify(object):
|
||||||
|
def __init__(self, path):
|
||||||
|
self.pathname = path
|
||||||
|
|
||||||
|
|
||||||
class OrganizeFile(BaseEvent, HasMetaData):
|
class OrganizeFile(BaseEvent, HasMetaData):
|
||||||
def __init__(self, *args, **kwargs): super(OrganizeFile, self).__init__(*args, **kwargs)
|
def __init__(self, *args, **kwargs): super(OrganizeFile, self).__init__(*args, **kwargs)
|
||||||
|
@ -78,7 +93,7 @@ class NewFile(BaseEvent, HasMetaData):
|
||||||
req_dict = self.metadata.extract()
|
req_dict = self.metadata.extract()
|
||||||
req_dict['mode'] = u'create'
|
req_dict['mode'] = u'create'
|
||||||
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
||||||
return req_dict
|
return [req_dict]
|
||||||
|
|
||||||
class DeleteFile(BaseEvent):
|
class DeleteFile(BaseEvent):
|
||||||
def __init__(self, *args, **kwargs): super(DeleteFile, self).__init__(*args, **kwargs)
|
def __init__(self, *args, **kwargs): super(DeleteFile, self).__init__(*args, **kwargs)
|
||||||
|
@ -86,7 +101,7 @@ class DeleteFile(BaseEvent):
|
||||||
req_dict = {}
|
req_dict = {}
|
||||||
req_dict['mode'] = u'delete'
|
req_dict['mode'] = u'delete'
|
||||||
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
||||||
return req_dict
|
return [req_dict]
|
||||||
|
|
||||||
class MoveFile(BaseEvent, HasMetaData):
|
class MoveFile(BaseEvent, HasMetaData):
|
||||||
"""Path argument should be the new path of the file that was moved"""
|
"""Path argument should be the new path of the file that was moved"""
|
||||||
|
@ -96,15 +111,34 @@ class MoveFile(BaseEvent, HasMetaData):
|
||||||
req_dict['mode'] = u'moved'
|
req_dict['mode'] = u'moved'
|
||||||
req_dict['MDATA_KEY_MD5'] = self.metadata.extract()['MDATA_KEY_MD5']
|
req_dict['MDATA_KEY_MD5'] = self.metadata.extract()['MDATA_KEY_MD5']
|
||||||
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
||||||
return req_dict
|
return [req_dict]
|
||||||
|
|
||||||
|
def map_events(directory, constructor):
|
||||||
|
#return map(lambda f: constructor( FakePyinotify(f) ).pack(),
|
||||||
|
#mmp.walk_supported(directory.replace("-unknown-path",""),
|
||||||
|
#clean_empties=False))
|
||||||
|
for f in mmp.walk_supported(directory.replace("-unknown-path",""),
|
||||||
|
clean_empties=True):
|
||||||
|
try: yield constructor( FakePyinotify(f) ).pack()[0]
|
||||||
|
except BadSongFile as e: yield e
|
||||||
|
|
||||||
class DeleteDir(BaseEvent):
|
class DeleteDir(BaseEvent):
|
||||||
def __init__(self, *args, **kwargs): super(DeleteDir, self).__init__(*args, **kwargs)
|
def __init__(self, *args, **kwargs): super(DeleteDir, self).__init__(*args, **kwargs)
|
||||||
|
def pack(self):
|
||||||
|
return map_events( self.path, DeleteFile )
|
||||||
|
|
||||||
|
class MoveDir(BaseEvent):
|
||||||
|
def __init__(self, *args, **kwargs): super(MoveDir, self).__init__(*args, **kwargs)
|
||||||
|
def pack(self):
|
||||||
|
return map_events( self.path, MoveFile )
|
||||||
|
|
||||||
|
class DeleteDirWatch(BaseEvent):
|
||||||
|
def __init__(self, *args, **kwargs): super(DeleteDirWatch, self).__init__(*args, **kwargs)
|
||||||
def pack(self):
|
def pack(self):
|
||||||
req_dict = {}
|
req_dict = {}
|
||||||
req_dict['mode'] = u'delete_dir'
|
req_dict['mode'] = u'delete_dir'
|
||||||
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
||||||
return req_dict
|
return [req_dict]
|
||||||
|
|
||||||
class ModifyFile(BaseEvent, HasMetaData):
|
class ModifyFile(BaseEvent, HasMetaData):
|
||||||
def __init__(self, *args, **kwargs): super(ModifyFile, self).__init__(*args, **kwargs)
|
def __init__(self, *args, **kwargs): super(ModifyFile, self).__init__(*args, **kwargs)
|
||||||
|
@ -113,5 +147,5 @@ class ModifyFile(BaseEvent, HasMetaData):
|
||||||
req_dict['mode'] = u'modify'
|
req_dict['mode'] = u'modify'
|
||||||
# path to directory that is to be removed
|
# path to directory that is to be removed
|
||||||
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
req_dict['MDATA_KEY_FILEPATH'] = unicode( self.path )
|
||||||
return req_dict
|
return [req_dict]
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ from pydispatch import dispatcher
|
||||||
import media.monitor.pure as mmp
|
import media.monitor.pure as mmp
|
||||||
from media.monitor.pure import IncludeOnly
|
from media.monitor.pure import IncludeOnly
|
||||||
from media.monitor.events import OrganizeFile, NewFile, MoveFile, DeleteFile, \
|
from media.monitor.events import OrganizeFile, NewFile, MoveFile, DeleteFile, \
|
||||||
ModifyFile, DeleteDir, EventRegistry
|
ModifyFile, DeleteDir, EventRegistry, MoveDir,\
|
||||||
|
DeleteDirWatch
|
||||||
from media.monitor.log import Loggable, get_logger
|
from media.monitor.log import Loggable, get_logger
|
||||||
|
|
||||||
# We attempt to document a list of all special cases and hacks that the
|
# We attempt to document a list of all special cases and hacks that the
|
||||||
|
@ -103,35 +104,47 @@ class OrganizeListener(BaseListener, pyinotify.ProcessEvent, Loggable):
|
||||||
dispatcher.send(signal=self.signal, sender=self, event=OrganizeFile(event))
|
dispatcher.send(signal=self.signal, sender=self, event=OrganizeFile(event))
|
||||||
|
|
||||||
class StoreWatchListener(BaseListener, Loggable, pyinotify.ProcessEvent):
|
class StoreWatchListener(BaseListener, Loggable, pyinotify.ProcessEvent):
|
||||||
|
# TODO : must intercept DeleteDirWatch events somehow
|
||||||
def process_IN_CLOSE_WRITE(self, event): self.process_create(event)
|
def process_IN_CLOSE_WRITE(self, event): self.process_create(event)
|
||||||
def process_IN_MOVED_TO(self, event):
|
def process_IN_MOVED_TO(self, event):
|
||||||
if EventRegistry.registered(event):
|
if EventRegistry.registered(event):
|
||||||
EventRegistry.matching(event).morph_into(MoveFile(event))
|
# We need this trick because we don't how to "expand" dir events
|
||||||
|
# into file events until we know for sure if we deleted or moved
|
||||||
|
morph = MoveDir(event) if event.dir else MoveFile(event)
|
||||||
|
EventRegistry.matching(event).morph_into(morph)
|
||||||
else: self.process_create(event)
|
else: self.process_create(event)
|
||||||
def process_IN_MOVED_FROM(self, event):
|
def process_IN_MOVED_FROM(self, event):
|
||||||
# Is either delete dir or delete file
|
# Is either delete dir or delete file
|
||||||
evt = self.process_delete_dir(event) if event.dir else self.process_delete(event)
|
evt = self.process_delete(event)
|
||||||
if hasattr(event,'cookie'): EventRegistry.register(evt)
|
if hasattr(event,'cookie'): EventRegistry.register(evt)
|
||||||
def process_IN_DELETE(self,event): self.process_delete(event)
|
def process_IN_DELETE(self,event): self.process_delete(event)
|
||||||
# Capturing modify events is too brittle and error prone
|
# Capturing modify events is too brittle and error prone
|
||||||
# def process_IN_MODIFY(self,event): self.process_modify(event)
|
# def process_IN_MODIFY(self,event): self.process_modify(event)
|
||||||
|
|
||||||
|
# TODO : Remove this code. Later decided we will ignore modify events
|
||||||
|
# since it's too difficult to tell which ones should be handled. Much
|
||||||
|
# easier to just intercept IN_CLOSE_WRITE and decide what to do on the php
|
||||||
|
# side
|
||||||
@mediate_ignored
|
@mediate_ignored
|
||||||
@IncludeOnly(mmp.supported_extensions)
|
@IncludeOnly(mmp.supported_extensions)
|
||||||
def process_modify(self, event):
|
def process_modify(self, event):
|
||||||
FileMediator.skip_next('IN_MODIFY','IN_CLOSE_WRITE',key='maskname')
|
FileMediator.skip_next('IN_MODIFY','IN_CLOSE_WRITE',key='maskname')
|
||||||
dispatcher.send(signal=self.signal, sender=self, event=ModifyFile(event))
|
evt = ModifyFile(event)
|
||||||
|
dispatcher.send(signal=self.signal, sender=self, event=evt)
|
||||||
|
|
||||||
@mediate_ignored
|
@mediate_ignored
|
||||||
@IncludeOnly(mmp.supported_extensions)
|
@IncludeOnly(mmp.supported_extensions)
|
||||||
def process_create(self, event):
|
def process_create(self, event):
|
||||||
dispatcher.send(signal=self.signal, sender=self, event=NewFile(event))
|
evt = NewFile(event)
|
||||||
|
dispatcher.send(signal=self.signal, sender=self, event=evt)
|
||||||
|
return evt
|
||||||
|
|
||||||
@mediate_ignored
|
@mediate_ignored
|
||||||
@IncludeOnly(mmp.supported_extensions)
|
@IncludeOnly(mmp.supported_extensions)
|
||||||
def process_delete(self, event):
|
def process_delete(self, event):
|
||||||
evt = DeleteFile(event)
|
evt = None
|
||||||
|
if event.dir: evt = DeleteDir(event)
|
||||||
|
else: evt = DeleteFile(event)
|
||||||
dispatcher.send(signal=self.signal, sender=self, event=evt)
|
dispatcher.send(signal=self.signal, sender=self, event=evt)
|
||||||
return evt
|
return evt
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,9 @@ class IncludeOnly(object):
|
||||||
def __call__(self, func):
|
def __call__(self, func):
|
||||||
def _wrap(moi, event, *args, **kwargs):
|
def _wrap(moi, event, *args, **kwargs):
|
||||||
ext = extension(event.pathname)
|
ext = extension(event.pathname)
|
||||||
if ext in self.exts: return func(moi, event, *args, **kwargs)
|
# Checking for emptiness b/c we don't want to skip direcotries
|
||||||
|
if (ext in self.exts) or event.dir:
|
||||||
|
return func(moi, event, *args, **kwargs)
|
||||||
return _wrap
|
return _wrap
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,12 @@ class RequestSync(threading.Thread,Loggable):
|
||||||
# A simplistic request would like:
|
# A simplistic request would like:
|
||||||
# TODO : recorded shows aren't flagged right
|
# TODO : recorded shows aren't flagged right
|
||||||
packed_requests = []
|
packed_requests = []
|
||||||
for request in self.requests:
|
for request_event in self.requests:
|
||||||
try: packed_requests.append(request.pack())
|
try:
|
||||||
|
for request in request_event.pack():
|
||||||
|
if isinstance(request, BadSongFile):
|
||||||
|
self.logger.info("Bad song file: '%s'" % request.path)
|
||||||
|
else: packed_requests.append(request)
|
||||||
except BadSongFile as e:
|
except BadSongFile as e:
|
||||||
self.logger.info("Bad song file: '%s'" % e.path)
|
self.logger.info("Bad song file: '%s'" % e.path)
|
||||||
self.logger.info("TODO : put in ignore list")
|
self.logger.info("TODO : put in ignore list")
|
||||||
|
@ -53,7 +57,7 @@ class RequestSync(threading.Thread,Loggable):
|
||||||
self.logger.info("Trying again after %f seconds" % self.request_wait)
|
self.logger.info("Trying again after %f seconds" % self.request_wait)
|
||||||
time.sleep( self.request_wait )
|
time.sleep( self.request_wait )
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.unexpected_exception(e)
|
self.unexpected_exception(e)
|
||||||
else:
|
else:
|
||||||
self.logger.info("Request worked on the '%d' try" % (try_index + 1))
|
self.logger.info("Request worked on the '%d' try" % (try_index + 1))
|
||||||
break
|
break
|
||||||
|
|
Loading…
Reference in New Issue