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

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:
SubElement(root, 'upnp:writeStatus').text = self.writeStatus


+ 101
- 2
FSStorage.py View File

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

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

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

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.internet import abstract, interfaces, process, protocol, reactor
from zope.interface import implements

__all__ = [ 'registerklassfun', 'FSObject', 'FSItem', 'FSVideoItem',
'FSAudioItem', 'FSTextItem', 'FSImageItem', 'mimetoklass',
@@ -69,13 +73,105 @@ class FSObject(object):
return '<%s.%s: path: %s>' % (self.__class__.__module__,
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):
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)
kwargs['content'] = DynamicTrans(self.FSpath,
static.File(self.FSpath, mimetype))
Item.__init__(self, *args, **kwargs)
self.url = '%s/%s' % (self.cd.urlbase, self.id)
self.mimetype = mimetype
@@ -83,6 +179,9 @@ class FSItem(FSObject, Item):
def doUpdate(self):
self.res = Resource(self.url, 'http-get:*:%s:*' % self.mimetype)
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)

def defFS(path, fobj):


+ 7
- 0
README View File

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

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.
Support sorting by other attributes.

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
response to BrowseDirectChildren. The DSM-520 askes for one
more than displayed, and uses the existant of the extra item


Loading…
Cancel
Save