|
- #!/usr/bin/env python
- # Copyright 2008 John-Mark Gurney <jmg@funktaht.com>
- '''PVR Interface'''
-
- __version__ = '$Change: 1743 $'
- # $Id: //depot/python/pymeds/main/pyvr.py#10 $
-
- from DIDLLite import Container, Item, VideoItem, Resource
- from FSStorage import registerklassfun, FSObject
-
- import os.path
- import time
- from twisted.internet import reactor
- from twisted.python import log
- import twisted.web
- import urllib.parse
- import urllib.request, urllib.error, urllib.parse
-
- 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 = urllib.parse.urlparse(url)[0]
- if not sc:
- # need to combine w/ base url
- url = urllib.parse.urljoin(baseurl, url)
- self.res = Resource(url,
- 'http-get:*:%s:*' % self.info['mimetype'])
- self.res.duration = self.info['duration']
- try:
- self.date = self.info['startdateiso']
- except KeyError:
- pass
-
- def doUpdate(self):
- pass
-
- import xml.sax
- import xml.sax.handler
- from xml.sax.saxutils import unescape
-
- class RecordingXML(xml.sax.handler.ContentHandler):
- dataels = ('title', 'startdateiso', 'subtitle', 'duration',
- 'mimetype', 'link', 'delete', )
-
- 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 self.dataels:
- self.data = []
- self.curel = name
- elif name == 'record':
- self.currec = {}
-
- def endElement(self, name):
- if name in self.dataels:
- 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.shows = {}
- self.lastmodified = None
-
- def checkUpdate(self):
- self.pyvr.checkUpdate()
- if self.pyvr.lastmodified != self.lastmodified:
- self.doUpdate()
-
- @staticmethod
- def getunique(eps, ep):
- i = 1
- while True:
- title = '%s Copy %d' % (ep['subtitle'], i)
- if title not in eps:
- return title
- i += 1
-
- @staticmethod
- def eplisttodict(eps):
- ret = {}
- for pos, i in enumerate(eps):
- title = i['subtitle']
- if title in ret:
- print('WARNING: dup:', repr(i), repr(ret[title]))
- title = PYVRShows.getunique(ret, i)
- i['pos'] = pos
- ret[title] = i
-
- return ret
-
- def genChildren(self):
- return self.eplisttodict(self.pyvr.shows[self.show])
-
- def createObject(self, i, arg):
- return PYVRShow, i, (), { 'url': self.pyvr.url, 'info': arg }
-
- def sort(self):
- Container.sort(self, lambda x, y: cmp(x.info['pos'],
- y.info['pos']))
-
- def doUpdate(self):
- Container.doUpdate(self)
-
- self.lastmodified = self.pyvr.lastmodified
-
- class PYVR(FSObject, Container):
- def __init__(self, *args, **kwargs):
- self.url = kwargs.pop('url')
-
- FSObject.__init__(self, kwargs.pop('path'))
- Container.__init__(self, *args, **kwargs)
-
- #self.pend = None
- self.lastmodified = None
- self.newobjs = None
- self.objs = {}
- self.lastcheck = 0
-
- def checkUpdate(self):
- #if self.pend is not None:
- # raise self.pend
-
- if time.time() - self.lastcheck < 10:
- return
-
- # Check to see if any changes have been made
- self.runCheck()
-
- def runCheck(self):
- while True:
- try:
- self.page = urllib.request.urlopen(self.url)
- break
- except urllib.error.HTTPError:
- time.sleep(.1)
- #self.page = getPage(self.url, method='HEAD')
- #self.page.deferred.addErrback(self.errCheck).addCallback(
- # self.doCheck)
- #self.pend = self.page.deferred
- return self.doCheck(self.page)
-
- def errCheck(self, x):
- print('errCheck:', repr(x))
- self.runCheck()
-
- def doCheck(self, x):
- #print 'doCheck:', self.page.status
- #if self.page.status != '200':
- # print 'foo'
- # return reactor.callLater(.01, self.runCheck)
-
- self.lastcheck = time.time()
- slm = self.page.info()['last-modified']
- if slm == self.lastmodified:
- # Page the same, don't do anything
- #self.pend = None
- return
-
- return self.parsePage(self.page)
- #self.page = getPage(self.url)
- #self.page.deferred.addCallback(self.parsePage)
- #self.pend = self.page.deferred
-
- #return self.pend
-
- def parsePage(self, page):
- slm = self.page.info()['last-modified']
- self.lastmodified = slm
- del self.page
- #self.pend = None
-
- self.newobjs = recxmltoobj(page.read())
- print('pp:', repr(self.newobjs))
- self.doUpdate()
-
- def genChildren(self):
- return list(self.newobjs.keys())
-
- def createObject(self, i, arg=None):
- print('co:', repr(i), repr(arg))
- return PYVRShows, i, (), { 'show': i, 'pyvr': self }
-
- def doUpdate(self):
- if self.newobjs is None:
- import traceback
- traceback.print_stack(file=log.logfile)
- raise ValueError('did not get shows')
-
- self.shows = self.newobjs
-
- Container.doUpdate(self)
-
- self.newobjs = None
-
- def detectpyvrfile(path, fobj):
- bn = os.path.basename(path)
- if bn == 'PYVR':
- return PYVR, { 'url': fobj.readline().strip(), 'path': path }
- return None, None
-
- registerklassfun(detectpyvrfile)
|