#!/usr/bin/env python # Copyright 2006 John-Mark Gurney # # $Id$ # import errno import itertools import os import sets import stat from DIDLLite import StorageFolder, Item, VideoItem, AudioItem, TextItem, ImageItem, Resource from twisted.web import static from twisted.python import log __all__ = [ 'registerklassfun', 'FSObject', 'FSItem', 'FSVideoItem', 'FSAudioItem', 'FSTextItem', 'FSImageItem', 'mimetoklass', 'FSDirectory', ] mimedict = static.loadMimeTypes() klassfuns = [] def registerklassfun(fun): klassfuns.append(fun) def statcmp(a, b, cmpattrs = [ 'st_ino', 'st_dev', 'st_size', 'st_mtime', ]): if a is None or b is None: return False for i in cmpattrs: if getattr(a, i) != getattr(b, i): return False return True class FSObject(object): def __init__(self, path): self.FSpath = path self.pstat = None def checkUpdate(self): # need to handle no such file or directory # push it up? but still need to handle disappearing try: nstat = os.stat(self.FSpath) if statcmp(self.pstat, nstat): return self self.pstat = nstat self.doUpdate() except OSError, x: log.msg('os.stat, OSError: %s' % x) if x.errno in (errno.ENOENT, errno.ENOTDIR, errno.EPERM, ): # We can't access it anymore, delete it self.cd.delItem(self.id) return None else: raise x return self def doUpdate(self): raise NotImplementedError class FSItem(FSObject, Item): def __init__(self, *args, **kwargs): FSObject.__init__(self, kwargs['path']) del kwargs['path'] mimetype = kwargs['mimetype'] del kwargs['mimetype'] kwargs['content'] = static.File(self.FSpath) Item.__init__(self, *args, **kwargs) self.url = '%s/%s' % (self.cd.urlbase, self.id) self.mimetype = mimetype def doUpdate(self): self.res = Resource(self.url, 'http-get:*:%s:*' % self.mimetype) self.res.size = os.path.getsize(self.FSpath) Item.doUpdate(self) class FSVideoItem(FSItem, VideoItem): pass class FSAudioItem(FSItem, AudioItem): pass class FSTextItem(FSItem, TextItem): pass class FSImageItem(FSItem, ImageItem): pass mimetoklass = { 'application/ogg': FSAudioItem, 'video': FSVideoItem, 'audio': FSAudioItem, 'text': FSTextItem, 'image': FSImageItem, } def defFS(path): if os.path.isdir(path): # new dir return FSDirectory, { 'path': path } elif os.path.isfile(path): # new file - fall through to below pass else: log.msg('skipping (not dir or reg): %s' % path) return None, None fn, ext = os.path.splitext(path) ext = ext.lower() try: mt = mimedict[ext] except KeyError: log.msg('no mime-type for: %s' % path) return None, None ty = mt.split('/')[0] if mimetoklass.has_key(mt): klass = mimetoklass[mt] elif mimetoklass.has_key(ty): klass = mimetoklass[ty] else: log.msg('no item for mt: %s' % mt) return None, None return klass, { 'path': path, 'mimetype': mt } def dofileadd(cd, parent, path, name): klass = None for i in itertools.chain(klassfuns, ( defFS, )): try: klass, kwargs = i(os.path.join(path, name)) if klass is not None: break except: import traceback traceback.print_exc() if klass is None: return log.msg('matched:', os.path.join(path, name), `i`, `klass`) return cd.addItem(parent, klass, name, **kwargs) class FSDirectory(FSObject, StorageFolder): def __init__(self, *args, **kwargs): path = kwargs['path'] del kwargs['path'] StorageFolder.__init__(self, *args, **kwargs) FSObject.__init__(self, path) # mapping from path to objectID self.pathObjmap = {} def doUpdate(self): # We need to rescan this dir, and see if our children has # changed any. children = sets.Set(os.listdir(self.FSpath)) for i in self.pathObjmap.keys(): if i not in children: # delete self.cd.delItem(self.pathObjmap[i]) del self.pathObjmap[i] for i in children: if i in self.pathObjmap: continue # new object nf = dofileadd(self.cd, self.id, self.FSpath, i) if nf is not None: self.pathObjmap[i] = nf # sort our children self.sort(lambda x, y: cmp(x.title, y.title)) # Pass up to handle UpdateID StorageFolder.doUpdate(self)