92 lines
3.8 KiB
Python
92 lines
3.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import media.monitor.pure as mmp
|
|
import media.monitor.owners as owners
|
|
from media.monitor.handler import ReportHandler
|
|
from media.monitor.log import Loggable
|
|
from media.monitor.exceptions import BadSongFile
|
|
from media.monitor.events import OrganizeFile
|
|
from pydispatch import dispatcher
|
|
from os.path import dirname
|
|
import os.path
|
|
|
|
class Organizer(ReportHandler,Loggable):
|
|
"""
|
|
Organizer is responsible to to listening to OrganizeListener events
|
|
and committing the appropriate changes to the filesystem. It does
|
|
not in any interact with WatchSyncer's even when the the WatchSyncer
|
|
is a "storage directory". The "storage" directory picks up all of
|
|
its events through pyinotify. (These events are fed to it through
|
|
StoreWatchListener)
|
|
"""
|
|
|
|
# Commented out making this class a singleton because it's just a band aid
|
|
# for the real issue. The real issue being making multiple Organizer
|
|
# instances with pydispatch
|
|
|
|
#_instance = None
|
|
#def __new__(cls, channel, target_path, recorded_path):
|
|
#if cls._instance:
|
|
#cls._instance.channel = channel
|
|
#cls._instance.target_path = target_path
|
|
#cls._instance.recorded_path = recorded_path
|
|
#else:
|
|
#cls._instance = super(Organizer, cls).__new__( cls, channel,
|
|
#target_path, recorded_path)
|
|
#return cls._instance
|
|
|
|
def __init__(self, channel, target_path, recorded_path):
|
|
self.channel = channel
|
|
self.target_path = target_path
|
|
self.recorded_path = recorded_path
|
|
super(Organizer, self).__init__(signal=self.channel, weak=False)
|
|
|
|
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)
|
|
"""
|
|
# Only handle this event type
|
|
assert isinstance(event, OrganizeFile), \
|
|
"Organizer can only handle OrganizeFile events.Given '%s'" % event
|
|
try:
|
|
# We must select the target_path based on whether file was recorded
|
|
# by airtime or not.
|
|
# Do we need to "massage" the path using mmp.organized_path?
|
|
target_path = self.recorded_path if event.metadata.is_recorded() \
|
|
else self.target_path
|
|
# nasty hack do this properly
|
|
owner_id = mmp.owner_id(event.path)
|
|
if owner_id != -1:
|
|
target_path = os.path.join(target_path, unicode(owner_id))
|
|
|
|
mdata = event.metadata.extract()
|
|
new_path = mmp.organized_path(event.path, target_path, mdata)
|
|
|
|
# See hack in mmp.magic_move
|
|
def new_dir_watch(d):
|
|
def cb():
|
|
dispatcher.send(signal="add_subwatch", sender=self,
|
|
directory=d)
|
|
return cb
|
|
|
|
mmp.magic_move(event.path, new_path,
|
|
after_dir_make=new_dir_watch(dirname(new_path)))
|
|
|
|
# The reason we need to go around saving the owner in this ass
|
|
# backwards way is bewcause we are unable to encode the owner id
|
|
# into the file itself so that the StoreWatchListener listener can
|
|
# detect it from the file
|
|
owners.add_file_owner(new_path, owner_id )
|
|
|
|
self.logger.info('Organized: "%s" into "%s"' %
|
|
(event.path, new_path))
|
|
except BadSongFile as e:
|
|
self.report_problem_file(event=event, exception=e)
|
|
# probably general error in mmp.magic.move...
|
|
except Exception as e:
|
|
self.unexpected_exception( e )
|
|
self.report_problem_file(event=event, exception=e)
|
|
|