#!/usr/bin/env python # Copyright 2008 John-Mark Gurney '''PVR Interface''' __version__ = '$Change: 1109 $' # $Id: //depot/python/pymeds/main/shoutcast.py#13 $ from DIDLLite import Container, Item, VideoItem, Resource from FSStorage import registerklassfun import os.path import time import twisted.web from twisted.internet import reactor from twisted.python import log def getPage(url, contextFactory=None, *args, **kwargs): """Download a web page as a string. Download a page. Return the HTTPClientFactory, which will callback with a page (as a string) or errback with a description of the error. See HTTPClientFactory to see what extra args can be passed. """ from twisted.web.client import _parse, HTTPClientFactory scheme, host, port, path = _parse(url) factory = HTTPClientFactory(url, *args, **kwargs) if scheme == 'https': from twisted.internet import ssl if contextFactory is None: contextFactory = ssl.ClientContextFactory() reactor.connectSSL(host, port, factory, contextFactory) else: reactor.connectTCP(host, port, factory) return factory class PYVRShow(VideoItem): def __init__(self, *args, **kwargs): baseurl = kwargs['url'] self.info = kwargs['info'] del kwargs['info'], kwargs['url'] VideoItem.__init__(self, *args, **kwargs) url = self.info['link'] sc = urlparse.urlparse(url) if not sc: # need to combine w/ base url url = urlparse.urljoin(baseurl, url) self.res = Resource(url, 'http-get:*:%s:*' % self.info['mimetype']) #self.res.size = self.chapter.size def doUpdate(self): pass import xml.sax import xml.sax.handler from xml.sax.saxutils import unescape class RecordingXML(xml.sax.handler.ContentHandler): def __init__(self): self.shows = {} self.data = None def characters(self, chars): if self.data is not None: self.data.append(chars) def startElement(self, name, attrs): if name in ('title', 'subtitle', 'mimetype', 'link', 'delete'): self.data = [] self.curel = name elif name == 'record': self.currec = {} def endElement(self, name): if name in ('title', 'subtitle', 'mimetype', 'link', 'delete'): data = unescape(''.join(self.data)) self.currec[self.curel] = data elif name == 'record': rec = self.currec try: self.shows[rec['title']].append(rec) except KeyError: self.shows[rec['title']] = [ rec ] self.data = None def recxmltoobj(page): obj = RecordingXML() xml.sax.parseString(page, obj) return obj.shows class PYVRShows(Container): def __init__(self, *args, **kwargs): self.pyvr = kwargs['pyvr'] del kwargs['pyvr'] self.show = kwargs['show'] del kwargs['show'] Container.__init__(self, *args, **kwargs) self.pathObjmap = {} self.shows = {} self.lastmodified = None def checkUpdate(self): self.pyvr.checkUpdate() if self.pyvr.lastmodified != self.lastmodified: self.doUpdate(self) @staticmethod def getunique(eps, ep): i = 1 while True: title = '%s Copy %d' % (ep['subtitle'], i) if not ret.has_key(title): return title i += 1 @staticmethod def eplisttodict(eps): ret = {} for i in eps: title = i['subtitle'] if ret.has_key(title): print 'WARNING: dup:', `i`, `ret[title]` title = self.getunique(ret, i) ret[title] = i return ret 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)) if doupdate: Container.doUpdate(self) class PYVR(Container): def __init__(self, *args, **kwargs): self.url = kwargs['url'] del kwargs['url'] Container.__init__(self, *args, **kwargs) self.pathObjmap = {} self.isPend = False self.lastmodified = None self.newobjs = None self.objs = {} self.lastcheck = 0 def checkUpdate(self): if self.isPend: print 'already' raise self.pend if time.time() - self.lastcheck < 5: print 'escape' return # Check to see if any changes have been made self.isPend = True self.lastcheck = time.time() self.page = getPage(self.url, method='HEAD') self.page.deferred.addCallback(self.doCheck) self.pend = self.page.deferred print 'doing raise' raise self.pend def doCheck(self, x): print 'got check' print `self.page.response_headers` slm = self.page.response_headers['last-modified'] if slm == self.lastmodified: # Page the same, don't do anything self.isPend = False return self.page = getPage(self.url) self.page.deferred.addCallback(self.parsePage) self.pend = self.page.deferred return self.pend def parsePage(self, page): print 'parsing' slm = self.page.response_headers['last-modified'] self.lastmodified = slm self.isPend = False del self.page del self.pend self.newobjs = recxmltoobj(page) self.doUpdate() def doUpdate(self): if self.newobjs is None: import traceback traceback.print_stack(file=log.logfile) return 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.newobjs = None # sort our children self.sort(lambda x, y: cmp(x.title, y.title)) if doupdate: Container.doUpdate(self) def detectpyvrfile(path, fobj): bn = os.path.basename(path) if bn == 'PYVR': print 'detected' return PYVR, { 'url': fobj.readline().strip() } return None, None registerklassfun(detectpyvrfile)