From 9d5bc2b3889cc9b53eb9238a7f989283682c0210 Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Fri, 7 Jul 2006 01:51:38 -0800 Subject: [PATCH] add dvd support through my pydvdread wrapper around libdvdread... it seems to work, though direct from disk seems to be too slow... [git-p4: depot-paths = "//depot/": change = 830] --- dvd.py | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++ pymediaserv | 1 + 2 files changed, 214 insertions(+) create mode 100644 dvd.py diff --git a/dvd.py b/dvd.py new file mode 100644 index 0000000..21684b4 --- /dev/null +++ b/dvd.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python + +'''DVD Handling''' + +import os +import sets + +from pydvdread import * + +from DIDLLite import StorageFolder, Movie, VideoItem, Resource +from FSStorage import FSObject, registerklassfun + +from twisted.python import log, threadable +from twisted.spread import pb +from twisted.web import resource, server + +def gennameindexes(pref, item): + ret = [] + d = {} + for i, title in enumerate(item): + t = '%s %d (%s)' % (pref, i + 1, title.time) + ret.append(t) + d[t] = i + + return ret, d + +class DVDChapterTransfer(pb.Viewable): + def __init__(self, iterable, request): + self.iter = iter(iterable) + self.request = request + request.registerProducer(self, 0) + + def resumeProducing(self): + if not self.request: + return + # get data and write to request. + try: + data = self.iter.next() + if data: + # this .write will spin the reactor, calling + # .doWrite and then .resumeProducing again, so + # be prepared for a re-entrant call + self.request.write(data) + except StopIteration: + if self.request: + self.request.unregisterProducer() + self.request.finish() + self.request = None + + def pauseProducing(self): + pass + + def stopProducing(self): + # close zipfile + self.request = None + + # Remotely relay producer interface. + + def view_resumeProducing(self, issuer): + self.resumeProducing() + + def view_pauseProducing(self, issuer): + self.pauseProducing() + + def view_stopProducing(self, issuer): + self.stopProducing() + + synchronized = ['resumeProducing', 'stopProducing'] + +threadable.synchronize(DVDChapterTransfer) + +class DVDChapterResource(resource.Resource): + isLeaf = True + + def __init__(self, chapter): + resource.Resource.__init__(self) + + self.chapter = chapter + + def getFileSize(self): + return self.chapter.size + + def render(self, request): + request.setHeader('content-type', 'video/mpeg') + + request.setHeader('content-length', str(self.getFileSize())) + if request.method == 'HEAD': + return '' + + # return data + DVDChapterTransfer(self.chapter, request) + # and make sure the connection doesn't get closed + return server.NOT_DONE_YET + +class DVDChapter(VideoItem): + def __init__(self, *args, **kwargs): + self.dvdtitle = kwargs['dvdtitle'] + self.chapter = kwargs['chapter'] + del kwargs['dvdtitle'], kwargs['chapter'] + + kwargs['content'] = DVDChapterResource(self.chapter) + VideoItem.__init__(self, *args, **kwargs) + + self.url = '%s/%s' % (self.cd.urlbase, self.id) + self.res = Resource(self.url, 'http-get:*:video/mpeg:*') + self.res.size = self.chapter.size + + def doUpdate(self): + pass + +class DVDTitle(StorageFolder): + def __init__(self, *args, **kwargs): + self.dvdtitle = kwargs['dvdtitle'] + self.dvddisc = kwargs['dvddisc'] + del kwargs['dvdtitle'], kwargs['dvddisc'] + + StorageFolder.__init__(self, *args, **kwargs) + + # mapping from path to objectID + self.pathObjmap = {} + + def checkUpdate(self): + self.doUpdate() + #return self.dvddisc.checkUpdate() + return self + + def doUpdate(self): + doupdate = False + children, toindex = gennameindexes('Chapter', self.dvdtitle) + children = sets.Set(children) + 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 + self.pathObjmap[i] = self.cd.addItem(self.id, + DVDChapter, i, dvdtitle = self.dvdtitle, + chapter = self.dvdtitle[toindex[i]]) + doupdate = True + + # sort our children + self.sort(lambda x, y: cmp(x.title, y.title)) + + if doupdate: + StorageFolder.doUpdate(self) + + +class DVDDisc(FSObject, StorageFolder): + def __init__(self, *args, **kwargs): + path = kwargs['path'] + del kwargs['path'] + + StorageFolder.__init__(self, *args, **kwargs) + FSObject.__init__(self, path) + + # mapping from path to objectID + self.pathObjmap = {} + + def doUpdate(self): + # open the DVD as necessary. + self.dvd = DVD(self.FSpath) + + doupdate = False + children, toindex = gennameindexes('Title', self.dvd) + children = sets.Set(children) + 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 + self.pathObjmap[i] = self.cd.addItem(self.id, DVDTitle, + i, dvdtitle = self.dvd[toindex[i]], dvddisc = self) + doupdate = True + + # sort our children + self.sort(lambda x, y: cmp(x.title, y.title)) + + if doupdate: + StorageFolder.doUpdate(self) + +def detectdvd(path, fobj): + if os.path.isdir(path): + log.msg('dvd isdir') + # Make sure we there is only a VIDEO_TS in there, even + # if there is a VIDEO_TS w/ other files, we will open + # the VIDEO_TS as a DVD (if it is one) + ld = os.listdir(path) + if ld == ['VIDEO_TS' ]: + pass + elif not filter(lambda x: x[:4] != 'VTS_' and + x[:9] != 'VIDEO_TS.', ld): + pass + else: + return None, None + + d = DVD(path) + return DVDDisc, { 'path': path } + +registerklassfun(detectdvd) diff --git a/pymediaserv b/pymediaserv index 1cb3e18..580512f 100755 --- a/pymediaserv +++ b/pymediaserv @@ -9,6 +9,7 @@ # # Modules to import, maybe config file or something? +import dvd import shoutcast import ZipStorage # w/ tarfile support, it will gobble up empty files!