22 __title__ =
"Netvision Common Query Processing";
23 __author__=
"R.D.Vaughan"
38 from optparse
import OptionParser
42 """Wraps a stream with an encoder"""
51 """Wraps the output stream, encoding Unicode strings with the specified encoding"""
52 if isinstance(obj, str):
54 self.
out.buffer.write(obj)
57 """Delegate everything but write to the stream"""
58 return getattr(self.
out, attr)
60 if isinstance(sys.stdout, io.TextIOWrapper):
66 from io
import StringIO
67 from lxml
import etree
68 except Exception
as e:
69 sys.stderr.write(
'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
77 for digit
in etree.LIBXML_VERSION:
78 version+=str(digit)+
'.'
79 version = version[:-1]
82 ! Error - The installed version of the "lxml" python library "libxml" version is too old.
83 At least "libxml" version 2.7.2 must be installed. Your version is (%s).
89 '''Methods that quering video Web sites for metadata and outputs the results to stdout any errors are output
101 search_all_languages = False,
103 """apikey (str/unicode):
104 Specify the themoviedb.org API key. Applications need their own key.
105 See http://api.themoviedb.org/2.1/ to get your own API key
108 When True, the movie metadata is being returned has the key and values massaged to match MythTV
109 When False, the movie metadata is being returned matches what TMDB returned
111 interactive (True/False):
112 When True, uses built-in console UI is used to select the correct show.
113 When False, the first search result is used.
115 select_first (True/False):
116 Automatically selects the first series search result (rather
117 than showing the user a list of more than one series).
118 Is overridden by interactive = False, or specifying a custom_ui
121 shows verbose debugging information
123 custom_ui (tvdb_ui.BaseUI subclass):
124 A callable subclass of tvdb_ui.BaseUI (overrides interactive option)
126 language (2 character language abbreviation):
127 The language of the returned data. Is also the language search
128 uses. Default is "en" (English). For full list, run..
130 >>> MovieDb().config['valid_languages'] #doctest: +ELLIPSIS
131 ['da', 'fi', 'nl', ...]
133 search_all_languages (True/False):
134 By default, TMDB will only search in the language specified using
135 the language option. When this is True, it will search for the
142 self.
config[
'target'] = target.Videos(apikey, mythtv = mythtv,
143 interactive = interactive,
144 select_first = select_first,
146 custom_ui = custom_ui,
148 search_all_languages = search_all_languages,)
149 self.
searchKeys = [
'title',
'releasedate',
'overview',
'url',
'thumbnail',
'video',
'runtime',
'viewcount']
151 self.
searchXML = {
'header':
"""<?xml version="1.0" encoding="UTF-8"?>""",
'rss':
"""
153 xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
154 xmlns:content="http://purl.org/rss/1.0/modules/content/"
155 xmlns:cnettv="http://cnettv.com/mrss/"
156 xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
157 xmlns:media="http://search.yahoo.com/mrss/"
158 xmlns:atom="http://www.w3.org/2005/Atom"
159 xmlns:amp="http://www.adobe.com/amp/1.0"
160 xmlns:dc="http://purl.org/dc/elements/1.1/"
161 xmlns:mythtv="http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format">""",
'channel':
"""
163 <title>%(channel_title)s</title>
164 <link>%(channel_link)s</link>
165 <description>%(channel_description)s</description>
166 <numresults>%(channel_numresults)d</numresults>
167 <returned>%(channel_returned)d</returned>
168 <startindex>%(channel_startindex)d</startindex>""",
'item':
"""
170 <title>%(item_title)s</title>
171 <author>%(item_author)s</author>
172 <pubDate>%(item_pubdate)s</pubDate>
173 <description>%(item_description)s</description>
174 <link>%(item_link)s</link>
176 <media:thumbnail url='%(item_thumbnail)s'/>
177 <media:content url='%(item_url)s' duration='%(item_duration)s' width='%(item_width)s' height='%(item_height)s' lang='%(item_lang)s'/>
179 <rating>%(item_rating)s</rating>
180 </item>""",
'end_channel':
"""
181 </channel>""",
'end_rss':
"""
185 <nextpagetoken>%(nextpagetoken)s</nextpagetoken>""",
187 <prevpagetoken>%(prevpagetoken)s</prevpagetoken>"""
190 self.
treeViewXML = {
'header':
"""<?xml version="1.0" encoding="UTF-8"?>""",
'rss':
"""
192 xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
193 xmlns:content="http://purl.org/rss/1.0/modules/content/"
194 xmlns:cnettv="http://cnettv.com/mrss/"
195 xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
196 xmlns:media="http://search.yahoo.com/mrss/"
197 xmlns:atom="http://www.w3.org/2005/Atom"
198 xmlns:amp="http://www.adobe.com/amp/1.0"
199 xmlns:dc="http://purl.org/dc/elements/1.1/"
200 xmlns:mythtv="http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format">""",
'start_channel':
"""
202 <title>%(channel_title)s</title>
203 <link>%(channel_link)s</link>
204 <description>%(channel_description)s</description>
205 <numresults>%(channel_numresults)d</numresults>
206 <returned>%(channel_returned)d</returned>
207 <startindex>%(channel_startindex)d</startindex>""",
"start_directory":
"""
208 <directory name="%s" thumbnail="%s">""",
'item':
"""
210 <title>%(item_title)s</title>
211 <author>%(item_author)s</author>
212 <pubDate>%(item_pubdate)s</pubDate>
213 <description>%(item_description)s</description>
214 <link>%(item_link)s</link>
216 <media:thumbnail url='%(item_thumbnail)s'/>
217 <media:content url='%(item_url)s' duration='%(item_duration)s' width='%(item_width)s' height='%(item_height)s' lang='%(item_lang)s'/>
219 <rating>%(item_rating)s</rating>
220 </item>""",
"end_directory":
"""
221 </directory>""",
'end_channel':
"""
222 </channel>""",
'end_rss':
"""
230 '''Search for vidoes that match the search text and output the results a XML to stdout
233 self.
config[
'target'].page_limit = self.page_limit
234 self.
config[
'target'].grabber_title = self.grabber_title
235 self.
config[
'target'].mashup_title = self.mashup_title
238 if data_sets
is None:
240 if not len(data_sets):
243 for data_set
in data_sets:
245 sys.stdout.write(self.
searchXML[
'header'])
248 sys.stdout.write(self.
searchXML[
'channel'] % data_set[0])
249 if 'nextpagetoken' in data_set[0]:
250 sys.stdout.write(self.
searchXML[
'nextpagetoken'] % data_set[0])
251 if 'prevpagetoken' in data_set[0]:
252 sys.stdout.write(self.
searchXML[
'prevpagetoken'] % data_set[0])
253 for item
in data_set[1]:
254 sys.stdout.write(self.
searchXML[
'item'] % item)
255 sys.stdout.write(self.
searchXML[
'end_channel'])
256 sys.stdout.write(self.
searchXML[
'end_rss'])
260 '''Create a Tree View specific to a target site and output the results a XML to stdout. Nested
261 directories are permissable.
264 self.
config[
'target'].page_limit = self.page_limit
265 self.
config[
'target'].grabber_title = self.grabber_title
266 self.
config[
'target'].mashup_title = self.mashup_title
269 if data_sets
is None:
271 if not len(data_sets):
274 for data_set
in data_sets:
279 sys.stdout.write(self.
treeViewXML[
'start_channel'] % data_set[0])
280 for directory
in data_set[1]:
281 if isinstance(directory, list):
282 if directory[0] ==
'':
283 sys.stdout.write(self.
treeViewXML[
'end_directory'])
285 sys.stdout.write(self.
treeViewXML[
'start_directory'] % (directory[0], directory[1]))
287 sys.stdout.write(self.
treeViewXML[
'item'] % directory)
293 '''Request a custom Web page from the grabber and display on stdout
296 self.
config[
'target'].page_limit = self.page_limit
297 self.
config[
'target'].grabber_title = self.grabber_title
298 self.
config[
'target'].mashup_title = self.mashup_title
299 self.
config[
'target'].HTMLvideocode = videocode
301 sys.stdout.write(self.
config[
'target'].displayCustomHTML())
308 '''Common processing for all Netvision python grabbers.
319 """Gets video details a search term
321 parser = OptionParser()
323 parser.add_option(
"-d",
"--debug", action=
"store_true", default=
False, dest=
"debug",
324 help=
"Show debugging info (URLs, raw XML ... etc, info varies per grabber)")
325 parser.add_option(
"-u",
"--usage", action=
"store_true", default=
False, dest=
"usage",
326 help=
"Display examples for executing the script")
327 parser.add_option(
"-v",
"--version", action=
"store_true", default=
False, dest=
"version",
328 help=
"Display grabber name and supported options")
329 parser.add_option(
"-l",
"--language", metavar=
"LANGUAGE", default=
'', dest=
"language",
330 help=
"Select data that matches the specified language fall back to English if nothing found (e.g. 'es' EspaƱol, 'de' Deutsch ... etc).\nNot all sites or grabbers support this option.")
331 parser.add_option(
"-p",
"--pagenumber", metavar=
"PAGE NUMBER", default=1, dest=
"pagenumber",
332 help=
"Display specific page of the search results. Default is page 1.\nPage number is ignored with the Tree View option (-T).")
334 if self.grabberInfo[
'search']:
335 parser.add_option(
"-S",
"--search", action=
"store_true", default=
False, dest=
"search",
336 help=
"Search for videos")
339 if self.grabberInfo[
'tree']:
340 parser.add_option(
"-T",
"--treeview", action=
"store_true", default=
False, dest=
"treeview",
341 help=
"Display a Tree View of a sites videos")
344 if self.grabberInfo[
'html']:
345 parser.add_option(
"-H",
"--customhtml", action=
"store_true", default=
False,
346 dest=
"customhtml", help=
"Return a custom HTML Web page")
349 parser.usage=
"./%%prog -hduvl%s [parameters] <search text>\nVersion: %s Author: %s\n\nFor details on the MythTV Netvision plugin see the wiki page at:\nhttp://www.mythtv.org/wiki/MythNetvision" % (functionality, self.grabberInfo[
'version'], self.grabberInfo[
'author'])
351 opts, args = parser.parse_args()
354 for index
in range(len(args)):
355 args[index] = str(args[index])
358 sys.stdout.write(
"\nopts: %s\n" % opts)
359 sys.stdout.write(
"\nargs: %s\n\n" % args)
364 if self.grabberInfo.
get(
'enabled',
False):
365 version = etree.XML(
'<grabber></grabber>')
367 version = etree.XML(
'<disabledgrabber></disabledgrabber>')
368 etree.SubElement(version,
"name").text = self.grabberInfo[
'title']
369 etree.SubElement(version,
"author").text = self.grabberInfo[
'author']
370 etree.SubElement(version,
"thumbnail").text = self.grabberInfo[
'thumbnail']
371 etree.SubElement(version,
"command").text = self.grabberInfo[
'command']
372 for t
in self.grabberInfo[
'type']:
373 etree.SubElement(version,
"type").text = t
374 etree.SubElement(version,
"description").text = self.grabberInfo[
'desc']
375 etree.SubElement(version,
"version").text = self.grabberInfo[
'version']
376 if self.grabberInfo[
'search']:
377 etree.SubElement(version,
"search").text =
'true'
378 if self.grabberInfo[
'tree']:
379 etree.SubElement(version,
"tree").text =
'true'
380 sys.stdout.write(etree.tostring(version, encoding=
'UTF-8', pretty_print=
True))
385 sys.stdout.write(self.grabberInfo[
'usage'])
388 if self.grabberInfo[
'search']:
389 if opts.search
and not len(args) == 1:
390 sys.stderr.write(
"! Error: There must be one value for the search option. Your options are (%s)\n" % (args))
392 if opts.search
and args[0] ==
'':
393 sys.stderr.write(
"! Error: There must be a non-empty search argument, yours is empty.\n")
396 if self.grabberInfo[
'html']:
397 if opts.customhtml
and not len(args) == 1:
398 sys.stderr.write(
"! Error: There must be one value for the search option. Your options are (%s)\n" % (args))
400 if opts.customhtml
and args[0] ==
'':
401 sys.stderr.write(
"! Error: There must be a non-empty Videocode argument, yours is empty.\n")
404 if not self.grabberInfo[
'search']
and not self.grabberInfo[
'tree']
and not self.grabberInfo[
'html']:
405 sys.stderr.write(
"! Error: You have not selected a valid option.\n")
411 select_first =
False,
414 language = opts.language,
415 search_all_languages =
False,)
418 if self.grabberInfo[
'search']:
420 if not 'SmaxPage' in list(self.grabberInfo.keys()):
421 Queries.page_limit = 20
423 Queries.page_limit = self.grabberInfo[
'SmaxPage']
426 if self.grabberInfo[
'tree']:
428 if not 'TmaxPage' in list(self.grabberInfo.keys()):
429 Queries.page_limit = 20
431 Queries.page_limit = self.grabberInfo[
'TmaxPage']
434 Queries.grabber_title = self.grabberInfo[
'title']
437 if not 'mashup_title' in list(self.grabberInfo.keys()):
438 Queries.mashup_title = Queries.grabber_title
440 if self.grabberInfo[
'search']:
442 Queries.mashup_title = self.grabberInfo[
'mashup_title'] +
"search"
443 if self.grabberInfo[
'tree']:
445 Queries.mashup_title = self.grabberInfo[
'mashup_title'] +
"treeview"
446 if self.grabberInfo[
'html']:
448 Queries.mashup_title = self.grabberInfo[
'mashup_title'] +
"customhtml"
451 if self.grabberInfo[
'search']:
454 Queries.searchForVideos(args[0], opts.pagenumber)
456 if self.grabberInfo[
'tree']:
459 Queries.displayTreeView()
461 if self.grabberInfo[
'html']:
463 Queries.displayHTML(args[0])