diff --git a/FSStorage.py b/FSStorage.py index db2621a..1d33613 100644 --- a/FSStorage.py +++ b/FSStorage.py @@ -4,6 +4,7 @@ # $Id$ # +import FileDIDL import errno import itertools import os @@ -79,27 +80,7 @@ class FSItem(FSObject, Item): 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): +def defFS(path, fobj): if os.path.isdir(path): # new dir return FSDirectory, { 'path': path } @@ -110,30 +91,24 @@ def defFS(path): 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 + klass, mt = FileDIDL.buildClassMT(FSItem, path) return klass, { 'path': path, 'mimetype': mt } def dofileadd(cd, parent, path, name): klass = None + try: + fobj = open(path) + except: + fobj = None for i in itertools.chain(klassfuns, ( defFS, )): try: - klass, kwargs = i(os.path.join(path, name)) + try: + fobj.seek(0) # incase the call expects a clean file + except: + pass + + klass, kwargs = i(os.path.join(path, name), fobj) if klass is not None: break except: diff --git a/FileDIDL.py b/FileDIDL.py new file mode 100644 index 0000000..6a9e3f0 --- /dev/null +++ b/FileDIDL.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# Copyright 2006 John-Mark Gurney +# +# $Id$ +# + +# +# Convert file information into a DIDL class. Dynamicly generate a new class +# from a base class and the DIDL class to be determined. +# + +__all__ = [ 'mimetoclass', 'buildClassMT', 'getClassMT', ] + +import os.path +import weakref +from DIDLLite import VideoItem, AudioItem, TextItem, ImageItem +from twisted.python import log +from twisted.web import static + +mimedict = static.loadMimeTypes() +classdict = weakref.WeakValueDictionary() + +mimetoclass = { + 'application/ogg': AudioItem, + 'video': VideoItem, + 'audio': AudioItem, + 'text': TextItem, + 'image': ImageItem, +} + +def getClassMT(name, mimetype = None, fp = None): + '''Return a tuple of the DIDLLite class and mimetype responsible for the named/mimetyped/fpd file.''' + if mimetype is None: + fn, ext = os.path.splitext(name) + ext = ext.lower() + try: + mimetype = mimedict[ext] + except KeyError: + log.msg('no mime-type for: %s' % name) + return None, None + + ty = mimetype.split('/')[0] + if mimetoclass.has_key(mimetype): + klass = mimetoclass[mimetype] + elif mimetoclass.has_key(ty): + klass = mimetoclass[ty] + else: + # XXX - We could fall file -i on it + log.msg('no item for mimetype: %s' % mimetype) + return None, None + + return klass, mimetype + +def buildClassMT(baseklass, name, *args, **kwargs): + klass, mt = getClassMT(name, *args, **kwargs) + + if klass is None: + return None, None + + try: + return classdict[(baseklass, klass)], mt + except KeyError: + pass + + class ret(baseklass, klass): + pass + ret.__name__ = '+'.join(map(lambda x: '%s.%s' % (x.__module__, x.__name__), (baseklass, klass))) + + classdict[(baseklass, klass)] = ret + + return ret, mt diff --git a/ZipStorage.py b/ZipStorage.py new file mode 100644 index 0000000..cb26fb4 --- /dev/null +++ b/ZipStorage.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# Copyright 2006 John-Mark Gurney +# +# $Id$ +# + +import os.path +import zipfile + +from DIDLLite import StorageFolder, Item, VideoItem, AudioItem, TextItem, ImageItem, Resource +from FSStorage import FSObject, registerklassfun + +def isatpath(f, p): + if f[:len(p)] != p: + # First part of path doesn't match, it's not + return False + + slash = f[len(p):].find('/') + if slash != -1 and slash != len(f) - len(p) + 1: + # Another path component, skip it, as long as it's not the char + return False + + # otherwise, it is at this level + endtrim = len(f) + if slash: + endtrip -= 1 + + return f[len(p):endtrim] + +class ZipChildDir(StorageFolder): + '''This is to represent a child dir of the zip file.''' + +class ZipFile(FSObject, StorageFolder): + def __init__(self, *args, **kwargs): + '''If a zip argument is passed it, use that as the zip archive.''' + path = kwargs['path'] + del kwargs['path'] + + StorageFolder.__init__(self, *args, **kwargs) + FSObject.__init__(self, path) + + # mapping from path to objectID + self.pathObjmap = {} + + def getdirents(path): + '''Returns the list of entires for the path, '' is the root path.''' + lst = self.zip.namelist() + if path: + path += '/' + + return filter(lambda x, p = path: isatpath(x, p), lst) + + def doUpdate(self): + # open the zipfile as necessary. + self.zip = zipfile.ZipFile(self.FSpath) + + allzipfiles = sets.Set(self.zip.namelist()) + children = sets.Set(getdirents('')) + 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 + if i not in allzipfiles: + # must be a dir + else: + ZipChild(self, i) + +def detectzipfile(path, fobj): + try: + z = zipfile.ZipFile(fobj) + except: + return None, None + + return ZipFile, {} + +registerklassfun(detectzipfile)