MythTV  master
mashups_api.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3 # ----------------------
4 # Name: mashups_api - Simple-to-use Python interface to Mashups of RSS feeds and HTML video data
5 #
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 various Internet sources.
10 #
11 # License:Creative Commons GNU GPL v2
12 # (http://creativecommons.org/licenses/GPL/2.0/)
13 #-------------------------------------
14 __title__ ="mashups_api - Simple-to-use Python interface to Mashups of RSS feeds and HTML video data"
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 various Internet sources. These routines process the RSS feeds and information into MNV standard channel, directory and item RSS XML files. The specific Mashups are specified through a user XML preference file usually found at
19 "~/.mythtv/MythNetvision/userGrabberPrefs/xxxxMashup.xml" where "xxxx" is the specific mashup name matching the associated grabber name that calls these functions.
20 '''
21 
22 __version__="v0.1.6"
23 # 0.1.0 Initial development
24 # 0.1.1 Added Search Mashup capabilities
25 # 0.1.2 Fixed a couple of error messages with improper variable names
26 # 0.1.3 Add the ability for a Mashup to search the "internetcontentarticles" table
27 # 0.1.4 Add the ability for a Mashup to pass variables to a XSLT style sheet
28 # 0.1.5 Removed a redundant build of the common XSLT function dictionary
29 # 0.1.6 Corrected a bug were a users custom setting were not being updated properly
30 
31 import os, struct, sys, time, datetime, shutil, urllib
32 from socket import gethostname, gethostbyname
33 from copy import deepcopy
34 import logging
35 
36 from mashups_exceptions import (MashupsUrlError, MashupsHttpError, MashupsRssError, MashupsVideoNotFound, MashupsConfigFileError, MashupsUrlDownloadError)
37 
38 class OutStreamEncoder(object):
39  """Wraps a stream with an encoder"""
40  def __init__(self, outstream, encoding=None):
41  self.out = outstream
42  if not encoding:
43  self.encoding = sys.getfilesystemencoding()
44  else:
45  self.encoding = encoding
46 
47  def write(self, obj):
48  """Wraps the output stream, encoding Unicode strings with the specified encoding"""
49  if isinstance(obj, unicode):
50  try:
51  self.out.write(obj.encode(self.encoding))
52  except IOError:
53  pass
54  else:
55  try:
56  self.out.write(obj)
57  except IOError:
58  pass
59 
60  def __getattr__(self, attr):
61  """Delegate everything but write to the stream"""
62  return getattr(self.out, attr)
63 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
64 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
65 
66 
67 try:
68  from StringIO import StringIO
69  from lxml import etree
70 except Exception, e:
71  sys.stderr.write(u'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
72  sys.exit(1)
73 
74 # Check that the lxml library is current enough
75 # From the lxml documents it states: (http://codespeak.net/lxml/installation.html)
76 # "If you want to use XPath, do not use libxml2 2.6.27. We recommend libxml2 2.7.2 or later"
77 # Testing was performed with the Ubuntu 9.10 "python-lxml" version "2.1.5-1ubuntu2" repository package
78 version = ''
79 for digit in etree.LIBXML_VERSION:
80  version+=str(digit)+'.'
81 version = version[:-1]
82 if version < '2.7.2':
83  sys.stderr.write(u'''
84 ! Error - The installed version of the "lxml" python library "libxml" version is too old.
85  At least "libxml" version 2.7.2 must be installed. Your version is (%s).
86 ''' % version)
87  sys.exit(1)
88 
89 
90 class Videos(object):
91  """Main interface to any Mashup
92  This is done to support a common naming framework for all python Netvision plugins
93  no matter their site target.
94 
95  Supports MNV Mashup Search and Treeview methods
96  The apikey is a not required for Mashups
97  """
98  def __init__(self,
99  apikey,
100  mythtv = True,
101  interactive = False,
102  select_first = False,
103  debug = False,
104  custom_ui = None,
105  language = None,
106  search_all_languages = False,
107  ):
108  """apikey (str/unicode):
109  Specify the target site API key. Applications need their own key in some cases
110 
111  mythtv (True/False):
112  When True, the returned meta data is being returned has the key and values massaged to match MythTV
113  When False, the returned meta data is being returned matches what target site returned
114 
115  interactive (True/False): (This option is not supported by all target site apis)
116  When True, uses built-in console UI is used to select the correct show.
117  When False, the first search result is used.
118 
119  select_first (True/False): (This option is not supported currently implemented in any grabbers)
120  Automatically selects the first series search result (rather
121  than showing the user a list of more than one series).
122  Is overridden by interactive = False, or specifying a custom_ui
123 
124  debug (True/False):
125  shows verbose debugging information
126 
127  custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
128  A callable subclass of interactive class (overrides interactive option)
129 
130  language (2 character language abbreviation): (This option is not supported by all target site apis)
131  The language of the returned data. Is also the language search
132  uses. Default is "en" (English). For full list, run..
133 
134  search_all_languages (True/False): (This option is not supported by all target site apis)
135  By default, a Netvision grabber will only search in the language specified using
136  the language option. When this is True, it will search for the
137  show in any language
138 
139  """
140  self.config = {}
141 
142  if apikey is not None:
143  self.config['apikey'] = apikey
144  else:
145  pass # Mashups does not require an apikey
146 
147  self.config['debug_enabled'] = debug # show debugging messages
148  self.common = common
149  self.common.debug = debug # Set the common function debug level
150 
151  self.log_name = u'Mashups_Grabber'
152  self.common.logger = self.common.initLogger(path=sys.stderr, log_name=self.log_name)
153  self.logger = self.common.logger # Setups the logger (self.log.debug() etc)
154 
155  self.config['custom_ui'] = custom_ui
156 
157  self.config['interactive'] = interactive
158 
159  self.config['select_first'] = select_first
160 
161  if language:
162  self.config['language'] = language
163  else:
164  self.config['language'] = u'en'
165 
166  self.config['search_all_languages'] = search_all_languages
167 
168  self.error_messages = {'MashupsUrlError': u"! Error: The URL (%s) cause the exception error (%s)\n", 'MashupsHttpError': u"! Error: An HTTP communications error with the Mashups was raised (%s)\n", 'MashupsRssError': u"! Error: Invalid RSS meta data\nwas received from the Mashups error (%s). Skipping item.\n", 'MashupsVideoNotFound': u"! Error: Video search with the Mashups did not return any results (%s)\n", 'MashupsConfigFileError': u"! Error: mashups_config.xml file missing\nit should be located in and named as (%s).\n", 'MashupsUrlDownloadError': u"! Error: Downloading a RSS feed or Web page (%s).\n", }
169 
170  # Channel details and search results
171  self.channel = {'channel_title': u'', 'channel_link': u'http://www.mythtv.org/wiki/MythNetvision', 'channel_description': u"Mashups combines media from multiple sources to create a new work", 'channel_numresults': 0, 'channel_returned': 0, u'channel_startindex': 0}
172 
173  self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/mashups.png'
174 
175  self.config[u'image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions
176  # end __init__()
177 
178 
183 
184  def setTreeViewIcon(self, dir_icon=None):
185  '''Check if there is a specific generic tree view icon. If not default to the channel icon.
186  return self.tree_dir_icon
187  '''
189  if not dir_icon:
190  if not self.feed_icons.has_key(self.tree_key):
191  return self.tree_dir_icon
192  if not self.feed_icons[self.tree_key].has_key(self.feed):
193  return self.tree_dir_icon
194  if not dir_icon:
195  return self.tree_dir_icon
196  dir_icon = self.feed_icons[self.tree_key][self.feed]
197  if not dir_icon:
198  return self.tree_dir_icon
199  self.tree_dir_icon = u'%%SHAREDIR%%/mythnetvision/icons/%s.png' % (dir_icon, )
200  return self.tree_dir_icon
201  # end setTreeViewIcon()
202 
203 
204  def getMashupsConfig(self):
205  ''' Read the MNV Mashups grabber "mashups_config.xml" configuration file
206  return nothing
207  '''
208  # Read the grabber mashups_config.xml configuration file
209  url = u'%s/nv_python_libs/configs/XML/mashups_config.xml' % (baseProcessingDir, )
210  if not os.path.isfile(url):
211  raise MashupsConfigFileError(self.error_messages['MashupsConfigFileError'] % (url, ))
212 
213  if self.config['debug_enabled']:
214  print url
215  print
216  try:
217  self.mashups_config = etree.parse(url)
218  except Exception, errormsg:
219  raise MashupsUrlError(self.error_messages['MashupsUrlError'] % (url, errormsg))
220  return
221  # end getMashupsConfig()
222 
223 
225  '''Read the mashups_config.xml and user preference xxxxxMashup.xml file.
226  If the xxxxxMashup.xml file does not exist then copy the default.
227  return nothing
228  '''
229  # Get mashups_config.xml
230  self.getMashupsConfig()
231 
232  # Check if the mashups.xml file exists
233  fileName = u'%s.xml' % self.mashup_title.replace(u'treeview', u'').replace(u'search', u'')
234  userPreferenceFile = u'%s/%s' % (self.mashups_config.find('userPreferenceFile').text, fileName)
235  if userPreferenceFile[0] == '~':
236  self.mashups_config.find('userPreferenceFile').text = u"%s%s" % (os.path.expanduser(u"~"), userPreferenceFile[1:])
237 
238  # If the user config file does not exists then copy one from the default
239  create = False
240  defaultConfig = u'%s/nv_python_libs/configs/XML/defaultUserPrefs/%s' % (baseProcessingDir, fileName, )
241  prefDir = self.mashups_config.find('userPreferenceFile').text.replace(u'/'+fileName, u'')
242  if not os.path.isfile(self.mashups_config.find('userPreferenceFile').text):
243  # Make the necessary directories if they do not already exist
244  if not os.path.isdir(prefDir):
245  os.makedirs(prefDir)
246  shutil.copy2(defaultConfig, self.mashups_config.find('userPreferenceFile').text)
247  create = True
248 
249  # Read the grabber mashups_config.xml configuration file
250  if self.config['debug_enabled']:
251  print self.mashups_config.find('userPreferenceFile').text
252  print
253  try:
254  self.userPrefs = etree.parse(self.mashups_config.find('userPreferenceFile').text)
255  except Exception, errormsg:
256  raise MashupsUrlError(self.error_messages['MashupsUrlError'] % (self.mashups_config.find('userPreferenceFile').text, errormsg))
257 
258  if create:
259  return
260 
261  # Merge the existing entries with the user's current settings to get any distributed
262  # additions or changes
263  try:
264  defaultPrefs = etree.parse(defaultConfig)
265  except Exception, errormsg:
266  raise MashupsUrlError(self.error_messages['MashupsUrlError'] % (defaultConfig, errormsg))
267  urlFilter = etree.XPath('//sourceURL[@url=$url and @name=$name]', namespaces=self.common.namespaces)
268  globalmaxFilter = etree.XPath('./../..', namespaces=self.common.namespaces)
269  for sourceURL in self.userPrefs.xpath('//sourceURL'):
270  url = sourceURL.attrib['url']
271  name = sourceURL.attrib['name']
272  defaultSourceURL = urlFilter(defaultPrefs, url=url, name=name)
273  if len(defaultSourceURL):
274  defaultSourceURL[0].attrib['enabled'] = sourceURL.attrib['enabled']
275  if sourceURL.attrib.get('max'):
276  defaultSourceURL[0].attrib['max'] = sourceURL.attrib['max']
277  directory = globalmaxFilter(sourceURL)[0]
278  if directory.attrib.get('globalmax'):
279  defaultDir = directory.attrib.get('globalmax')
280  globalmaxFilter(defaultSourceURL[0])[0].attrib['globalmax'] = directory.attrib['globalmax']
281 
282  # Save the updated xxxxMashup.xml file
283  tagName = defaultPrefs.getroot().tag
284 
285  # Get the internal documentaion
286  docComment = u''
287  for element in defaultPrefs.iter(tag=etree.Comment):
288  docComment+=etree.tostring(element, encoding='UTF-8', pretty_print=True)[:-1]
289 
290  fd = open(self.mashups_config.find('userPreferenceFile').text, 'w')
291  fd.write((u'<%s>\n' % tagName)+docComment)
292  fd.write(u''.join(etree.tostring(element, encoding='UTF-8', pretty_print=True) for element in defaultPrefs.xpath(u'/%s/*' % tagName))+(u'</%s>\n'% tagName))
293  fd.close()
294 
295  # Make sure that the user preferences are using the latest version
296  self.userPrefs = defaultPrefs
297 
298  return
299  # end getUserPreferences()
300 
301 
306 
307  def searchForVideos(self, title, pagenumber):
308  """Common name for a video search. Used to interface with MythTV plugin NetVision
309  Display the results and exit
310  """
311  # Get the user preferences
312  try:
313  self.getUserPreferences()
314  except Exception, e:
315  sys.stderr.write(u'%s' % e)
316  sys.exit(1)
317 
318  if self.config['debug_enabled']:
319  print "self.userPrefs:"
320  sys.stdout.write(etree.tostring(self.userPrefs, encoding='UTF-8', pretty_print=True))
321  print
322 
323  # Import the mnvsearch_api.py functions
324  fullPath = u'%s/nv_python_libs/%s' % (self.common.baseProcessingDir, 'mnvsearch')
325  sys.path.append(fullPath)
326  try:
327  exec('''import mnvsearch_api''')
328  except Exception, errmsg:
329  sys.stderr.write(u'! Error: Dynamic import of mnvsearch_api functions\nmessage(%s)\n' % (errmsg))
330  sys.exit(1)
331  mnvsearch_api.common = self.common
332  mnvsearch = mnvsearch_api.Videos(None, debug=self.config['debug_enabled'], language=self.config['language'])
333  mnvsearch.page_limit = self.page_limit
334 
335  # Get the dictionary of mashups functions pointers
336  self.common.buildFunctionDict()
337 
338  # Massage channel icon
339  self.channel_icon = self.common.ampReplace(self.channel_icon)
340 
341  # Create RSS element tree
342  rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
343 
344  # Add the Channel element tree
345  self.channel['channel_title'] = self.grabber_title
346  self.channel_icon = self.setTreeViewIcon(dir_icon=self.mashup_title.replace(u'Mashuptreeview', u''))
347  channelTree = self.common.mnvChannelElement(self.channel)
348  rssTree.append(channelTree)
349 
350  # Globally add all the xpath extentions to the "mythtv" namespace allowing access within the
351  # XSLT stylesheets
352  self.common.buildFunctionDict()
353  mnvXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format')
354  mnvXpath.prefix = 'mnvXpath'
355  for key in self.common.functionDict.keys():
356  mnvXpath[key] = self.common.functionDict[key]
357 
358  # Build Search parameter dictionary
359  self.common.pagenumber = pagenumber
360  self.common.page_limit = self.page_limit
361  self.common.language = self.config['language']
362  self.common.searchterm = title.encode("utf-8")
363  searchParms = {
364  'searchterm': urllib.quote_plus(title.encode("utf-8")),
365  'pagemax': self.page_limit,
366  'language': self.config['language'],
367  }
368  # Create a structure of feeds that can be concurrently downloaded
369  xsltFilename = etree.XPath('./@xsltFile', namespaces=self.common.namespaces)
370  sourceData = etree.XML(u'<xml></xml>')
371  for source in self.userPrefs.xpath('//search//sourceURL[@enabled="true"]'):
372  if source.attrib.get('mnvsearch'):
373  continue
374  urlName = source.attrib.get('name')
375  if urlName:
376  uniqueName = u'%s;%s' % (urlName, source.attrib.get('url'))
377  else:
378  uniqueName = u'RSS;%s' % (source.attrib.get('url'))
379  url = etree.XML(u'<url></url>')
380  etree.SubElement(url, "name").text = uniqueName
381  if source.attrib.get('pageFunction'):
382  searchParms['pagenum'] = self.common.functionDict[source.attrib['pageFunction']]('dummy', 'dummy')
383  else:
384  searchParms['pagenum'] = pagenumber
385  etree.SubElement(url, "href").text = source.attrib.get('url') % searchParms
386  if len(xsltFilename(source)):
387  for xsltName in xsltFilename(source):
388  etree.SubElement(url, "xslt").text = xsltName.strip()
389  etree.SubElement(url, "parserType").text = source.attrib.get('type')
390  sourceData.append(url)
391 
392  if self.config['debug_enabled']:
393  print "rssData:"
394  sys.stdout.write(etree.tostring(sourceData, encoding='UTF-8', pretty_print=True))
395  print
396 
397  # Get the source data
398  if sourceData.find('url') != None:
399  # Process each directory of the user preferences that have an enabled rss feed
400  try:
401  resultTree = self.common.getUrlData(sourceData)
402  except Exception, errormsg:
403  raise MashupsUrlDownloadError(self.error_messages['MashupsUrlDownloadError'] % (errormsg))
404  if self.config['debug_enabled']:
405  print "resultTree:"
406  sys.stdout.write(etree.tostring(resultTree, encoding='UTF-8', pretty_print=True))
407  print
408 
409  # Process the results
410  itemFilter = etree.XPath('.//item', namespaces=self.common.namespaces)
411  # Create special directory elements
412  for result in resultTree.findall('results'):
413  channelTree.xpath('numresults')[0].text = self.common.numresults
414  channelTree.xpath('returned')[0].text = self.common.returned
415  channelTree.xpath('startindex')[0].text = self.common.startindex
416  for item in itemFilter(result):
417  channelTree.append(item)
418 
419  # Process any mnvsearches
420  for source in self.userPrefs.xpath('//search//sourceURL[@enabled="true" and @mnvsearch]'):
421  results = mnvsearch.searchForVideos(title, pagenumber, feedtitle=source.xpath('./@mnvsearch')[0])
422  if len(results[0].keys()):
423  channelTree.xpath('returned')[0].text = u'%s' % (int(channelTree.xpath('returned')[0].text)+results[1])
424  channelTree.xpath('startindex')[0].text = u'%s' % (int(channelTree.xpath('startindex')[0].text)+results[2])
425  channelTree.xpath('numresults')[0].text = u'%s' % (int(channelTree.xpath('numresults')[0].text)+results[3])
426  lastKey = None
427  for key in sorted(results[0].keys()):
428  if lastKey != key:
429  channelTree.append(results[0][key])
430  lastKey = key
431 
432  # Check that there was at least some items
433  if len(rssTree.xpath('//item')):
434  # Output the MNV Mashup results
435  sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')
436  sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True))
437 
438  sys.exit(0)
439  # end searchForVideos()
440 
441 
442  def displayTreeView(self):
443  '''Gather the Mashups Internet sources then get the videos meta data in each of them
444  Display the results and exit
445  '''
446  # Get the user preferences
447  try:
448  self.getUserPreferences()
449  except Exception, e:
450  sys.stderr.write(u'%s' % e)
451  sys.exit(1)
452 
453  if self.config['debug_enabled']:
454  print "self.userPrefs:"
455  sys.stdout.write(etree.tostring(self.userPrefs, encoding='UTF-8', pretty_print=True))
456  print
457 
458  # Massage channel icon
459  self.channel_icon = self.common.ampReplace(self.channel_icon)
460 
461  # Create RSS element tree
462  rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
463 
464  # Add the Channel element tree
465  self.channel['channel_title'] = self.grabber_title
466  self.channel_icon = self.setTreeViewIcon(dir_icon=self.mashup_title.replace(u'Mashuptreeview', u''))
467  channelTree = self.common.mnvChannelElement(self.channel)
468  rssTree.append(channelTree)
469  self.common.language = self.config['language']
470 
471  # Globally add all the xpath extentions to the "mythtv" namespace allowing access within the
472  # XSLT stylesheets
473  self.common.buildFunctionDict()
474  mnvXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format')
475  mnvXpath.prefix = 'mnvXpath'
476  for key in self.common.functionDict.keys():
477  mnvXpath[key] = common.functionDict[key]
478 
479  # Create a structure of feeds that can be concurrently downloaded
480  xsltFilename = etree.XPath('./@xsltFile', namespaces=self.common.namespaces)
481  sourceData = etree.XML(u'<xml></xml>')
482  for source in self.userPrefs.xpath('//directory//sourceURL[@enabled="true"]'):
483  urlName = source.attrib.get('name')
484  if urlName:
485  uniqueName = u'%s;%s' % (urlName, source.attrib.get('url'))
486  else:
487  uniqueName = u'RSS;%s' % (source.attrib.get('url'))
488  url = etree.XML(u'<url></url>')
489  etree.SubElement(url, "name").text = uniqueName
490  etree.SubElement(url, "href").text = source.attrib.get('url')
491  if source.attrib.get('parameter') != None:
492  etree.SubElement(url, "parameter").text = source.attrib.get('parameter')
493  if len(xsltFilename(source)):
494  for xsltName in xsltFilename(source):
495  etree.SubElement(url, "xslt").text = xsltName.strip()
496  etree.SubElement(url, "parserType").text = source.attrib.get('type')
497  sourceData.append(url)
498 
499  if self.config['debug_enabled']:
500  print "rssData:"
501  sys.stdout.write(etree.tostring(sourceData, encoding='UTF-8', pretty_print=True))
502  print
503 
504  # Get the source data
505  if sourceData.find('url') != None:
506  # Process each directory of the user preferences that have an enabled rss feed
507  try:
508  resultTree = self.common.getUrlData(sourceData)
509  except Exception, errormsg:
510  raise MashupsUrlDownloadError(self.error_messages['MashupsUrlDownloadError'] % (errormsg))
511  if self.config['debug_enabled']:
512  print "resultTree:"
513  sys.stdout.write(etree.tostring(resultTree, encoding='UTF-8', pretty_print=True))
514  print
515  # Process the results
516  categoryDir = None
517  categoryElement = None
518  xsltShowName = etree.XPath('//directory//sourceURL[@url=$url]/../@name', namespaces=self.common.namespaces)
519  channelThumbnail = etree.XPath('.//directoryThumbnail', namespaces=self.common.namespaces)
520  directoryFilter = etree.XPath('.//directory', namespaces=self.common.namespaces)
521  itemFilter = etree.XPath('.//item', namespaces=self.common.namespaces)
522  feedFilter = etree.XPath('//directory//sourceURL[@url=$url]')
523  channelThumbnail = etree.XPath('.//directoryThumbnail', namespaces=self.common.namespaces)
524  specialDirectoriesFilter = etree.XPath('.//specialDirectories')
525  specialDirectoriesKeyFilter = etree.XPath('.//specialDirectories/*[name()=$name]/@key')
526  specialDirectoriesDict = {}
527  # Create special directory elements
528  for result in resultTree.findall('results'):
529  if len(specialDirectoriesFilter(result)):
530  for element in specialDirectoriesFilter(result)[0]:
531  if not element.tag in specialDirectoriesDict.keys():
532  specialDirectoriesElement = etree.XML(u'<directory></directory>')
533  specialDirectoriesElement.attrib['name'] = element.attrib['dirname']
534  if element.attrib.get('count'):
535  count = int(element.attrib['count'])
536  else:
537  count = 1
538  if element.attrib.get('thumbnail'):
539  specialDirectoriesElement.attrib['thumbnail'] = element.attrib['thumbnail']
540  else:
541  specialDirectoriesElement.attrib['thumbnail'] = self.channel_icon
542  specialDirectoriesDict[element.tag] = [specialDirectoriesElement, element.attrib['reverse'], count]
543  for result in resultTree.findall('results'):
544  names = result.find('name').text.split(u';')
545  names[0] = self.common.massageText(names[0])
546  if len(xsltShowName(self.userPrefs, url=names[1])):
547  names[0] = self.common.massageText(xsltShowName(self.userPrefs, url=names[1])[0].strip())
548  if names[0] == 'RSS':
549  names[0] = self.common.massageText(rssName(result.find('result'))[0].text)
550  count = 0
551  urlMax = None
552  url = feedFilter(self.userPrefs, url=names[1])
553  if len(url):
554  if url[0].attrib.get('max'):
555  try:
556  urlMax = int(url[0].attrib.get('max'))
557  except:
558  pass
559  elif url[0].getparent().getparent().attrib.get('globalmax'):
560  try:
561  urlMax = int(url[0].getparent().getparent().attrib.get('globalmax'))
562  except:
563  pass
564  if urlMax == 0:
565  urlMax = None
566 
567  # Create a new directory and/or subdirectory if required
568  if names[0] != categoryDir:
569  if categoryDir != None:
570  channelTree.append(categoryElement)
571  categoryElement = etree.XML(u'<directory></directory>')
572  categoryElement.attrib['name'] = names[0]
573  if len(channelThumbnail(result)):
574  categoryElement.attrib['thumbnail'] = channelThumbnail(result)[0].text
575  else:
576  categoryElement.attrib['thumbnail'] = self.channel_icon
577  categoryDir = names[0]
578 
579  # Add the special directories videos
580  for key in specialDirectoriesDict.keys():
581  sortDict = {}
582  count = 0
583  for sortData in specialDirectoriesKeyFilter(result, name=key):
584  sortDict[sortData] = count
585  count+=1
586  if len(sortDict):
587  if specialDirectoriesDict[key][1] == 'true':
588  sortedKeys = sorted(sortDict.keys(), reverse=True)
589  else:
590  sortedKeys = sorted(sortDict.keys(), reverse=False)
591  if specialDirectoriesDict[key][2] == 0:
592  number = len(sortDict)
593  else:
594  number = specialDirectoriesDict[key][2]
595  for count in range(number):
596  if count == len(sortDict):
597  break
598  specialDirectoriesDict[key][0].append(deepcopy(itemFilter(result)[sortDict[sortedKeys[count]]]))
599 
600  if len(directoryFilter(result)):
601  for directory in directoryFilter(result):
602  if not len(itemFilter(directory)):
603  continue
604  tmpDirElement = etree.XML(u'<directory></directory>')
605  tmpDirElement.attrib['name'] = directory.attrib['name']
606  if directory.attrib.get('thumbnail'):
607  tmpDirElement.attrib['thumbnail'] = directory.attrib['thumbnail']
608  else:
609  tmpDirElement.attrib['thumbnail'] = self.channel_icon
610  count = 0
611  for item in itemFilter(directory):
612  tmpDirElement.append(item)
613  if urlMax:
614  count+=1
615  if count == urlMax:
616  break
617  categoryElement.append(tmpDirElement)
618  else:
619  # Process the results through its XSLT stylesheet and append the results to the
620  # directory date and time order
621  count = 0
622  for item in itemFilter(result):
623  categoryElement.append(item)
624  if urlMax:
625  count+=1
626  if count == urlMax:
627  break
628 
629  # Add the last directory processed and the "Special" directories
630  if categoryElement != None:
631  if len(itemFilter(categoryElement)):
632  channelTree.append(categoryElement)
633  # Add the special directories videos
634  for key in specialDirectoriesDict.keys():
635  if len(itemFilter(specialDirectoriesDict[key][0])):
636  channelTree.append(specialDirectoriesDict[key][0])
637 
638  # Check that there was at least some items
639  if len(rssTree.xpath('//item')):
640  # Output the MNV Mashup results
641  sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')
642  sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True))
643 
644  sys.exit(0)
645  # end displayTreeView()
646 # end Videos() class
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)
def setTreeViewIcon(self, dir_icon=None)
Start - Utility functions.
Definition: mashups_api.py:184
def searchForVideos(self, title, pagenumber)
End of Utility functions.
Definition: mashups_api.py:307
def __init__(self, apikey, mythtv=True, interactive=False, select_first=False, debug=False, custom_ui=None, language=None, search_all_languages=False)
Definition: mashups_api.py:98
def __init__(self, outstream, encoding=None)
Definition: mashups_api.py:40