diff --git a/Clip.py b/Clip.py new file mode 100644 index 0000000..56133dd --- /dev/null +++ b/Clip.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# Copyright 2009 John-Mark Gurney +'''MPEG-TS clip''' + +__version__ = '$Change: 1308 $' +# $Id: //depot/python/pymeds/main/shoutcast.py#22 $ + +import bisect +import email +import os.path + +from DIDLLite import VideoItem, Resource +from FSStorage import registerklassfun + +from twisted.web import static + +class ClipProxyFile: + def __init__(self, f, p): + self.fp = open(f) + self.p = p + self.pos = 0 + lp = p[-1] + self.size = lp[0] + lp[1] + + def read(self, s=None): + if s is None: + s = self.size - self.pos + + p = bisect.bisect_right(self.p, (self.pos,)) + if p > 0: + p -= 1 + # We might be able to do this with proper construction of + # self.p, but this is easier. + r = [] + fp = self.fp + records = iter(self.p[p:]) + while s: + rec = records.next() + diff = self.pos - rec[0] + rlen = min(s, rec[1] - diff) + fp.seek(rec[2] + diff) + r.append(fp.read(rlen)) + s -= rlen + self.pos += rlen + + return ''.join(r) + + def close(self): + self.fp.close() + + def seek(self, p, d=0): + assert d == 0 + self.pos = p + if self.pos > self.size: + self.pos = self.size + + def tell(self): + return self.pos + +class ClipProxy(static.File): + isLeaf = True + + synchronized = [ 'parsefile', 'getsize', 'open' ] + + def __init__(self, f, *args): + self.__mtime = None + static.File.__init__(self, f, *args) + self.parsefile(self.path) + + def parsefile(self, f): + if self.getModificationTime() == self.__mtime: + return + + self.__mtime = self.getModificationTime() + i = email.message_from_file(open(f)) + self.origfile = i['file'] + self.date = eval(i['datetuple'], { '__builtins__': {} }) + # date is UTC + p = [ map(int, x.split()) for x in i.get_payload().split('\n') if x ] + pos = 0 + self.pos = par = [] + for j in p: + l = j[1] - j[0] + 188 + par.append((pos, l, j[0])) + pos += l + self.__size = pos + + def getsize(self): + return self.__size + + def open(self): + return ClipProxyFile(self.origfile, self.pos) + + def restat(self): + static.File.restat(self) + self.parsefile(self.path) + +class ClipFile(VideoItem): + def __init__(self, *args, **kwargs): + file = kwargs.pop('file') + mimetype = 'video/mpeg' + kwargs['content'] = ClipProxy(file, mimetype) + VideoItem.__init__(self, *args, **kwargs) + self.url = '%s/%s' % (self.cd.urlbase, self.id) + self.res = Resource(self.url, 'http-get:*:%s:*' % mimetype) + +def detectclipfile(origpath, fobj): + path = os.path.basename(origpath) + ext = os.path.splitext(path)[1] + if ext == '.clip': + return ClipFile, { 'file': origpath } + + return None, None + +registerklassfun(detectclipfile) diff --git a/QACheckList b/QACheckList index 919e691..95044a1 100644 --- a/QACheckList +++ b/QACheckList @@ -1,7 +1,7 @@ General Functionality error w/o media dir - PS3 DSM-520 Cidero Intel + PS3 DSM-520 Cidero Intel DMC-250 Discovered remains after 30m FSStorage @@ -12,3 +12,5 @@ mpegtsmod shoutcast dvd pyvr +Clip +item diff --git a/README b/README index 98e4c3d..526b993 100644 --- a/README +++ b/README @@ -14,6 +14,7 @@ Tested basic functionality with the following devices and/or programs: Intel's Media Control Point and Media Renderer D-Link DSM-520 Sony PlayStation 3 + Linksys DMC-250 The Intel tools are good for testing (though Windows only) and are available at: @@ -82,7 +83,7 @@ v0.x: Ignore AppleDouble Resource Fork Files. (Maybe we should ignore all dot files.) Readded SOAPpy/fpconst requirement as it is necessary for twisted. - Made it a bit closer to a real twisted proejct, in the process, + Made it a bit closer to a real twisted project, in the process, pymediaserv looks more like a normal program. It allows you to override the media path and title of the server. Minor change to checkUpdate API, we didn't use the return value, so @@ -91,6 +92,11 @@ v0.x: This means that you can support streaming radio stations that provides a .pls file such as KCSM 91.1. Fix ShoutCast error handling to be correct. + Add support for an XML file containing an object so you can create + an item that points to an mp3 streaming url that isn't a .pls. + Add support for playing parts of TS streams. This is useful for + splitting apart a TS stream w/ multiple clips (like HDV) w/o + creating the files. v0.5: Support multiple SSDP servers on the same box. diff --git a/pymeds.py b/pymeds.py index 9f7775d..d376f55 100755 --- a/pymeds.py +++ b/pymeds.py @@ -3,7 +3,7 @@ # Licensed under the MIT license # http://opensource.org/licenses/mit-license.php # Copyright 2005, Tim Potter -# Copyright 2006 John-Mark Gurney +# Copyright 2006-2009 John-Mark Gurney __version__ = '$Change$' # $Id$ @@ -29,6 +29,7 @@ def tryloadmodule(mod): # mpegtsmod can be really expensive. modules = [ 'shoutcast', + 'Clip', 'pyvr', 'item', 'dvd',