MythTV master
pbsXSL_api.py
Go to the documentation of this file.
1# -*- coding: UTF-8 -*-
2
3# ----------------------
4# Name: pbsXSL_api - XPath and XSLT functions for the PBS RSS/HTML itmes
5# Python Script
6# Author: R.D. Vaughan
7# Purpose: This python script is intended to perform a variety of utility functions
8# for the conversion of data to the MNV standard RSS output format.
9# See this link for the specifications:
10# http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format
11#
12# License:Creative Commons GNU GPL v2
13# (http://creativecommons.org/licenses/GPL/2.0/)
14#-------------------------------------
15__title__ ="pbsXSL_api - XPath and XSLT functions for the PBS RSS/HTML"
16__author__="R.D. Vaughan"
17__purpose__='''
18This python script is intended to perform a variety of utility functions
19for the conversion of data to the MNV standard RSS output format.
20See this link for the specifications:
21http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format
22'''
23
24__version__="v0.1.0"
25# 0.1.0 Initial development
26
27
28# Specify the class names that have XPath extention functions
29__xpathClassList__ = ['xpathFunctions', ]
30
31# Specify the XSLT extention class names. Each class is a stand lone extention function
32#__xsltExtentionList__ = ['xsltExtExample', ]
33__xsltExtentionList__ = []
34
35import os, sys, re, time, datetime, shutil, urllib.request, urllib.parse, urllib.error, string
36from copy import deepcopy
37
38
39class OutStreamEncoder(object):
40 """Wraps a stream with an encoder"""
41 def __init__(self, outstream, encoding=None):
42 self.out = outstream
43 if not encoding:
44 self.encoding = sys.getfilesystemencoding()
45 else:
46 self.encoding = encoding
47
48 def write(self, obj):
49 """Wraps the output stream, encoding Unicode strings with the specified encoding"""
50 if isinstance(obj, str):
51 obj = obj.encode(self.encoding)
52 try:
53 self.out.buffer.write(obj)
54 except OSError:
55 pass
56
57 def __getattr__(self, attr):
58 """Delegate everything but write to the stream"""
59 return getattr(self.out, attr)
60
61if isinstance(sys.stdout, io.TextIOWrapper):
62 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
63 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
64
65try:
66 from io import StringIO
67 from lxml import etree
68except Exception as e:
69 sys.stderr.write('\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
70 sys.exit(1)
71
72
73class xpathFunctions(object):
74 """Functions specific extending XPath
75 """
76 def __init__(self):
77 self.functList = ['pbsTitleSeriesEpisodeLink', 'pbsDuration', 'pbsDownloadlink']
79 # Season 8: Episode 1
80 re.compile('''^.+?Season\\ (?P<seasno>[0-9]+)\\:\\ Episode\\ (?P<epno>[0-9]+).*$''', re.UNICODE),
81 # Season 8, Episode 1
82 re.compile('''^.+?Season\\ (?P<seasno>[0-9]+)\\,\\ Episode\\ (?P<epno>[0-9]+).*$''', re.UNICODE),
83 # Episode 1:
84 re.compile('''^.+?Episode\\ (?P<epno>[0-9]+).*$''', re.UNICODE),
85 ]
86 self.namespaces = {
87 'media': "http://search.yahoo.com/mrss/",
88 'xhtml': "http://www.w3.org/1999/xhtml",
89 'mythtv': "http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format",
90 'mediaad': "http://PBS/dtd/mediaad/1.0",
91 }
92 self.persistence = {}
93 # end __init__()
94
95
100
101 def pbsTitleSeriesEpisodeLink(self, context, *arg):
102 '''Generate a link for the PBS site.
103 Call example: 'mnvXpath:pbsTitleSeriesEpisodeLink(normalize-space(./title),normalize-space(./link))/*'
104 return the title, link and season and episode number elements if any were in the title
105 '''
106 tmpTitle = arg[0]
107 tmpLink = arg[1]
108
109 # Set the custom HTML PBS link
110 tmpVideoCode = tmpLink.replace('http://video.pbs.org/video/', '')[:-1]
111 self.persistence[tmpLink] = common.linkWebPage('dummy', 'pbs')
112
113 # Parse out any Season
114 seasonNumber = None
115 episodeNumber = None
116 for index in range(len(self.seriesEpisodeRegex)):
117 match = self.seriesEpisodeRegex[index].match(tmpTitle)
118 if match:
119 if index < 2:
120 (seasonNumber, episodeNumber) = match.groups()
121 break
122 else:
123 episodeNumber = match.groups()[0]
124 break
125
126 # Massage video title
127 index = tmpTitle.rfind('|')
128 if index != -1:
129 tmpTitle = tmpTitle[index+1:].strip()
130 if seasonNumber and episodeNumber:
131 tmpTitle = 'S%02dE%02d: %s' % (int(seasonNumber), int(episodeNumber), tmpTitle)
132 elif episodeNumber:
133 index = tmpTitle.find(':')
134 if index != -1:
135 tmpTitle = tmpTitle[index+1:].strip()
136 tmpTitle = 'Ep%02d: %s' % (int(episodeNumber), tmpTitle)
137 self.persistence[tmpLink] = common.linkWebPage('dummy', 'pbs').replace('TITLE', urllib.parse.quote(tmpTitle)).replace('VIDEOCODE', tmpVideoCode)
138
139 # Make the elements to include in the item
140 elementTmp = etree.XML('<xml></xml>')
141 etree.SubElement(elementTmp, "title").text = tmpTitle
142 if seasonNumber:
143 etree.SubElement(elementTmp, "season").text = "%s" % int(seasonNumber)
144 if episodeNumber:
145 etree.SubElement(elementTmp, "episode").text = "%s" % int(episodeNumber)
146 etree.SubElement(elementTmp, "link").text = self.persistence[tmpLink]
147 return elementTmp
148 # end pbsTitleSeriesEpisodeLink()
149
150 def pbsDuration(self, context, *arg):
151 '''Return the video duration in seconds
152 Call example: 'mnvXpath:pbsDuration(normalize-string(./dd/p[@class="info"]/span[@class="time"]))'
153 return the video duration
154 '''
155 if not arg[0]:
156 return ''
157 return arg[0][:-3]
158 # end pbsDuration()
159
160 def pbsDownloadlink(self, context, *arg):
161 '''Return a previously created download link
162 Call example: 'mnvXpath:pbsDownloadlink(normalize-space(./link))'
163 return the url link
164 '''
165 downloadLink = self.persistence[arg[0]]
166 del self.persistence[arg[0]]
167 return downloadLink
168 # end pbsDownloadlink()
169
170
175
176
181
182
def __init__(self, outstream, encoding=None)
Definition: pbsXSL_api.py:41
def pbsTitleSeriesEpisodeLink(self, context, *arg)
Start of XPath extension functions.
Definition: pbsXSL_api.py:101