Browse Source

support multiple res elements...

add code to dynamicly transcode to mpeg2 or xvid via additional
res elements..

document this and other new features in the README...

[git-p4: depot-paths = "//depot/": change = 837]
v0.3
John-Mark Gurney 19 years ago
parent
commit
31ddad6041
3 changed files with 113 additions and 3 deletions
  1. +5
    -1
      DIDLLite.py
  2. +101
    -2
      FSStorage.py
  3. +7
    -0
      README

+ 5
- 1
DIDLLite.py View File

@@ -82,7 +82,11 @@ class Object(object):
SubElement(root, 'dc:creator').text = self.creator SubElement(root, 'dc:creator').text = self.creator


if self.res is not None: if self.res is not None:
root.append(self.res.toElement())
try:
for res in iter(self.res):
root.append(res.toElement())
except TypeError:
root.append(self.res.toElement())


if self.writeStatus is not None: if self.writeStatus is not None:
SubElement(root, 'upnp:writeStatus').text = self.writeStatus SubElement(root, 'upnp:writeStatus').text = self.writeStatus


+ 101
- 2
FSStorage.py View File

@@ -4,6 +4,8 @@
# $Id$ # $Id$
# #


ffmpeg_path = '/Users/jgurney/src/ffmpeg/ffmpeg'

import FileDIDL import FileDIDL
import errno import errno
import itertools import itertools
@@ -12,8 +14,10 @@ import sets
import stat import stat


from DIDLLite import StorageFolder, Item, VideoItem, AudioItem, TextItem, ImageItem, Resource from DIDLLite import StorageFolder, Item, VideoItem, AudioItem, TextItem, ImageItem, Resource
from twisted.web import 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 zope.interface import implements


__all__ = [ 'registerklassfun', 'FSObject', 'FSItem', 'FSVideoItem', __all__ = [ 'registerklassfun', 'FSObject', 'FSItem', 'FSVideoItem',
'FSAudioItem', 'FSTextItem', 'FSImageItem', 'mimetoklass', 'FSAudioItem', 'FSTextItem', 'FSImageItem', 'mimetoklass',
@@ -69,13 +73,105 @@ class FSObject(object):
return '<%s.%s: path: %s>' % (self.__class__.__module__, return '<%s.%s: path: %s>' % (self.__class__.__module__,
self.__class__.__name__, self.FSpath) self.__class__.__name__, self.FSpath)


class NullConsumer(file, abstract.FileDescriptor):
implements(interfaces.IConsumer)

def __init__(self):
file.__init__(self, '/dev/null', 'w')
abstract.FileDescriptor.__init__(self)

def write(self, data):
pass

class DynamTransfer(protocol.ProcessProtocol):
def __init__(self, path, mods, request):
self.path = path
self.mods = mods
self.request = request

def outReceived(self, data):
self.request.write(data)

def outConnectionLost(self):
if self.request:
self.request.unregisterProducer()
self.request.finish()
self.request = None

def errReceived(self, data):
pass
#log.msg(data)

def stopProducing(self):
if self.request:
self.request.unregisterProducer()
self.request.finish()

if self.proc:
self.proc.loseConnection()
self.proc.signalProcess('INT')

self.request = None
self.proc = None

pauseProducing = lambda x: x.proc.pauseProducing()
resumeProducing = lambda x: x.proc.resumeProducing()

def render(self):
mods = self.mods
path = self.path
request = self.request

vcodec = mods[0]
if mods[0] not in ('xvid', 'mpeg2', ):
vcodec = 'xvid'

mimetype = { 'xvid': 'video/avi', 'mpeg2': 'video/mpeg', }
request.setHeader('content-type', mimetype[vcodec])
if request.method == 'HEAD':
return ''

optdict = {
'xvid': [ '-vcodec', 'xvid',
#'-mv4', '-gmc', '-g', '240',
'-f', 'avi', ],
'mpeg2': [ '-vcodec', 'mpeg2video', #'-g', '60',
'-f', 'mpeg', ],
}
audio = [ '-acodec', 'mp3', '-ab', '192', ]
args = [ 'ffmpeg', '-i', path, '-b', '8000',
#'-sc_threshold', '500000', '-b_strategy', '1', '-max_b_frames', '6',
] + optdict[vcodec] + audio + [ '-', ]
#log.msg(*args)
self.proc = process.Process(reactor, ffmpeg_path, args,
None, None, self)
self.proc.closeStdin()
request.registerProducer(self, 1)

return server.NOT_DONE_YET

class DynamicTrans(resource.Resource):
isLeaf = True

def __init__(self, path, notrans):
self.path = path
self.notrans = notrans

def render(self, request):
if request.postpath:
# Translation request
return DynamTransfer(self.path, request.postpath, request).render()
else:
return self.notrans.render(request)

class FSItem(FSObject, Item): class FSItem(FSObject, Item):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
FSObject.__init__(self, kwargs['path']) FSObject.__init__(self, kwargs['path'])
del kwargs['path'] del kwargs['path']
mimetype = kwargs['mimetype'] mimetype = kwargs['mimetype']
del kwargs['mimetype'] del kwargs['mimetype']
kwargs['content'] = static.File(self.FSpath)
kwargs['content'] = DynamicTrans(self.FSpath,
static.File(self.FSpath, mimetype))
Item.__init__(self, *args, **kwargs) Item.__init__(self, *args, **kwargs)
self.url = '%s/%s' % (self.cd.urlbase, self.id) self.url = '%s/%s' % (self.cd.urlbase, self.id)
self.mimetype = mimetype self.mimetype = mimetype
@@ -83,6 +179,9 @@ class FSItem(FSObject, Item):
def doUpdate(self): def doUpdate(self):
self.res = Resource(self.url, 'http-get:*:%s:*' % self.mimetype) self.res = Resource(self.url, 'http-get:*:%s:*' % self.mimetype)
self.res.size = os.path.getsize(self.FSpath) self.res.size = os.path.getsize(self.FSpath)
self.res = [ self.res ]
self.res.append(Resource(self.url + '/mpeg2', 'http-get:*:%s:*' % 'video/mpeg'))
self.res.append(Resource(self.url + '/xvid', 'http-get:*:%s:*' % 'video/avi'))
Item.doUpdate(self) Item.doUpdate(self)


def defFS(path, fobj): def defFS(path, fobj):


+ 7
- 0
README View File

@@ -25,10 +25,17 @@ Good Luck!
John-Mark Gurney <gurney_j@resnet.uoregon.edu> John-Mark Gurney <gurney_j@resnet.uoregon.edu>


Ideas for future improvements: Ideas for future improvements:
Figure out how to rearchitect ContentDirectoryControl so I don't
need to use doRecall. This may be helped by not necessarily
figuring out all the children of a member just to fetch it.
childCount isn't a required attribute.
Autodetect IP address. Autodetect IP address.
Support sorting by other attributes. Support sorting by other attributes.


v0.x: v0.x:
Add support for multiple res elements and automatic transcoding
to either avi/xvid or mpeg2 using ffmpeg.
Look inside DVDs and handle titles and chapters.
Empty dirs w/ no content would disappear, and cause a short Empty dirs w/ no content would disappear, and cause a short
response to BrowseDirectChildren. The DSM-520 askes for one response to BrowseDirectChildren. The DSM-520 askes for one
more than displayed, and uses the existant of the extra item more than displayed, and uses the existant of the extra item


Loading…
Cancel
Save