22__title__ =
"Netvision Common Query Processing";
23__author__=
"R.D.Vaughan"
38from 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)
60if isinstance(sys.stdout, io.TextIOWrapper):
66 from io
import StringIO
67 from lxml
import etree
69 sys.stderr.write(
'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
74 '''Methods that quering video Web sites for metadata and outputs the results to stdout any errors are output
86 search_all_languages = False,
88 """apikey (str/unicode):
89 Specify the themoviedb.org API key. Applications need their own key.
90 See http://api.themoviedb.org/2.1/ to get your own API key
93 When
True, the movie metadata
is being returned has the key
and values massaged to match MythTV
94 When
False, the movie metadata
is being returned matches what TMDB returned
96 interactive (
True/
False):
97 When
True, uses built-
in console UI
is used to select the correct show.
98 When
False, the first search result
is used.
100 select_first (
True/
False):
101 Automatically selects the first series search result (rather
102 than showing the user a list of more than one series).
103 Is overridden by interactive =
False,
or specifying a custom_ui
106 shows verbose debugging information
108 custom_ui (tvdb_ui.BaseUI subclass):
109 A callable subclass of tvdb_ui.BaseUI (overrides interactive option)
111 language (2 character language abbreviation):
112 The language of the returned data. Is also the language search
113 uses. Default
is "en" (English). For full list, run..
115 >>> MovieDb().config[
'valid_languages']
116 [
'da',
'fi',
'nl', ...]
118 search_all_languages (
True/
False):
119 By default, TMDB will only search
in the language specified using
120 the language option. When this
is True, it will search
for the
126 self.config['apikey'] = apikey
127 self.
config[
'target'] = target.Videos(apikey, mythtv = mythtv,
128 interactive = interactive,
129 select_first = select_first,
131 custom_ui = custom_ui,
133 search_all_languages = search_all_languages,)
134 self.
searchKeys = [
'title',
'releasedate',
'overview',
'url',
'thumbnail',
'video',
'runtime',
'viewcount']
136 self.
searchXML = {
'header':
"""<?xml version="1.0" encoding="UTF-8"?>""",
'rss':
"""
138xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
139xmlns:content="http://purl.org/rss/1.0/modules/content/"
140xmlns:cnettv="http://cnettv.com/mrss/"
141xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
142xmlns:media="http://search.yahoo.com/mrss/"
143xmlns:atom="http://www.w3.org/2005/Atom"
144xmlns:amp="http://www.adobe.com/amp/1.0"
145xmlns:dc="http://purl.org/dc/elements/1.1/"
146xmlns:mythtv="http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format">""",
'channel':
"""
148 <title>%(channel_title)s</title>
149 <link>%(channel_link)s</link>
150 <description>%(channel_description)s</description>
151 <numresults>%(channel_numresults)d</numresults>
152 <returned>%(channel_returned)d</returned>
153 <startindex>%(channel_startindex)d</startindex>""", 'item': """
155 <title>%(item_title)s</title>
156 <author>%(item_author)s</author>
157 <pubDate>%(item_pubdate)s</pubDate>
158 <description>%(item_description)s</description>
159 <link>%(item_link)s</link>
161 <media:thumbnail url=
'%(item_thumbnail)s'/>
162 <media:content url=
'%(item_url)s' duration=
'%(item_duration)s' width=
'%(item_width)s' height=
'%(item_height)s' lang=
'%(item_lang)s'/>
164 <rating>%(item_rating)s</rating>
165 </item>
""", 'end_channel': """
166 </channel>
""", 'end_rss': """
170 <nextpagetoken>%(nextpagetoken)s</nextpagetoken>""",
172 <prevpagetoken>%(prevpagetoken)s</prevpagetoken>"""
175 self.treeViewXML = {'header':
"""<?xml version="1.0" encoding="UTF-8"?>""",
'rss':
"""
177xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
178xmlns:content="http://purl.org/rss/1.0/modules/content/"
179xmlns:cnettv="http://cnettv.com/mrss/"
180xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
181xmlns:media="http://search.yahoo.com/mrss/"
182xmlns:atom="http://www.w3.org/2005/Atom"
183xmlns:amp="http://www.adobe.com/amp/1.0"
184xmlns:dc="http://purl.org/dc/elements/1.1/"
185xmlns:mythtv="http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format">""",
'start_channel':
"""
187 <title>%(channel_title)s</title>
188 <link>%(channel_link)s</link>
189 <description>%(channel_description)s</description>
190 <numresults>%(channel_numresults)d</numresults>
191 <returned>%(channel_returned)d</returned>
192 <startindex>%(channel_startindex)d</startindex>""", "start_directory": """
193 <directory name=
"%s" thumbnail=
"%s">
""", 'item': """
195 <title>%(item_title)s</title>
196 <author>%(item_author)s</author>
197 <pubDate>%(item_pubdate)s</pubDate>
198 <description>%(item_description)s</description>
199 <link>%(item_link)s</link>
201 <media:thumbnail url=
'%(item_thumbnail)s'/>
202 <media:content url=
'%(item_url)s' duration=
'%(item_duration)s' width=
'%(item_width)s' height=
'%(item_height)s' lang=
'%(item_lang)s'/>
204 <rating>%(item_rating)s</rating>
205 </item>
""", "end_directory": """
206 </directory>
""", 'end_channel': """
207 </channel>
""", 'end_rss': """
215 '''Search for vidoes that match the search text and output the results a XML to stdout
218 self.
config[
'target'].page_limit = self.page_limit
219 self.
config[
'target'].grabber_title = self.grabber_title
220 self.
config[
'target'].mashup_title = self.mashup_title
223 if data_sets
is None:
225 if not len(data_sets):
228 for data_set
in data_sets:
230 sys.stdout.write(self.
searchXML[
'header'])
233 sys.stdout.write(self.
searchXML[
'channel'] % data_set[0])
234 if 'nextpagetoken' in data_set[0]:
235 sys.stdout.write(self.
searchXML[
'nextpagetoken'] % data_set[0])
236 if 'prevpagetoken' in data_set[0]:
237 sys.stdout.write(self.
searchXML[
'prevpagetoken'] % data_set[0])
238 for item
in data_set[1]:
239 sys.stdout.write(self.
searchXML[
'item'] % item)
240 sys.stdout.write(self.
searchXML[
'end_channel'])
241 sys.stdout.write(self.
searchXML[
'end_rss'])
245 '''Create a Tree View specific to a target site and output the results a XML to stdout. Nested
246 directories are permissable.
249 self.
config[
'target'].page_limit = self.page_limit
250 self.
config[
'target'].grabber_title = self.grabber_title
251 self.
config[
'target'].mashup_title = self.mashup_title
254 if data_sets
is None:
256 if not len(data_sets):
259 for data_set
in data_sets:
264 sys.stdout.write(self.
treeViewXML[
'start_channel'] % data_set[0])
265 for directory
in data_set[1]:
266 if isinstance(directory, list):
267 if directory[0] ==
'':
268 sys.stdout.write(self.
treeViewXML[
'end_directory'])
270 sys.stdout.write(self.
treeViewXML[
'start_directory'] % (directory[0], directory[1]))
272 sys.stdout.write(self.
treeViewXML[
'item'] % directory)
278 '''Request a custom Web page from the grabber and display on stdout
281 self.
config[
'target'].page_limit = self.page_limit
282 self.
config[
'target'].grabber_title = self.grabber_title
283 self.
config[
'target'].mashup_title = self.mashup_title
284 self.
config[
'target'].HTMLvideocode = videocode
286 sys.stdout.write(self.
config[
'target'].displayCustomHTML())
293 '''Common processing for all Netvision python grabbers.
304 """Gets video details a search term
306 parser = OptionParser()
308 parser.add_option( "-d",
"--debug", action=
"store_true", default=
False, dest=
"debug",
309 help=
"Show debugging info (URLs, raw XML ... etc, info varies per grabber)")
310 parser.add_option(
"-u",
"--usage", action=
"store_true", default=
False, dest=
"usage",
311 help=
"Display examples for executing the script")
312 parser.add_option(
"-v",
"--version", action=
"store_true", default=
False, dest=
"version",
313 help=
"Display grabber name and supported options")
314 parser.add_option(
"-l",
"--language", metavar=
"LANGUAGE", default=
'', dest=
"language",
315 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.")
316 parser.add_option(
"-p",
"--pagenumber", metavar=
"PAGE NUMBER", default=1, dest=
"pagenumber",
317 help=
"Display specific page of the search results. Default is page 1.\nPage number is ignored with the Tree View option (-T).")
319 if self.grabberInfo[
'search']:
320 parser.add_option(
"-S",
"--search", action=
"store_true", default=
False, dest=
"search",
321 help=
"Search for videos")
324 if self.grabberInfo[
'tree']:
325 parser.add_option(
"-T",
"--treeview", action=
"store_true", default=
False, dest=
"treeview",
326 help=
"Display a Tree View of a sites videos")
329 if self.grabberInfo[
'html']:
330 parser.add_option(
"-H",
"--customhtml", action=
"store_true", default=
False,
331 dest=
"customhtml", help=
"Return a custom HTML Web page")
334 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'])
336 opts, args = parser.parse_args()
339 for index
in range(len(args)):
340 args[index] = str(args[index])
343 sys.stdout.write(
"\nopts: %s\n" % opts)
344 sys.stdout.write(
"\nargs: %s\n\n" % args)
349 if self.grabberInfo.
get(
'enabled',
False):
350 version = etree.XML(
'<grabber></grabber>')
352 version = etree.XML(
'<disabledgrabber></disabledgrabber>')
353 etree.SubElement(version,
"name").text = self.grabberInfo[
'title']
354 etree.SubElement(version,
"author").text = self.grabberInfo[
'author']
355 etree.SubElement(version,
"thumbnail").text = self.grabberInfo[
'thumbnail']
356 etree.SubElement(version,
"command").text = self.grabberInfo[
'command']
357 for t
in self.grabberInfo[
'type']:
358 etree.SubElement(version,
"type").text = t
359 etree.SubElement(version,
"description").text = self.grabberInfo[
'desc']
360 etree.SubElement(version,
"version").text = self.grabberInfo[
'version']
361 if self.grabberInfo[
'search']:
362 etree.SubElement(version,
"search").text =
'true'
363 if self.grabberInfo[
'tree']:
364 etree.SubElement(version,
"tree").text =
'true'
365 sys.stdout.write(etree.tostring(version, encoding=
'UTF-8', pretty_print=
True))
370 sys.stdout.write(self.grabberInfo[
'usage'])
373 if self.grabberInfo[
'search']:
374 if opts.search
and not len(args) == 1:
375 sys.stderr.write(
"! Error: There must be one value for the search option. Your options are (%s)\n" % (args))
377 if opts.search
and args[0] ==
'':
378 sys.stderr.write(
"! Error: There must be a non-empty search argument, yours is empty.\n")
381 if self.grabberInfo[
'html']:
382 if opts.customhtml
and not len(args) == 1:
383 sys.stderr.write(
"! Error: There must be one value for the search option. Your options are (%s)\n" % (args))
385 if opts.customhtml
and args[0] ==
'':
386 sys.stderr.write(
"! Error: There must be a non-empty Videocode argument, yours is empty.\n")
389 if not self.grabberInfo[
'search']
and not self.grabberInfo[
'tree']
and not self.grabberInfo[
'html']:
390 sys.stderr.write(
"! Error: You have not selected a valid option.\n")
396 select_first =
False,
399 language = opts.language,
400 search_all_languages =
False,)
403 if self.grabberInfo[
'search']:
405 if not 'SmaxPage' in list(self.grabberInfo.keys()):
406 Queries.page_limit = 20
408 Queries.page_limit = self.grabberInfo[
'SmaxPage']
411 if self.grabberInfo[
'tree']:
413 if not 'TmaxPage' in list(self.grabberInfo.keys()):
414 Queries.page_limit = 20
416 Queries.page_limit = self.grabberInfo[
'TmaxPage']
419 Queries.grabber_title = self.grabberInfo[
'title']
422 if not 'mashup_title' in list(self.grabberInfo.keys()):
423 Queries.mashup_title = Queries.grabber_title
425 if self.grabberInfo[
'search']:
427 Queries.mashup_title = self.grabberInfo[
'mashup_title'] +
"search"
428 if self.grabberInfo[
'tree']:
430 Queries.mashup_title = self.grabberInfo[
'mashup_title'] +
"treeview"
431 if self.grabberInfo[
'html']:
433 Queries.mashup_title = self.grabberInfo[
'mashup_title'] +
"customhtml"
436 if self.grabberInfo[
'search']:
439 Queries.searchForVideos(args[0], opts.pagenumber)
441 if self.grabberInfo[
'tree']:
444 Queries.displayTreeView()
446 if self.grabberInfo[
'html']:
448 Queries.displayHTML(args[0])
def __getattr__(self, attr)
def __init__(self, outstream, encoding=None)
def __init__(self, target, apikey)
def displayTreeView(self)
def searchForVideos(self, search_text, pagenumber)
def __init__(self, apikey, target, mythtv=False, interactive=False, select_first=False, debug=False, custom_ui=None, language=None, search_all_languages=False)
def displayHTML(self, videocode)