rid of a lot of cut and paste... add a new module that uses XML to create a custom object... One example is to support a simple mp3 streaming website: <?xml version="1.0"?> <Object> <mimetype>audio/mpeg</mimetype> <res> <url>http://streaming.uoregon.edu:8000/</url> </res> </Object> Should be expanded to support more, but this is a good first cut... [git-p4: depot-paths = "//depot/": change = 1345]main
| @@ -2,7 +2,7 @@ | |||||
| # http://opensource.org/licenses/mit-license.php | # http://opensource.org/licenses/mit-license.php | ||||
| # Copyright 2005, Tim Potter <tpot@samba.org> | # Copyright 2005, Tim Potter <tpot@samba.org> | ||||
| # Copyright 2006-2008 John-Mark Gurney <jmg@funkthat.com> | |||||
| # Copyright 2006-2009 John-Mark Gurney <jmg@funkthat.com> | |||||
| __version__ = '$Change$' | __version__ = '$Change$' | ||||
| # $Id$ | # $Id$ | ||||
| @@ -319,13 +319,99 @@ class Container(Object, list): | |||||
| Object.__init__(self, cd, id, parentID, title, restricted, | Object.__init__(self, cd, id, parentID, title, restricted, | ||||
| creator, **kwargs) | creator, **kwargs) | ||||
| list.__init__(self) | list.__init__(self) | ||||
| self.doingUpdate = False | |||||
| self.oldchildren = {} | |||||
| def genCurrent(self): | |||||
| '''This function returns a tuple/list/generator that returns | |||||
| tuples of (id, name). name must match what is returned by | |||||
| genChildren. If name is used for the title directly, no | |||||
| override is necessary.''' | |||||
| return ((x.id, x.title) for x in self) | |||||
| def genChildren(self): | |||||
| '''This function returns a list or dict of names for new | |||||
| children.''' | |||||
| raise NotImplementedError | |||||
| def createObject(self, i, arg=None): | |||||
| '''This function returns the (class, name, *args, **kwargs) | |||||
| that will be passed to the addItem method of the | |||||
| ContentDirectory. arg will be passed the value of the dict | |||||
| keyed by i if genChildren is a dict.''' | |||||
| raise NotImplementedError | |||||
| def sort(self, fun=None): | |||||
| if fun is None: | |||||
| return list.sort(self, lambda x, y: cmp(x.title, | |||||
| y.title)) | |||||
| return list.sort(self, fun) | |||||
| def doUpdate(self): | def doUpdate(self): | ||||
| if self.doingUpdate: | |||||
| return | |||||
| self.doingUpdate = True | |||||
| didupdate = False | |||||
| children = self.genChildren() | |||||
| if isinstance(children, dict): | |||||
| oldchildren = self.oldchildren | |||||
| self.oldchildren = children | |||||
| isdict = True | |||||
| else: | |||||
| children = set(children) | |||||
| isdict = False | |||||
| names = {} | |||||
| #print 'i:', `self`, `self.genCurrent`, `self.__class__` | |||||
| for id, i in tuple(self.genCurrent()): | |||||
| if i not in children: | |||||
| didupdate = True | |||||
| # delete | |||||
| self.cd.delItem(id) | |||||
| else: | |||||
| names[i] = id | |||||
| for i in children: | |||||
| if i in names: | |||||
| if isdict: | |||||
| if oldchildren[i] == children[i]: | |||||
| continue | |||||
| self.cd.delItem(names[i]) | |||||
| else: | |||||
| # XXX - some sort of comparision? | |||||
| continue | |||||
| # new object | |||||
| if isdict: | |||||
| args = (children[i], ) | |||||
| else: | |||||
| args = () | |||||
| #print 'i:', `i`, `isdict`, `args`, `self` | |||||
| klass, name, args, kwargs = self.createObject(i, *args) | |||||
| if klass is not None: | |||||
| self.cd.addItem(self.id, klass, name, *args, | |||||
| **kwargs) | |||||
| didupdate = True | |||||
| # sort our children | |||||
| self.sort() | |||||
| if didupdate: | |||||
| self.didUpdate() | |||||
| self.doingUpdate = False | |||||
| def didUpdate(self): | |||||
| if self.id == '0': | if self.id == '0': | ||||
| self.updateID = (self.updateID + 1) | self.updateID = (self.updateID + 1) | ||||
| else: | else: | ||||
| self.updateID = (self.updateID + 1) % (1l << 32) | self.updateID = (self.updateID + 1) % (1l << 32) | ||||
| Container.doUpdate(self.cd['0']) | |||||
| Container.didUpdate(self.cd['0']) | |||||
| def toElement(self): | def toElement(self): | ||||
| @@ -1,5 +1,5 @@ | |||||
| #!/usr/bin/env python | #!/usr/bin/env python | ||||
| # Copyright 2006-2008 John-Mark Gurney <jmg@funkthat.com> | |||||
| # Copyright 2006-2009 John-Mark Gurney <jmg@funkthat.com> | |||||
| __version__ = '$Change$' | __version__ = '$Change$' | ||||
| # $Id$ | # $Id$ | ||||
| @@ -13,7 +13,7 @@ import os | |||||
| import sets | import sets | ||||
| import stat | import stat | ||||
| from DIDLLite import Container, StorageFolder, Item, VideoItem, AudioItem, TextItem, ImageItem, Resource, ResourceList | |||||
| from DIDLLite import StorageFolder, Item, Resource, ResourceList | |||||
| from twisted.web import resource, server, static | from twisted.web import resource, server, static | ||||
| from twisted.python import log | from twisted.python import log | ||||
| from twisted.internet import abstract, interfaces, process, protocol, reactor | from twisted.internet import abstract, interfaces, process, protocol, reactor | ||||
| @@ -21,8 +21,6 @@ from zope.interface import implements | |||||
| __all__ = [ 'registerklassfun', 'registerfiletoignore', | __all__ = [ 'registerklassfun', 'registerfiletoignore', | ||||
| 'FSObject', 'FSItem', 'FSDirectory', | 'FSObject', 'FSItem', 'FSDirectory', | ||||
| 'FSVideoItem', 'FSAudioItem', 'FSTextItem', 'FSImageItem', | |||||
| 'mimetoklass', | |||||
| ] | ] | ||||
| mimedict = static.loadMimeTypes() | mimedict = static.loadMimeTypes() | ||||
| @@ -59,7 +57,7 @@ class FSObject(object): | |||||
| self.FSpath = path | self.FSpath = path | ||||
| self.pstat = None | self.pstat = None | ||||
| def checkUpdate(self): | |||||
| def checkUpdate(self, **kwargs): | |||||
| # need to handle no such file or directory | # need to handle no such file or directory | ||||
| # push it up? but still need to handle disappearing | # push it up? but still need to handle disappearing | ||||
| try: | try: | ||||
| @@ -68,7 +66,7 @@ class FSObject(object): | |||||
| return | return | ||||
| self.pstat = nstat | self.pstat = nstat | ||||
| self.doUpdate() | |||||
| self.doUpdate(**kwargs) | |||||
| except OSError, x: | except OSError, x: | ||||
| log.msg('os.stat, OSError: %s' % x) | log.msg('os.stat, OSError: %s' % x) | ||||
| if x.errno in (errno.ENOENT, errno.ENOTDIR, errno.EPERM, ): | if x.errno in (errno.ENOENT, errno.ENOTDIR, errno.EPERM, ): | ||||
| @@ -78,10 +76,6 @@ class FSObject(object): | |||||
| else: | else: | ||||
| raise | raise | ||||
| def doUpdate(self): | |||||
| raise NotImplementedError | |||||
| def __repr__(self): | def __repr__(self): | ||||
| return '<%s.%s: path: %s, id: %s, parent: %s, title: %s>' % \ | return '<%s.%s: path: %s, id: %s, parent: %s, title: %s>' % \ | ||||
| (self.__class__.__module__, self.__class__.__name__, | (self.__class__.__module__, self.__class__.__name__, | ||||
| @@ -189,10 +183,8 @@ class DynamicTrans(resource.Resource): | |||||
| class FSItem(FSObject, Item): | class FSItem(FSObject, Item): | ||||
| def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | ||||
| FSObject.__init__(self, kwargs['path']) | |||||
| del kwargs['path'] | |||||
| mimetype = kwargs['mimetype'] | |||||
| del kwargs['mimetype'] | |||||
| FSObject.__init__(self, kwargs.pop('path')) | |||||
| mimetype = kwargs.pop('mimetype') | |||||
| kwargs['content'] = DynamicTrans(self.FSpath, | kwargs['content'] = DynamicTrans(self.FSpath, | ||||
| static.File(self.FSpath, mimetype)) | static.File(self.FSpath, mimetype)) | ||||
| Item.__init__(self, *args, **kwargs) | Item.__init__(self, *args, **kwargs) | ||||
| @@ -238,7 +230,7 @@ def defFS(path, fobj): | |||||
| return klass, { 'path': path, 'mimetype': mt } | return klass, { 'path': path, 'mimetype': mt } | ||||
| def dofileadd(cd, parent, path, name): | |||||
| def dofileadd(path, name): | |||||
| klass = None | klass = None | ||||
| fsname = os.path.join(path, name) | fsname = os.path.join(path, name) | ||||
| try: | try: | ||||
| @@ -262,10 +254,10 @@ def dofileadd(cd, parent, path, name): | |||||
| pass | pass | ||||
| if klass is None or klass is IgnoreFile: | if klass is None or klass is IgnoreFile: | ||||
| return | |||||
| return None, None, None, None | |||||
| #print 'matched:', os.path.join(path, name), `i`, `klass` | #print 'matched:', os.path.join(path, name), `i`, `klass` | ||||
| return cd.addItem(parent, klass, name, **kwargs) | |||||
| return klass, name, (), kwargs | |||||
| class FSDirectory(FSObject, StorageFolder): | class FSDirectory(FSObject, StorageFolder): | ||||
| def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | ||||
| @@ -274,48 +266,14 @@ class FSDirectory(FSObject, StorageFolder): | |||||
| StorageFolder.__init__(self, *args, **kwargs) | StorageFolder.__init__(self, *args, **kwargs) | ||||
| FSObject.__init__(self, path) | FSObject.__init__(self, path) | ||||
| # mapping from path to objectID | |||||
| self.pathObjmap = {} | |||||
| self.indoUpdate = False | |||||
| def genCurrent(self): | |||||
| return ((x.id, os.path.basename(x.FSpath)) for x in self ) | |||||
| def doUpdate(self): | |||||
| # We need to rescan this dir, and see if our children has | |||||
| # changed any. | |||||
| if self.indoUpdate: | |||||
| return | |||||
| #import traceback | |||||
| #traceback.print_stack() | |||||
| self.indoUpdate = True | |||||
| doupdate = False | |||||
| children = sets.Set(os.listdir(self.FSpath)) | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in children: | |||||
| doupdate = True | |||||
| # 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: | |||||
| doupdate = True | |||||
| self.pathObjmap[i] = nf | |||||
| # sort our children | |||||
| self.sort(lambda x, y: cmp(x.title, y.title)) | |||||
| # Pass up to handle UpdateID | |||||
| if doupdate: | |||||
| # Calling StorageFolder.doUpdate results in calling | |||||
| # ourselves. | |||||
| Container.doUpdate(self) | |||||
| self.indoUpdate = False | |||||
| def genChildren(self): | |||||
| return os.listdir(self.FSpath) | |||||
| def createObject(self, i): | |||||
| return dofileadd(self.FSpath, i) | |||||
| def __repr__(self): | def __repr__(self): | ||||
| return ('<%s.%s: path: %s, id: %s, parent: %s, title: %s, ' + \ | return ('<%s.%s: path: %s, id: %s, parent: %s, title: %s, ' + \ | ||||
| @@ -158,6 +158,7 @@ class ZipItem: | |||||
| del kwargs['name'] | del kwargs['name'] | ||||
| def checkUpdate(self): | def checkUpdate(self): | ||||
| # XXX - is this the correct order? | |||||
| self.doUpdate() | self.doUpdate() | ||||
| self.zo.checkUpdate() | self.zo.checkUpdate() | ||||
| @@ -174,9 +175,6 @@ class ZipFile(ZipItem, Item): | |||||
| self.res = Resource(self.url, 'http-get:*:%s:*' % self.mimetype) | self.res = Resource(self.url, 'http-get:*:%s:*' % self.mimetype) | ||||
| self.res.size = self.zi.file_size | self.res.size = self.zi.file_size | ||||
| def doUpdate(self): | |||||
| pass | |||||
| class ZipChildDir(ZipItem, StorageFolder): | class ZipChildDir(ZipItem, StorageFolder): | ||||
| '''This is to represent a child dir of the zip file.''' | '''This is to represent a child dir of the zip file.''' | ||||
| @@ -188,48 +186,29 @@ class ZipChildDir(ZipItem, StorageFolder): | |||||
| del kwargs['zf'], kwargs['zo'], kwargs['name'] | del kwargs['zf'], kwargs['zo'], kwargs['name'] | ||||
| StorageFolder.__init__(self, *args, **kwargs) | StorageFolder.__init__(self, *args, **kwargs) | ||||
| # mapping from path to objectID | |||||
| self.pathObjmap = {} | |||||
| def doUpdate(self): | |||||
| doupdate = False | |||||
| children = sets.Set(self.hier.keys()) | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in children: | |||||
| # delete | |||||
| doupdate = True | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| cursep = self.sep | |||||
| for i in children: | |||||
| if i in self.pathObjmap: | |||||
| continue | |||||
| # new object | |||||
| pathname = cursep.join((self.name, i)) | |||||
| if isinstance(self.hier[i], dict): | |||||
| # must be a dir | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| ZipChildDir, i, zf=self.zf, zo=self, | |||||
| name=pathname, hier=self.hier[i], | |||||
| sep=cursep) | |||||
| else: | |||||
| klass, mt = FileDIDL.buildClassMT(ZipFile, i) | |||||
| if klass is None: | |||||
| continue | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| klass, i, zf = self.zf, zo = self, | |||||
| name = pathname, mimetype = mt) | |||||
| doupdate = True | |||||
| # sort our children | |||||
| self.sort(lambda x, y: cmp(x.title, y.title)) | |||||
| if doupdate: | |||||
| StorageFolder.doUpdate(self) | |||||
| def genChildren(self): | |||||
| return self.hier | |||||
| def genCurrent(self): | |||||
| return ((x.id, x.name.rsplit(self.sep, 1)[1]) for x in self) | |||||
| def createObject(self, i, arg): | |||||
| pathname = self.sep.join((self.name, i)) | |||||
| kwargs = { 'zf': self.zf, 'zo': self, 'name': pathname } | |||||
| if isinstance(self.hier[i], dict): | |||||
| # must be a dir | |||||
| klass = ZipChildDir | |||||
| kwargs['sep'] = self.sep | |||||
| kwargs['hier'] = self.hier[i] | |||||
| else: | |||||
| klass, mt = FileDIDL.buildClassMT(ZipFile, i) | |||||
| kwargs['name'] = pathname | |||||
| kwargs['mimetype'] = mt | |||||
| return klass, i, (), kwargs | |||||
| def __repr__(self): | def __repr__(self): | ||||
| return '<ZipChildDir: len: %d>' % len(self.pathObjmap) | |||||
| return '<ZipChildDir: len: %d>' % len(self) | |||||
| def tryTar(path): | def tryTar(path): | ||||
| # Try to see if it's a tar file | # Try to see if it's a tar file | ||||
| @@ -270,19 +249,16 @@ class ZipObject(FSObject, StorageFolder): | |||||
| def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | ||||
| '''If a zip argument is passed it, use that as the zip archive.''' | '''If a zip argument is passed it, use that as the zip archive.''' | ||||
| path = kwargs['path'] | |||||
| del kwargs['path'] | |||||
| path = kwargs.pop('path') | |||||
| StorageFolder.__init__(self, *args, **kwargs) | StorageFolder.__init__(self, *args, **kwargs) | ||||
| FSObject.__init__(self, path) | FSObject.__init__(self, path) | ||||
| # mapping from path to objectID | |||||
| self.pathObjmap = {} | |||||
| def doUpdate(self): | |||||
| def genChildren(self): | |||||
| # open the zipfile as necessary. | # open the zipfile as necessary. | ||||
| self.zip = genZipFile(self.FSpath) | self.zip = genZipFile(self.FSpath) | ||||
| nl = self.zip.namelist() | nl = self.zip.namelist() | ||||
| cnt = 0 | cnt = 0 | ||||
| cursep = None | cursep = None | ||||
| for i in self.seps: | for i in self.seps: | ||||
| @@ -291,41 +267,26 @@ class ZipObject(FSObject, StorageFolder): | |||||
| cursep = i | cursep = i | ||||
| cnt = newsum | cnt = newsum | ||||
| self.sep = cursep | self.sep = cursep | ||||
| hier = buildNameHier(nl, self.zip.infolist(), cursep) | hier = buildNameHier(nl, self.zip.infolist(), cursep) | ||||
| doupdate = False | |||||
| children = sets.Set(hier.keys()) | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in children: | |||||
| doupdate = True | |||||
| # delete | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| for i in children: | |||||
| if i in self.pathObjmap: | |||||
| continue | |||||
| # new object | |||||
| if isinstance(hier[i], dict): | |||||
| # must be a dir | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| ZipChildDir, i, zf=self.zip, zo=self, | |||||
| name=i, hier=hier[i], sep=cursep) | |||||
| else: | |||||
| klass, mt = FileDIDL.buildClassMT(ZipFile, i) | |||||
| if klass is None: | |||||
| continue | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| klass, i, zf = self.zip, zo = self, | |||||
| name = i, mimetype = mt) | |||||
| doupdate = True | |||||
| # sort our children | |||||
| self.sort(lambda x, y: cmp(x.title, y.title)) | |||||
| if doupdate: | |||||
| StorageFolder.doUpdate(self) | |||||
| return hier | |||||
| def genCurrent(self): | |||||
| return ((x.id, x.name) for x in self) | |||||
| def createObject(self, i, arg): | |||||
| kwargs = { 'zf': self.zip, 'zo': self, 'name': i } | |||||
| if isinstance(arg, dict): | |||||
| # must be a dir | |||||
| kwargs['hier'] = arg | |||||
| kwargs['sep'] = self.sep | |||||
| klass = ZipChildDir | |||||
| else: | |||||
| klass, mt = FileDIDL.buildClassMT(ZipFile, i) | |||||
| kwargs['mimetype'] = mt | |||||
| return klass, i, (), kwargs | |||||
| def detectzipfile(path, fobj): | def detectzipfile(path, fobj): | ||||
| try: | try: | ||||
| @@ -30,14 +30,12 @@ from twisted.spread import pb | |||||
| from twisted.web import resource, server | from twisted.web import resource, server | ||||
| def gennameindexes(pref, item): | def gennameindexes(pref, item): | ||||
| ret = [] | |||||
| d = {} | d = {} | ||||
| for i, title in enumerate(item): | for i, title in enumerate(item): | ||||
| t = '%s %d (%s)' % (pref, i + 1, title.time) | t = '%s %d (%s)' % (pref, i + 1, title.time) | ||||
| ret.append(t) | |||||
| d[t] = i | d[t] = i | ||||
| return ret, d | |||||
| return d | |||||
| class IterTransfer(pb.Viewable): | class IterTransfer(pb.Viewable): | ||||
| def __init__(self, iterable, request): | def __init__(self, iterable, request): | ||||
| @@ -142,30 +140,12 @@ class DVDTitle(StorageFolder): | |||||
| self.doUpdate() | self.doUpdate() | ||||
| #return self.dvddisc.checkUpdate() | #return self.dvddisc.checkUpdate() | ||||
| def doUpdate(self): | |||||
| doupdate = False | |||||
| origchildren, toindex = gennameindexes('Chapter', self.dvdtitle) | |||||
| children = sets.Set(origchildren) | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in children: | |||||
| doupdate = True | |||||
| # delete | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| for i in origchildren: | |||||
| if i in self.pathObjmap: | |||||
| continue | |||||
| # new object | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| DVDChapter, i, dvdtitle = self.dvdtitle, | |||||
| chapter = self.dvdtitle[toindex[i]]) | |||||
| doupdate = True | |||||
| if doupdate: | |||||
| StorageFolder.doUpdate(self) | |||||
| def genChildren(self): | |||||
| return gennameindexes('Chapter', self.dvdtitle) | |||||
| def createObject(self, i, arg): | |||||
| return DVDChapter, i, (), { 'dvdtitle': self.dvdtitle, | |||||
| 'chapter': self.dvdtitle[arg] } | |||||
| class DVDDisc(FSObject, StorageFolder): | class DVDDisc(FSObject, StorageFolder): | ||||
| def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | ||||
| @@ -175,34 +155,14 @@ class DVDDisc(FSObject, StorageFolder): | |||||
| StorageFolder.__init__(self, *args, **kwargs) | StorageFolder.__init__(self, *args, **kwargs) | ||||
| FSObject.__init__(self, path) | FSObject.__init__(self, path) | ||||
| # mapping from path to objectID | |||||
| self.pathObjmap = {} | |||||
| def doUpdate(self): | |||||
| # open the DVD as necessary. | |||||
| def genChildren(self): | |||||
| self.dvd = DVD(self.FSpath) | self.dvd = DVD(self.FSpath) | ||||
| doupdate = False | |||||
| origchildren, toindex = gennameindexes('Title', self.dvd) | |||||
| children = sets.Set(origchildren) | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in children: | |||||
| doupdate = True | |||||
| # delete | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| for i in origchildren: | |||||
| if i in self.pathObjmap: | |||||
| continue | |||||
| # new object | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, DVDTitle, | |||||
| i, dvdtitle = self.dvd[toindex[i]], dvddisc = self) | |||||
| doupdate = True | |||||
| if doupdate: | |||||
| StorageFolder.doUpdate(self) | |||||
| return gennameindexes('Title', self.dvd) | |||||
| def createObject(self, i, arg): | |||||
| return DVDTitle, i, (), { 'dvdtitle': self.dvd[arg], | |||||
| 'dvddisc': self } | |||||
| def detectdvd(path, fobj): | def detectdvd(path, fobj): | ||||
| if os.path.isdir(path): | if os.path.isdir(path): | ||||
| @@ -0,0 +1,69 @@ | |||||
| #!/usr/bin/env python | |||||
| # Copyright 2009 John-Mark Gurney <jmg@funkthat.com> | |||||
| __version__ = '$Change$' | |||||
| # $Id$ | |||||
| import FileDIDL | |||||
| import xml.dom.minidom | |||||
| from DIDLLite import StorageFolder, Item, Resource, ResourceList | |||||
| from FSStorage import FSObject, registerklassfun | |||||
| def getElementText(elm, strip=True): | |||||
| s = ''.join(x.nodeValue for x in elm.childNodes) | |||||
| if strip: | |||||
| s = s.strip() | |||||
| return s | |||||
| class ItemObject(FSObject, Item): | |||||
| def __init__(self, *args, **kwargs): | |||||
| path = kwargs.pop('path') | |||||
| dom = kwargs.pop('dom') | |||||
| FSObject.__init__(self, path) | |||||
| Item.__init__(self, *args, **kwargs) | |||||
| self.checkUpdate(dom=dom) | |||||
| def doUpdate(self, dom=None): | |||||
| if dom is None: | |||||
| dom = xml.dom.minidom.parse(open(self.FSpath)) | |||||
| obj = dom.getElementsByTagName('Object')[0] | |||||
| mt = getElementText(obj.getElementsByTagName('mimetype')[0]) | |||||
| self.res = ResourceList() | |||||
| for i in obj.getElementsByTagName('res'): | |||||
| r = Resource(getElementText(i.getElementsByTagName( | |||||
| 'url')[0]), 'http-get:*:%s:*' % mt) | |||||
| size = i.getElementsByTagName('size') | |||||
| if size: | |||||
| r.size = int(getElementText(size[0])) | |||||
| self.res.append(r) | |||||
| super(ItemObject, self).doUpdate() | |||||
| def canHandle(fobj): | |||||
| # XXX - create schema and validate | |||||
| dom = xml.dom.minidom.parse(fobj) | |||||
| #print 'ch:', `dom` | |||||
| obj = dom.getElementsByTagName('Object')[0] | |||||
| mt = getElementText(obj.getElementsByTagName('mimetype')[0]) | |||||
| #print 'ch:', `obj`, `mt` | |||||
| return dom, mt | |||||
| def detectitem(path, fobj): | |||||
| #print 'di:', `path`, `fobj` | |||||
| try: | |||||
| dom, mt = canHandle(fobj) | |||||
| except: | |||||
| #import traceback | |||||
| #traceback.print_exc() | |||||
| return None, None | |||||
| klass, mt = FileDIDL.buildClassMT(ItemObject, path, mimetype=mt) | |||||
| return klass, { 'path': path, 'dom': dom } | |||||
| registerklassfun(detectitem) | |||||
| @@ -48,15 +48,13 @@ class _LimitedFile(file): | |||||
| return file.read(self, min(size, self.remain())) | return file.read(self, min(size, self.remain())) | ||||
| def _gennameindexes(chan): | def _gennameindexes(chan): | ||||
| ret = [] | |||||
| d = {} | d = {} | ||||
| for i in chan: | for i in chan: | ||||
| t = '%s %s.%s' % (i['name'], i['major'], i['minor']) | t = '%s %s.%s' % (i['name'], i['major'], i['minor']) | ||||
| ret.append(t) | |||||
| d[t] = i | d[t] = i | ||||
| return ret, d | |||||
| return d | |||||
| class MPEGTSTransfer(pb.Viewable): | class MPEGTSTransfer(pb.Viewable): | ||||
| def __init__(self, iterable, request): | def __init__(self, iterable, request): | ||||
| @@ -307,42 +305,25 @@ class MultiMPEGTS(FSObject, StorageFolder): | |||||
| StorageFolder.__init__(self, *args, **kwargs) | StorageFolder.__init__(self, *args, **kwargs) | ||||
| FSObject.__init__(self, path) | FSObject.__init__(self, path) | ||||
| # mapping from path to objectID | |||||
| self.pathObjmap = {} | |||||
| def doUpdate(self): | |||||
| def genChildren(self): | |||||
| f = mpegts.TSPStream(_LimitedFile(self.FSpath, | f = mpegts.TSPStream(_LimitedFile(self.FSpath, | ||||
| size= 2*1024*1024)) | |||||
| size=2*1024*1024)) | |||||
| self.tvct = mpegts.GetTVCT(f) | self.tvct = mpegts.GetTVCT(f) | ||||
| #log.msg('MultiMPEGTS genChildren: tvct: %s' % self.tvct) | |||||
| return _gennameindexes(self.tvct['channels']) | |||||
| doupdate = False | |||||
| origchildren, toindex = _gennameindexes(self.tvct['channels']) | |||||
| #log.msg('MultiMPEGTS doUpdate: tvct: %s' % self.tvct) | |||||
| children = sets.Set(origchildren) | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in children: | |||||
| doupdate = True | |||||
| # delete | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| for i in origchildren: | |||||
| if i in self.pathObjmap: | |||||
| continue | |||||
| # new object | |||||
| if toindex[i]['prog_num'] == 0: | |||||
| log.msg('bogus:', toindex[i]) | |||||
| continue | |||||
| def createObject(self, i, arg): | |||||
| if arg['prog_num'] == 0: | |||||
| log.msg('bogus:', arg) | |||||
| return None, None, (), {} | |||||
| #log.msg('real tvct:', toindex[i], toindex.keys(), | |||||
| # self.tvct) | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, MPEGTS, | |||||
| i, path = self.FSpath, tvct = toindex[i]) | |||||
| doupdate = True | |||||
| #log.msg('real tvct:', arg, toindex.keys(), self.tvct) | |||||
| return MPEGTS, i, (), { 'path': self.FSpath, 'tvct': arg } | |||||
| if doupdate: | |||||
| StorageFolder.doUpdate(self) | |||||
| def sort(self): | |||||
| return super(MultiMPEGTS, self).sort(lambda x, y: | |||||
| cmp((x.tvct['major'], x.tvct['minor']), | |||||
| (y.tvct['major'], y.tvct['minor']))) | |||||
| def findtsstream(fp, pktsz=188): | def findtsstream(fp, pktsz=188): | ||||
| d = fp.read(200*pktsz) | d = fp.read(200*pktsz) | ||||
| @@ -30,6 +30,7 @@ def tryloadmodule(mod): | |||||
| modules = [ | modules = [ | ||||
| 'shoutcast', | 'shoutcast', | ||||
| 'pyvr', | 'pyvr', | ||||
| 'item', | |||||
| 'dvd', | 'dvd', | ||||
| 'ZipStorage', | 'ZipStorage', | ||||
| 'mpegtsmod', | 'mpegtsmod', | ||||
| @@ -196,6 +197,9 @@ def makeService(config): | |||||
| s.doStop() | s.doStop() | ||||
| service.MultiService.stopService(self) | service.MultiService.stopService(self) | ||||
| import pickle | |||||
| pickle.dump(cds, open('test.pickle', 'wb'), -1) | |||||
| serv = PyMedS() | serv = PyMedS() | ||||
| internet.TCPServer(listenPort, site).setServiceParent(serv) | internet.TCPServer(listenPort, site).setServiceParent(serv) | ||||
| @@ -109,7 +109,6 @@ class PYVRShows(Container): | |||||
| Container.__init__(self, *args, **kwargs) | Container.__init__(self, *args, **kwargs) | ||||
| self.pathObjmap = {} | |||||
| self.shows = {} | self.shows = {} | ||||
| self.lastmodified = None | self.lastmodified = None | ||||
| @@ -140,34 +139,18 @@ class PYVRShows(Container): | |||||
| return ret | return ret | ||||
| def genChildren(self): | |||||
| return self.eplisttodict(self.pyvr.shows[self.show]) | |||||
| def createObject(self, i, arg): | |||||
| return PYVRShow, i, (), { 'url': self.pyvr.url, 'info': arg } | |||||
| def sort(self): | |||||
| Container.sort(self, lambda x, y: cmp(x.info['pos'], | |||||
| y.info['pos'])) | |||||
| def doUpdate(self): | def doUpdate(self): | ||||
| nl = self.eplisttodict(self.pyvr.shows[self.show]) | |||||
| doupdate = False | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in nl: | |||||
| # delete | |||||
| doupdate = True | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| for i in nl: | |||||
| if i in self.pathObjmap and self.shows[i] == nl[i]: | |||||
| continue | |||||
| doupdate = True | |||||
| if i in self.pathObjmap: | |||||
| # changed | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| PYVRShow, i, url=self.pyvr.url, info=nl[i]) | |||||
| self.shows = nl | |||||
| # sort our children | |||||
| #self.sort(lambda x, y: cmp(x.title, y.title)) | |||||
| self.sort(lambda x, y: cmp(x.info['pos'], y.info['pos'])) | |||||
| if doupdate: | |||||
| Container.doUpdate(self) | |||||
| Container.doUpdate(self) | |||||
| self.lastmodified = self.pyvr.lastmodified | self.lastmodified = self.pyvr.lastmodified | ||||
| @@ -178,7 +161,6 @@ class PYVR(Container): | |||||
| Container.__init__(self, *args, **kwargs) | Container.__init__(self, *args, **kwargs) | ||||
| self.pathObjmap = {} | |||||
| #self.pend = None | #self.pend = None | ||||
| self.lastmodified = None | self.lastmodified = None | ||||
| self.newobjs = None | self.newobjs = None | ||||
| @@ -241,42 +223,23 @@ class PYVR(Container): | |||||
| self.newobjs = recxmltoobj(page.read()) | self.newobjs = recxmltoobj(page.read()) | ||||
| self.doUpdate() | self.doUpdate() | ||||
| def genChildren(self): | |||||
| return self.newobjs.iterkeys() | |||||
| def createObject(self, i): | |||||
| return PYVRShows, i, (), { 'show': i, 'pyvr': self } | |||||
| def doUpdate(self): | def doUpdate(self): | ||||
| if self.newobjs is None: | if self.newobjs is None: | ||||
| import traceback | import traceback | ||||
| traceback.print_stack(file=log.logfile) | traceback.print_stack(file=log.logfile) | ||||
| return | |||||
| raise ValueError('did not get shows') | |||||
| nl = self.newobjs | |||||
| doupdate = False | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in nl: | |||||
| # delete | |||||
| doupdate = True | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| # This data is referenced when adding new shows | |||||
| self.shows = nl | |||||
| for i in nl: | |||||
| if i in self.pathObjmap: | |||||
| continue | |||||
| doupdate = True | |||||
| try: | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| PYVRShows, i, show=i, pyvr=self) | |||||
| except: | |||||
| import traceback | |||||
| traceback.print_exc(file=log.logfile) | |||||
| raise | |||||
| self.shows = self.newobjs | |||||
| self.newobjs = None | |||||
| Container.doUpdate(self) | |||||
| # sort our children | |||||
| self.sort(lambda x, y: cmp(x.title, y.title)) | |||||
| if doupdate: | |||||
| Container.doUpdate(self) | |||||
| self.newobjs = None | |||||
| def detectpyvrfile(path, fobj): | def detectpyvrfile(path, fobj): | ||||
| bn = os.path.basename(path) | bn = os.path.basename(path) | ||||
| @@ -15,7 +15,7 @@ import ConfigParser | |||||
| import cStringIO as StringIO | import cStringIO as StringIO | ||||
| import os.path | import os.path | ||||
| import random | import random | ||||
| import time | |||||
| import traceback | import traceback | ||||
| from py_shoutcast import * | from py_shoutcast import * | ||||
| @@ -312,7 +312,6 @@ class ShoutGenre(MusicGenre): | |||||
| #self.feeds = ShoutcastFeedAsync(self.genre) | #self.feeds = ShoutcastFeedAsync(self.genre) | ||||
| self.feeds = feeds.ShoutcastFeed(self.genre) | self.feeds = feeds.ShoutcastFeed(self.genre) | ||||
| self.sl = None | self.sl = None | ||||
| self.pathObjmap = {} | |||||
| MusicGenre.__init__(self, *args, **kwargs) | MusicGenre.__init__(self, *args, **kwargs) | ||||
| @@ -335,43 +334,36 @@ class ShoutGenre(MusicGenre): | |||||
| return ret | return ret | ||||
| def checkUpdate(self): | def checkUpdate(self): | ||||
| self.doUpdate() | |||||
| # Sometimes the shoutcast server returns a 503 (busy) which | |||||
| # isn't valid XML, try again. | |||||
| while True: | |||||
| try: | |||||
| stations = self.feeds.parse_stations() | |||||
| break | |||||
| except: | |||||
| traceback.print_exc() | |||||
| time.sleep(1) | |||||
| def doUpdate(self): | |||||
| #traceback.print_stack(file=log.logfile) | |||||
| stations = self.feeds.parse_stations() | |||||
| if stations == self.sl: | if stations == self.sl: | ||||
| return | return | ||||
| nl = self.genStations(stations) | |||||
| doupdate = False | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in nl: | |||||
| # delete | |||||
| doupdate = True | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| for name, i in nl.iteritems(): | |||||
| if name in self.pathObjmap: | |||||
| if cmpStation(i, self.cd[self.pathObjmap[name]].station): | |||||
| continue | |||||
| # Didn't match, readd | |||||
| self.cd.delItem(self.pathObjmap[name]) | |||||
| del self.pathObjmap[name] | |||||
| doupdate = True | |||||
| self.pathObjmap[name] = self.cd.addItem(self.id, | |||||
| ShoutStation, '%sk-%s' % (i['Bitrate'], name), | |||||
| station = i) | |||||
| self.sl = stations | self.sl = stations | ||||
| # sort our children | |||||
| self.sort(lambda *a: stationwbitratecmp(*a)) | |||||
| if doupdate: | |||||
| Container.doUpdate(self) | |||||
| self.doUpdate() | |||||
| def genChildren(self): | |||||
| return self.genStations(self.sl) | |||||
| def genCurrent(self): | |||||
| return ((x.id, x.station) for x in self) | |||||
| def createObject(self, name, arg): | |||||
| return ShoutStation, '%sk-%s' % (arg['Bitrate'], name), (), \ | |||||
| { 'station': arg } | |||||
| def sort(self): | |||||
| super(ShoutGenre, self).sort(lambda *a: stationwbitratecmp(*a)) | |||||
| class ShoutCast(Container): | class ShoutCast(Container): | ||||
| def __init__(self, *args, **kwargs): | def __init__(self, *args, **kwargs): | ||||
| @@ -380,38 +372,24 @@ class ShoutCast(Container): | |||||
| #self.genres = GenreFeedAsync() | #self.genres = GenreFeedAsync() | ||||
| self.genres = feeds.GenreFeed() | self.genres = feeds.GenreFeed() | ||||
| self.genre_list = None | self.genre_list = None | ||||
| self.pathObjmap = {} | |||||
| def checkUpdate(self): | def checkUpdate(self): | ||||
| self.doUpdate() | |||||
| def doUpdate(self): | |||||
| #traceback.print_stack(file=log.logfile) | |||||
| nl = self.genres.parse_genres() | nl = self.genres.parse_genres() | ||||
| if nl == self.genre_list: | if nl == self.genre_list: | ||||
| return | return | ||||
| doupdate = False | |||||
| for i in self.pathObjmap.keys(): | |||||
| if i not in nl: | |||||
| # delete | |||||
| doupdate = True | |||||
| self.cd.delItem(self.pathObjmap[i]) | |||||
| del self.pathObjmap[i] | |||||
| for i in nl: | |||||
| if i in self.pathObjmap: | |||||
| continue | |||||
| doupdate = True | |||||
| self.pathObjmap[i] = self.cd.addItem(self.id, | |||||
| ShoutGenre, i, genre=i) | |||||
| self.genre_list = nl | self.genre_list = nl | ||||
| # sort our children | |||||
| self.sort(lambda x, y: cmp(x.title, y.title)) | |||||
| if doupdate: | |||||
| Container.doUpdate(self) | |||||
| super(ShoutCast, self).doUpdate() | |||||
| def genChildren(self): | |||||
| return self.genre_list | |||||
| def genCurrent(self): | |||||
| return ((x.id, x.genre) for x in self) | |||||
| def createObject(self, i): | |||||
| return ShoutGenre, i, (), { 'genre': i } | |||||
| def detectshoutcastfile(origpath, fobj): | def detectshoutcastfile(origpath, fobj): | ||||
| path = os.path.basename(origpath) | path = os.path.basename(origpath) | ||||