MythTV  master
tedtalks_api.py
Go to the documentation of this file.
1 # -*- coding: UTF-8 -*-
2 
3 # ----------------------
4 # Name: tedtalks_api - Simple-to-use Python interface to the TedTalks RSS feeds
5 # (http://www.ted.com)
6 # Python Script
7 # Author: R.D. Vaughan
8 # Purpose: This python script is intended to perform a variety of utility functions to
9 # search and access text metadata, video and image URLs from TedTalks Web site.
10 #
11 # License:Creative Commons GNU GPL v2
12 # (http://creativecommons.org/licenses/GPL/2.0/)
13 #-------------------------------------
14 __title__ ="tedtalks_api - Simple-to-use Python interface to the TedTalks videos (http://www.ted.com)"
15 __author__="R.D. Vaughan"
16 __purpose__='''
17 This python script is intended to perform a variety of utility functions to search and access text
18 meta data, video and image URLs from the TedTalks Web site. These routines process videos
19 provided by TedTalks (http://www.ted.com). The specific TedTalks RSS feeds that are processed are controled through a user XML preference file usually found at
20 "~/.mythtv/MythNetvision/userGrabberPrefs/tedtalks.xml"
21 '''
22 
23 __version__="v0.1.0"
24 # 0.1.0 Initial development
25 
26 import os, struct, sys, re, time, datetime, shutil, urllib.request, urllib.parse, urllib.error
27 
28 import logging
29 from threading import Thread
30 from copy import deepcopy
31 from operator import itemgetter, attrgetter
32 
33 from .tedtalks_exceptions import (TedTalksUrlError, TedTalksHttpError, TedTalksRssError, TedTalksVideoNotFound, TedTalksConfigFileError, TedTalksUrlDownloadError)
34 import io
35 
36 class OutStreamEncoder(object):
37  """Wraps a stream with an encoder"""
38  def __init__(self, outstream, encoding=None):
39  self.out = outstream
40  if not encoding:
41  self.encoding = sys.getfilesystemencoding()
42  else:
43  self.encoding = encoding
44 
45  def write(self, obj):
46  """Wraps the output stream, encoding Unicode strings with the specified encoding"""
47  if isinstance(obj, str):
48  obj = obj.encode(self.encoding)
49  self.out.buffer.write(obj)
50 
51  def __getattr__(self, attr):
52  """Delegate everything but write to the stream"""
53  return getattr(self.out, attr)
54 
55 if isinstance(sys.stdout, io.TextIOWrapper):
56  sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
57  sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
58 
59 
60 try:
61  from io import StringIO
62  from lxml import etree
63 except Exception as e:
64  sys.stderr.write('\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
65  sys.exit(1)
66 
67 
68 # Used for debugging
69 #import nv_python_libs.mashups.mashups_api as target
70 try:
71  '''Import the python mashups support classes
72  '''
73  import nv_python_libs.mashups.mashups_api as mashups_api
74 except Exception as e:
75  sys.stderr.write('''
76 The subdirectory "nv_python_libs/mashups" containing the modules mashups_api and
77 mashups_exceptions.py (v0.1.0 or greater),
78 They should have been included with the distribution of tedtalks.py.
79 Error(%s)
80 ''' % e)
81  sys.exit(1)
82 if mashups_api.__version__ < '0.1.0':
83  sys.stderr.write("\n! Error: Your current installed mashups_api.py version is (%s)\nYou must at least have version (0.1.0) or higher.\n" % mashups_api.__version__)
84  sys.exit(1)
85 
86 
87 class Videos(object):
88  """Main interface to http://www.ted.com
89  This is done to support a common naming framework for all python Netvision plugins no matter their
90  site target.
91 
92  Supports search methods
93  The apikey is a not required to access http://www.ted.com
94  """
95  def __init__(self,
96  apikey,
97  mythtv = True,
98  interactive = False,
99  select_first = False,
100  debug = False,
101  custom_ui = None,
102  language = None,
103  search_all_languages = False,
104  ):
105  """apikey (str/unicode):
106  Specify the target site API key. Applications need their own key in some cases
107 
108  mythtv (True/False):
109  When True, the returned meta data is being returned has the key and values massaged to match MythTV
110  When False, the returned meta data is being returned matches what target site returned
111 
112  interactive (True/False): (This option is not supported by all target site apis)
113  When True, uses built-in console UI is used to select the correct show.
114  When False, the first search result is used.
115 
116  select_first (True/False): (This option is not supported currently implemented in any grabbers)
117  Automatically selects the first series search result (rather
118  than showing the user a list of more than one series).
119  Is overridden by interactive = False, or specifying a custom_ui
120 
121  debug (True/False):
122  shows verbose debugging information
123 
124  custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
125  A callable subclass of interactive class (overrides interactive option)
126 
127  language (2 character language abbreviation): (This option is not supported by all target site apis)
128  The language of the returned data. Is also the language search
129  uses. Default is "en" (English). For full list, run..
130 
131  search_all_languages (True/False): (This option is not supported by all target site apis)
132  By default, a Netvision grabber will only search in the language specified using
133  the language option. When this is True, it will search for the
134  show in any language
135 
136  """
137  self.config = {}
138 
139  if apikey is not None:
140  self.config['apikey'] = apikey
141  else:
142  pass # TedTalks does not require an apikey
143 
144  self.config['debug_enabled'] = debug # show debugging messages
145  self.common = common
146  self.common.debug = debug # Set the common function debug level
147 
148  self.log_name = 'TedTalks_Grabber'
149  self.common.logger = self.common.initLogger(path=sys.stderr, log_name=self.log_name)
150  self.logger = self.common.logger # Setups the logger (self.log.debug() etc)
151 
152  self.config['custom_ui'] = custom_ui
153 
154  self.config['interactive'] = interactive
155 
156  self.config['select_first'] = select_first
157 
158  self.config['search_all_languages'] = search_all_languages
159 
160  self.error_messages = {'TedTalksUrlError': "! Error: The URL (%s) cause the exception error (%s)\n", 'TedTalksHttpError': "! Error: An HTTP communications error with the TedTalks was raised (%s)\n", 'TedTalksRssError': "! Error: Invalid RSS meta data\nwas received from the TedTalks error (%s). Skipping item.\n", 'TedTalksVideoNotFound': "! Error: Video search with the TedTalks did not return any results (%s)\n", 'TedTalksConfigFileError': "! Error: tedtalks_config.xml file missing\nit should be located in and named as (%s).\n", 'TedTalksUrlDownloadError': "! Error: Downloading a RSS feed or Web page (%s).\n", }
161 
162  # Channel details and search results
163  self.channel = {'channel_title': 'TedTalks', 'channel_link': 'http://www.ted.com', 'channel_description': "TED is a small nonprofit devoted to Ideas Worth Spreading.", 'channel_numresults': 0, 'channel_returned': 1, 'channel_startindex': 0}
164 
165  self.channel_icon = '%SHAREDIR%/mythnetvision/icons/tedtalks.png'
166 
167  self.config['image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions
168 
169  # Initialize Mashups api variables
170  mashups_api.common = self.common
171  self.mashups_api = mashups_api.Videos('')
172  self.mashups_api.channel = self.channel
173  if language:
174  self.mashups_api.config['language'] = self.config['language']
175  self.mashups_api.config['debug_enabled'] = self.config['debug_enabled']
176  self.mashups_api.getUserPreferences = self.getUserPreferences
177  # end __init__()
178 
179 
184 
185  def getTedTalksConfig(self):
186  ''' Read the MNV TedTalks grabber "tedtalks_config.xml" configuration file
187  return nothing
188  '''
189  # Read the grabber tedtalks_config.xml configuration file
190  url = 'file://%s/nv_python_libs/configs/XML/tedtalks_config.xml' % (baseProcessingDir, )
191  if not os.path.isfile(url[7:]):
192  raise TedTalksConfigFileError(self.error_messages['TedTalksConfigFileError'] % (url[7:], ))
193 
194  if self.config['debug_enabled']:
195  print(url)
196  print()
197  try:
198  self.tedtalks_config = etree.parse(url)
199  except Exception as errormsg:
200  raise TedTalksUrlError(self.error_messages['TedTalksUrlError'] % (url, errormsg))
201  return
202  # end getTedTalksConfig()
203 
204 
206  '''Read the tedtalks_config.xml and user preference tedtalks.xml file.
207  If the tedtalks.xml file does not exist then create it.
208  If the tedtalks.xml file is too old then update it.
209  return nothing
210  '''
211  # Get tedtalks_config.xml
212  self.getTedTalksConfig()
213 
214  # Check if the tedtalks.xml file exists
215  userPreferenceFile = self.tedtalks_config.find('userPreferenceFile').text
216  if userPreferenceFile[0] == '~':
217  self.tedtalks_config.find('userPreferenceFile').text = "%s%s" % (os.path.expanduser("~"), userPreferenceFile[1:])
218  if os.path.isfile(self.tedtalks_config.find('userPreferenceFile').text):
219  # Read the grabber tedtalks_config.xml configuration file
220  url = 'file://%s' % (self.tedtalks_config.find('userPreferenceFile').text, )
221  if self.config['debug_enabled']:
222  print(url)
223  print()
224  try:
225  self.userPrefs = etree.parse(url)
226  except Exception as errormsg:
227  raise TedTalksUrlError(self.error_messages['TedTalksUrlError'] % (url, errormsg))
228  create = False
229  else:
230  create = True
231 
232  # If required create/update the tedtalks.xml file
233  self.updateTedTalks(create)
234  return
235  # end getUserPreferences()
236 
237  def updateTedTalks(self, create=False):
238  ''' Create or update the tedtalks.xml user preferences file
239  return nothing
240  '''
241  userDefaultFile = '%s/nv_python_libs/configs/XML/defaultUserPrefs/tedtalks.xml' % (baseProcessingDir, )
242  if os.path.isfile(userDefaultFile):
243  # Read the default tedtalks.xml user preferences file
244  url = 'file://%s' % (userDefaultFile, )
245  if self.config['debug_enabled']:
246  print(url)
247  print()
248  try:
249  userTedTalks = etree.parse(url)
250  except Exception as e:
251  raise TedTalksUrlError(self.error_messages['TedTalksUrlError'] % (url, e))
252  else:
253  raise Exception('!Error: The default TedTalk file is missing (%s)', userDefaultFile)
254 
255  # If there was an existing tedtalks.xml file then add any relevant user settings
256  # to this new tedtalks.xml
257  if not create:
258  for showElement in self.userPrefs.xpath("//sourceURL"):
259  showName = showElement.getparent().attrib['name']
260  sourceName = showElement.attrib['name']
261  elements = userTedTalks.xpath("//sourceURL[@name=$showName]", showName=showName,)
262  if len(elements):
263  elements[0].attrib['enabled'] = showElement.attrib['enabled']
264  elements[0].attrib['parameter'] = showElement.attrib['parameter']
265 
266  if self.config['debug_enabled']:
267  print("After any merging userTedTalks:")
268  sys.stdout.write(etree.tostring(userTedTalks, encoding='UTF-8', pretty_print=True))
269  print()
270 
271  # Save the tedtalks.xml file
272  prefDir = self.tedtalks_config.find('userPreferenceFile').text.replace('/tedtalks.xml', '')
273  if not os.path.isdir(prefDir):
274  os.makedirs(prefDir)
275  fd = open(self.tedtalks_config.find('userPreferenceFile').text, 'w')
276  fd.write(etree.tostring(userTedTalks, encoding='UTF-8', pretty_print=True))
277  fd.close()
278 
279  # Read the refreshed user config file
280  try:
281  self.userPrefs = etree.parse(self.tedtalks_config.find('userPreferenceFile').text)
282  self.mashups_api.userPrefs = self.userPrefs
283  except Exception as errormsg:
284  raise TedTalksUrlError(self.error_messages['TedTalksUrlError'] % (url, errormsg))
285  return
286  # end updateTedTalks()
287 
288 
293 
294  def searchTitle(self, title, pagenumber, pagelen):
295  '''Key word video search of the TedTalks web site
296  return an array of matching item elements
297  return
298  '''
299  searchVar = self.tedtalks_config.find('searchURLS').xpath(".//href")[0].text
300  try:
301  searchVar = searchVar.replace('SEARCHTERM', urllib.parse.quote_plus(title.encode("utf-8")))
302  searchVar = searchVar.replace('PAGENUM', str(pagenumber))
303  except UnicodeDecodeError:
304  searchVar = '?q=%s' % ()
305  searchVar = searchVar.replace('SEARCHTERM', urllib.parse.quote_plus(title))
306  searchVar = searchVar.replace('PAGENUM', str(pagenumber))
307  url = searchVar
308 
309  if self.config['debug_enabled']:
310  print(url)
311  print()
312 
313  self.tedtalks_config.find('searchURLS').xpath(".//href")[0].text = url
314 
315  # Globally add all the xpath extentions to the "mythtv" namespace allowing access within the
316  # XSLT stylesheets
317  self.common.buildFunctionDict()
318  mnvXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format')
319  mnvXpath.prefix = 'mnvXpath'
320  for key in list(self.common.functionDict.keys()):
321  mnvXpath[key] = common.functionDict[key]
322 
323  # Add the parameter element from the User preferences file
324  paraMeter = self.userPrefs.find('search').xpath("//search//sourceURL[@enabled='true']/@parameter")
325  if not len(paraMeter):
326  raise Exception('TedTalks User preferences file "tedtalks.xml" does not have an enabled search with a "parameter" attribute.')
327  etree.SubElement(self.tedtalks_config.find('searchURLS').xpath(".//url")[0], "parameter").text = paraMeter[0]
328 
329  # Perform a search
330  try:
331  resultTree = self.common.getUrlData(self.tedtalks_config.find('searchURLS'))
332  except Exception as errormsg:
333  raise TedTalksUrlDownloadError(self.error_messages['TedTalksUrlDownloadError'] % (errormsg))
334 
335  if resultTree is None:
336  raise TedTalksVideoNotFound("No TedTalks Video matches found for search value (%s)" % title)
337 
338  searchResults = resultTree.xpath('//result//item')
339  if not len(searchResults):
340  raise TedTalksVideoNotFound("No TedTalks Video matches found for search value (%s)" % title)
341 
342  return searchResults
343  # end searchTitle()
344 
345 
346  def searchForVideos(self, title, pagenumber):
347  """Common name for a video search. Used to interface with MythTV plugin NetVision
348  """
349  # Get tedtalks_config.xml
350  self.getUserPreferences()
351 
352  if self.config['debug_enabled']:
353  print("self.tedtalks_config:")
354  sys.stdout.write(etree.tostring(self.tedtalks_config, encoding='UTF-8', pretty_print=True))
355  print()
356 
357  # Easier for debugging
358 # print self.searchTitle(title, pagenumber, self.page_limit)
359 # print
360 # sys.exit()
361 
362  try:
363  data = self.searchTitle(title, pagenumber, self.page_limit)
364  except TedTalksVideoNotFound as msg:
365  sys.stderr.write("%s\n" % msg)
366  sys.exit(0)
367  except TedTalksUrlError as msg:
368  sys.stderr.write('%s\n' % msg)
369  sys.exit(1)
370  except TedTalksHttpError as msg:
371  sys.stderr.write(self.error_messages['TedTalksHttpError'] % msg)
372  sys.exit(1)
373  except TedTalksRssError as msg:
374  sys.stderr.write(self.error_messages['TedTalksRssError'] % msg)
375  sys.exit(1)
376  except Exception as e:
377  sys.stderr.write("! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e))
378  sys.exit(1)
379 
380  # Create RSS element tree
381  rssTree = etree.XML(self.common.mnvRSS+'</rss>')
382 
383  # Set the paging values
384  if len(data) == self.page_limit:
385  self.channel['channel_returned'] = len(data)
386  self.channel['channel_startindex'] = len(data)+(self.page_limit*(int(pagenumber)-1))
387  self.channel['channel_numresults'] = len(data)+(self.page_limit*(int(pagenumber)-1)+1)
388  else:
389  self.channel['channel_returned'] = len(data)+(self.page_limit*(int(pagenumber)-1))
390  self.channel['channel_startindex'] = len(data)
391  self.channel['channel_numresults'] = len(data)
392 
393  # Add the Channel element tree
394  channelTree = self.common.mnvChannelElement(self.channel)
395  rssTree.append(channelTree)
396 
397  for item in data:
398  channelTree.append(item)
399 
400  # Output the MNV search results
401  sys.stdout.write('<?xml version="1.0" encoding="UTF-8"?>\n')
402  sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True))
403  sys.exit(0)
404  # end searchForVideos()
405 
406  def displayTreeView(self):
407  '''Gather all videos for each TedTalks show
408  Display the results and exit
409  '''
410  self.mashups_api.page_limit = self.page_limit
411  self.mashups_api.grabber_title = self.grabber_title
412  self.mashups_api.mashup_title = self.mashup_title
413  self.mashups_api.channel_icon = self.channel_icon
414  self.mashups_api.mashup_title = 'tedtalks'
415 
416  # Easier for debugging
417 # self.mashups_api.displayTreeView()
418 # print
419 # sys.exit(1)
420 
421  try:
422  self.mashups_api.Search = False
424  except Exception as e:
425  sys.stderr.write("! Error: During a TedTalks Video treeview\nError(%s)\n" % (e))
426  sys.exit(1)
427 
428  sys.exit(0)
429  # end displayTreeView()
430 # end Videos() class
nv_python_libs.tedtalks.tedtalks_api.Videos.__init__
def __init__(self, apikey, mythtv=True, interactive=False, select_first=False, debug=False, custom_ui=None, language=None, search_all_languages=False)
Definition: tedtalks_api.py:95
nv_python_libs.tedtalks.tedtalks_api.Videos
Definition: tedtalks_api.py:87
nv_python_libs.tedtalks.tedtalks_exceptions.TedTalksUrlDownloadError
Definition: tedtalks_exceptions.py:47
nv_python_libs.tedtalks.tedtalks_exceptions.TedTalksVideoNotFound
Definition: tedtalks_exceptions.py:37
nv_python_libs.tedtalks.tedtalks_api.OutStreamEncoder.__init__
def __init__(self, outstream, encoding=None)
Definition: tedtalks_api.py:38
nv_python_libs.tedtalks.tedtalks_api.Videos.getTedTalksConfig
def getTedTalksConfig(self)
Start - Utility functions.
Definition: tedtalks_api.py:185
nv_python_libs.tedtalks.tedtalks_exceptions.TedTalksUrlError
Definition: tedtalks_exceptions.py:22
nv_python_libs.tedtalks.tedtalks_api.Videos.searchForVideos
def searchForVideos(self, title, pagenumber)
Definition: tedtalks_api.py:346
nv_python_libs.tedtalks.tedtalks_exceptions.TedTalksConfigFileError
Definition: tedtalks_exceptions.py:42
nv_python_libs.tedtalks.tedtalks_api.Videos.channel_icon
channel_icon
Definition: tedtalks_api.py:156
nv_python_libs.tedtalks.tedtalks_api.OutStreamEncoder.encoding
encoding
Definition: tedtalks_api.py:41
nv_python_libs.mashups.mashups_api
Definition: mashups_api.py:1
nv_python_libs.tedtalks.tedtalks_api.Videos.updateTedTalks
def updateTedTalks(self, create=False)
Definition: tedtalks_api.py:237
nv_python_libs.tedtalks.tedtalks_api.Videos.userPrefs
userPrefs
Definition: tedtalks_api.py:225
nv_python_libs.tedtalks.tedtalks_api.OutStreamEncoder.__getattr__
def __getattr__(self, attr)
Definition: tedtalks_api.py:51
nv_python_libs.tedtalks.tedtalks_api.Videos.logger
logger
Definition: tedtalks_api.py:141
nv_python_libs.tedtalks.tedtalks_api.OutStreamEncoder.write
def write(self, obj)
Definition: tedtalks_api.py:45
nv_python_libs.tedtalks.tedtalks_api.Videos.error_messages
error_messages
Definition: tedtalks_api.py:151
nv_python_libs.tedtalks.tedtalks_api.Videos.mashups_api
mashups_api
Definition: tedtalks_api.py:162
print
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)
Definition: vbi608extractor.cpp:29
nv_python_libs.tedtalks.tedtalks_api.Videos.tedtalks_config
tedtalks_config
Definition: tedtalks_api.py:198
nv_python_libs.tedtalks.tedtalks_api.Videos.log_name
log_name
Definition: tedtalks_api.py:139
nv_python_libs.tedtalks.tedtalks_api.Videos.displayTreeView
def displayTreeView(self)
Definition: tedtalks_api.py:406
nv_python_libs.tedtalks.tedtalks_api.Videos.config
config
Definition: tedtalks_api.py:128
nv_python_libs.tedtalks.tedtalks_api.Videos.common
common
Definition: tedtalks_api.py:136
nv_python_libs.tedtalks.tedtalks_api.Videos.getUserPreferences
def getUserPreferences(self)
Definition: tedtalks_api.py:205
find
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
Definition: dvbstreamhandler.cpp:363
nv_python_libs.tedtalks.tedtalks_api.OutStreamEncoder.out
out
Definition: tedtalks_api.py:39
nv_python_libs.tedtalks.tedtalks_api.Videos.channel
channel
Definition: tedtalks_api.py:154
nv_python_libs.tedtalks.tedtalks_api.OutStreamEncoder
Definition: tedtalks_api.py:36
nv_python_libs.tedtalks.tedtalks_api.Videos.searchTitle
def searchTitle(self, title, pagenumber, pagelen)
End of Utility functions.
Definition: tedtalks_api.py:294