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) | ||||