MythTV  0.26-pre
mashups_api.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # -*- coding: UTF-8 -*-
00003 # ----------------------
00004 # Name: mashups_api - Simple-to-use Python interface to Mashups of RSS feeds and HTML video data
00005 #
00006 # Python Script
00007 # Author:   R.D. Vaughan
00008 # Purpose:  This python script is intended to perform a variety of utility functions to
00009 #           search and access text metadata, video and image URLs from various Internet sources.
00010 #
00011 # License:Creative Commons GNU GPL v2
00012 # (http://creativecommons.org/licenses/GPL/2.0/)
00013 #-------------------------------------
00014 __title__ ="mashups_api - Simple-to-use Python interface to Mashups of RSS feeds and HTML video data"
00015 __author__="R.D. Vaughan"
00016 __purpose__='''
00017 This python script is intended to perform a variety of utility functions to search and access text
00018 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
00019 "~/.mythtv/MythNetvision/userGrabberPrefs/xxxxMashup.xml" where "xxxx" is the specific mashup name matching the associated grabber name that calls these functions.
00020 '''
00021 
00022 __version__="v0.1.6"
00023 # 0.1.0 Initial development
00024 # 0.1.1 Added Search Mashup capabilities
00025 # 0.1.2 Fixed a couple of error messages with improper variable names
00026 # 0.1.3 Add the ability for a Mashup to search the "internetcontentarticles" table
00027 # 0.1.4 Add the ability for a Mashup to pass variables to a XSLT style sheet
00028 # 0.1.5 Removed a redundant build of the common XSLT function dictionary
00029 # 0.1.6 Corrected a bug were a users custom setting were not being updated properly
00030 
00031 import os, struct, sys, time, datetime, shutil, urllib
00032 from socket import gethostname, gethostbyname
00033 from copy import deepcopy
00034 import logging
00035 
00036 from mashups_exceptions import (MashupsUrlError, MashupsHttpError, MashupsRssError, MashupsVideoNotFound, MashupsConfigFileError, MashupsUrlDownloadError)
00037 
00038 class OutStreamEncoder(object):
00039     """Wraps a stream with an encoder"""
00040     def __init__(self, outstream, encoding=None):
00041         self.out = outstream
00042         if not encoding:
00043             self.encoding = sys.getfilesystemencoding()
00044         else:
00045             self.encoding = encoding
00046 
00047     def write(self, obj):
00048         """Wraps the output stream, encoding Unicode strings with the specified encoding"""
00049         if isinstance(obj, unicode):
00050             try:
00051                 self.out.write(obj.encode(self.encoding))
00052             except IOError:
00053                 pass
00054         else:
00055             try:
00056                 self.out.write(obj)
00057             except IOError:
00058                 pass
00059 
00060     def __getattr__(self, attr):
00061         """Delegate everything but write to the stream"""
00062         return getattr(self.out, attr)
00063 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
00064 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
00065 
00066 
00067 try:
00068     from StringIO import StringIO
00069     from lxml import etree
00070 except Exception, e:
00071     sys.stderr.write(u'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
00072     sys.exit(1)
00073 
00074 # Check that the lxml library is current enough
00075 # From the lxml documents it states: (http://codespeak.net/lxml/installation.html)
00076 # "If you want to use XPath, do not use libxml2 2.6.27. We recommend libxml2 2.7.2 or later"
00077 # Testing was performed with the Ubuntu 9.10 "python-lxml" version "2.1.5-1ubuntu2" repository package
00078 version = ''
00079 for digit in etree.LIBXML_VERSION:
00080     version+=str(digit)+'.'
00081 version = version[:-1]
00082 if version < '2.7.2':
00083     sys.stderr.write(u'''
00084 ! Error - The installed version of the "lxml" python library "libxml" version is too old.
00085           At least "libxml" version 2.7.2 must be installed. Your version is (%s).
00086 ''' % version)
00087     sys.exit(1)
00088 
00089 
00090 class Videos(object):
00091     """Main interface to any Mashup
00092     This is done to support a common naming framework for all python Netvision plugins
00093     no matter their site target.
00094 
00095     Supports MNV Mashup Search and Treeview methods
00096     The apikey is a not required for Mashups
00097     """
00098     def __init__(self,
00099                 apikey,
00100                 mythtv = True,
00101                 interactive = False,
00102                 select_first = False,
00103                 debug = False,
00104                 custom_ui = None,
00105                 language = None,
00106                 search_all_languages = False,
00107                 ):
00108         """apikey (str/unicode):
00109             Specify the target site API key. Applications need their own key in some cases
00110 
00111         mythtv (True/False):
00112             When True, the returned meta data is being returned has the key and values massaged to match MythTV
00113             When False, the returned meta data  is being returned matches what target site returned
00114 
00115         interactive (True/False): (This option is not supported by all target site apis)
00116             When True, uses built-in console UI is used to select the correct show.
00117             When False, the first search result is used.
00118 
00119         select_first (True/False): (This option is not supported currently implemented in any grabbers)
00120             Automatically selects the first series search result (rather
00121             than showing the user a list of more than one series).
00122             Is overridden by interactive = False, or specifying a custom_ui
00123 
00124         debug (True/False):
00125              shows verbose debugging information
00126 
00127         custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
00128             A callable subclass of interactive class (overrides interactive option)
00129 
00130         language (2 character language abbreviation): (This option is not supported by all target site apis)
00131             The language of the returned data. Is also the language search
00132             uses. Default is "en" (English). For full list, run..
00133 
00134         search_all_languages (True/False): (This option is not supported by all target site apis)
00135             By default, a Netvision grabber will only search in the language specified using
00136             the language option. When this is True, it will search for the
00137             show in any language
00138 
00139         """
00140         self.config = {}
00141 
00142         if apikey is not None:
00143             self.config['apikey'] = apikey
00144         else:
00145             pass    # Mashups does not require an apikey
00146 
00147         self.config['debug_enabled'] = debug # show debugging messages
00148         self.common = common
00149         self.common.debug = debug   # Set the common function debug level
00150 
00151         self.log_name = u'Mashups_Grabber'
00152         self.common.logger = self.common.initLogger(path=sys.stderr, log_name=self.log_name)
00153         self.logger = self.common.logger # Setups the logger (self.log.debug() etc)
00154 
00155         self.config['custom_ui'] = custom_ui
00156 
00157         self.config['interactive'] = interactive
00158 
00159         self.config['select_first'] = select_first
00160 
00161         if language:
00162             self.config['language'] = language
00163         else:
00164             self.config['language'] = u'en'
00165 
00166         self.config['search_all_languages'] = search_all_languages
00167 
00168         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", }
00169 
00170         # Channel details and search results
00171         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}
00172 
00173         self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/mashups.png'
00174 
00175         self.config[u'image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions
00176     # end __init__()
00177 
00178 ###########################################################################################################
00179 #
00180 # Start - Utility functions
00181 #
00182 ###########################################################################################################
00183 
00184     def setTreeViewIcon(self, dir_icon=None):
00185         '''Check if there is a specific generic tree view icon. If not default to the channel icon.
00186         return self.tree_dir_icon
00187         '''
00188         self.tree_dir_icon = self.channel_icon
00189         if not dir_icon:
00190             if not self.feed_icons.has_key(self.tree_key):
00191                 return self.tree_dir_icon
00192             if not self.feed_icons[self.tree_key].has_key(self.feed):
00193                 return self.tree_dir_icon
00194             if not dir_icon:
00195                 return self.tree_dir_icon
00196             dir_icon = self.feed_icons[self.tree_key][self.feed]
00197             if not dir_icon:
00198                 return self.tree_dir_icon
00199         self.tree_dir_icon = u'%%SHAREDIR%%/mythnetvision/icons/%s.png' % (dir_icon, )
00200         return self.tree_dir_icon
00201     # end setTreeViewIcon()
00202 
00203 
00204     def getMashupsConfig(self):
00205         ''' Read the MNV Mashups grabber "mashups_config.xml" configuration file
00206         return nothing
00207         '''
00208         # Read the grabber mashups_config.xml configuration file
00209         url = u'%s/nv_python_libs/configs/XML/mashups_config.xml' % (baseProcessingDir, )
00210         if not os.path.isfile(url):
00211             raise MashupsConfigFileError(self.error_messages['MashupsConfigFileError'] % (url, ))
00212 
00213         if self.config['debug_enabled']:
00214             print url
00215             print
00216         try:
00217             self.mashups_config = etree.parse(url)
00218         except Exception, errormsg:
00219             raise MashupsUrlError(self.error_messages['MashupsUrlError'] % (url, errormsg))
00220         return
00221     # end getMashupsConfig()
00222 
00223 
00224     def getUserPreferences(self):
00225         '''Read the mashups_config.xml and user preference xxxxxMashup.xml file.
00226         If the xxxxxMashup.xml file does not exist then copy the default.
00227         return nothing
00228         '''
00229         # Get mashups_config.xml
00230         self.getMashupsConfig()
00231 
00232         # Check if the mashups.xml file exists
00233         fileName = u'%s.xml' % self.mashup_title.replace(u'treeview', u'').replace(u'search', u'')
00234         userPreferenceFile = u'%s/%s' % (self.mashups_config.find('userPreferenceFile').text, fileName)
00235         if userPreferenceFile[0] == '~':
00236              self.mashups_config.find('userPreferenceFile').text = u"%s%s" % (os.path.expanduser(u"~"), userPreferenceFile[1:])
00237 
00238         # If the user config file does not exists then copy one from the default
00239         create = False
00240         defaultConfig = u'%s/nv_python_libs/configs/XML/defaultUserPrefs/%s' % (baseProcessingDir, fileName, )
00241         prefDir = self.mashups_config.find('userPreferenceFile').text.replace(u'/'+fileName, u'')
00242         if not os.path.isfile(self.mashups_config.find('userPreferenceFile').text):
00243             # Make the necessary directories if they do not already exist
00244             if not os.path.isdir(prefDir):
00245                 os.makedirs(prefDir)
00246             shutil.copy2(defaultConfig, self.mashups_config.find('userPreferenceFile').text)
00247             create = True
00248 
00249         # Read the grabber mashups_config.xml configuration file
00250         if self.config['debug_enabled']:
00251             print self.mashups_config.find('userPreferenceFile').text
00252             print
00253         try:
00254             self.userPrefs = etree.parse(self.mashups_config.find('userPreferenceFile').text)
00255         except Exception, errormsg:
00256             raise MashupsUrlError(self.error_messages['MashupsUrlError'] % (self.mashups_config.find('userPreferenceFile').text, errormsg))
00257 
00258         if create:
00259             return
00260 
00261         # Merge the existing entries with the user's current settings to get any distributed
00262         # additions or changes
00263         try:
00264             defaultPrefs = etree.parse(defaultConfig)
00265         except Exception, errormsg:
00266             raise MashupsUrlError(self.error_messages['MashupsUrlError'] % (defaultConfig, errormsg))
00267         urlFilter = etree.XPath('//sourceURL[@url=$url and @name=$name]', namespaces=self.common.namespaces)
00268         globalmaxFilter = etree.XPath('./../..', namespaces=self.common.namespaces)
00269         for sourceURL in self.userPrefs.xpath('//sourceURL'):
00270             url = sourceURL.attrib['url']
00271             name = sourceURL.attrib['name']
00272             defaultSourceURL = urlFilter(defaultPrefs, url=url, name=name)
00273             if len(defaultSourceURL):
00274                 defaultSourceURL[0].attrib['enabled'] = sourceURL.attrib['enabled']
00275                 if sourceURL.attrib.get('max'):
00276                     defaultSourceURL[0].attrib['max'] = sourceURL.attrib['max']
00277                 directory = globalmaxFilter(sourceURL)[0]
00278                 if directory.attrib.get('globalmax'):
00279                     defaultDir = directory.attrib.get('globalmax')
00280                     globalmaxFilter(defaultSourceURL[0])[0].attrib['globalmax'] = directory.attrib['globalmax']
00281 
00282         # Save the updated xxxxMashup.xml file
00283         tagName = defaultPrefs.getroot().tag
00284 
00285         # Get the internal documentaion
00286         docComment = u''
00287         for element in defaultPrefs.iter(tag=etree.Comment):
00288             docComment+=etree.tostring(element, encoding='UTF-8', pretty_print=True)[:-1]
00289 
00290         fd = open(self.mashups_config.find('userPreferenceFile').text, 'w')
00291         fd.write((u'<%s>\n' % tagName)+docComment)
00292         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))
00293         fd.close()
00294 
00295         # Make sure that the user preferences are using the latest version
00296         self.userPrefs = defaultPrefs
00297 
00298         return
00299     # end getUserPreferences()
00300 
00301 ###########################################################################################################
00302 #
00303 # End of Utility functions
00304 #
00305 ###########################################################################################################
00306 
00307     def searchForVideos(self, title, pagenumber):
00308         """Common name for a video search. Used to interface with MythTV plugin NetVision
00309         Display the results and exit
00310         """
00311         # Get the user preferences
00312         try:
00313             self.getUserPreferences()
00314         except Exception, e:
00315             sys.stderr.write(u'%s' % e)
00316             sys.exit(1)
00317 
00318         if self.config['debug_enabled']:
00319             print "self.userPrefs:"
00320             sys.stdout.write(etree.tostring(self.userPrefs, encoding='UTF-8', pretty_print=True))
00321             print
00322 
00323         # Import the mnvsearch_api.py functions
00324         fullPath = u'%s/nv_python_libs/%s' % (self.common.baseProcessingDir, 'mnvsearch')
00325         sys.path.append(fullPath)
00326         try:
00327             exec('''import mnvsearch_api''')
00328         except Exception, errmsg:
00329             sys.stderr.write(u'! Error: Dynamic import of mnvsearch_api functions\nmessage(%s)\n' % (errmsg))
00330             sys.exit(1)
00331         mnvsearch_api.common = self.common
00332         mnvsearch = mnvsearch_api.Videos(None, debug=self.config['debug_enabled'], language=self.config['language'])
00333         mnvsearch.page_limit = self.page_limit
00334 
00335         # Get the dictionary of mashups functions pointers
00336         self.common.buildFunctionDict()
00337 
00338         # Massage channel icon
00339         self.channel_icon = self.common.ampReplace(self.channel_icon)
00340 
00341         # Create RSS element tree
00342         rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
00343 
00344         # Add the Channel element tree
00345         self.channel['channel_title'] = self.grabber_title
00346         self.channel_icon = self.setTreeViewIcon(dir_icon=self.mashup_title.replace(u'Mashuptreeview', u''))
00347         channelTree = self.common.mnvChannelElement(self.channel)
00348         rssTree.append(channelTree)
00349 
00350         # Globally add all the xpath extentions to the "mythtv" namespace allowing access within the
00351         # XSLT stylesheets
00352         self.common.buildFunctionDict()
00353         mnvXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format')
00354         mnvXpath.prefix = 'mnvXpath'
00355         for key in self.common.functionDict.keys():
00356             mnvXpath[key] = self.common.functionDict[key]
00357 
00358         # Build Search parameter dictionary
00359         self.common.pagenumber = pagenumber
00360         self.common.page_limit = self.page_limit
00361         self.common.language = self.config['language']
00362         self.common.searchterm = title.encode("utf-8")
00363         searchParms = {
00364             'searchterm': urllib.quote_plus(title.encode("utf-8")),
00365             'pagemax': self.page_limit,
00366             'language': self.config['language'],
00367             }
00368         # Create a structure of feeds that can be concurrently downloaded
00369         xsltFilename = etree.XPath('./@xsltFile', namespaces=self.common.namespaces)
00370         sourceData = etree.XML(u'<xml></xml>')
00371         for source in self.userPrefs.xpath('//search//sourceURL[@enabled="true"]'):
00372             if source.attrib.get('mnvsearch'):
00373                 continue
00374             urlName = source.attrib.get('name')
00375             if urlName:
00376                  uniqueName = u'%s;%s' % (urlName, source.attrib.get('url'))
00377             else:
00378                 uniqueName = u'RSS;%s' % (source.attrib.get('url'))
00379             url = etree.XML(u'<url></url>')
00380             etree.SubElement(url, "name").text = uniqueName
00381             if source.attrib.get('pageFunction'):
00382                 searchParms['pagenum'] = self.common.functionDict[source.attrib['pageFunction']]('dummy', 'dummy')
00383             else:
00384                 searchParms['pagenum'] = pagenumber
00385             etree.SubElement(url, "href").text = source.attrib.get('url') % searchParms
00386             if len(xsltFilename(source)):
00387                 for xsltName in xsltFilename(source):
00388                     etree.SubElement(url, "xslt").text = xsltName.strip()
00389             etree.SubElement(url, "parserType").text = source.attrib.get('type')
00390             sourceData.append(url)
00391 
00392         if self.config['debug_enabled']:
00393             print "rssData:"
00394             sys.stdout.write(etree.tostring(sourceData, encoding='UTF-8', pretty_print=True))
00395             print
00396 
00397         # Get the source data
00398         if sourceData.find('url') != None:
00399             # Process each directory of the user preferences that have an enabled rss feed
00400             try:
00401                 resultTree = self.common.getUrlData(sourceData)
00402             except Exception, errormsg:
00403                 raise MashupsUrlDownloadError(self.error_messages['MashupsUrlDownloadError'] % (errormsg))
00404             if self.config['debug_enabled']:
00405                 print "resultTree:"
00406                 sys.stdout.write(etree.tostring(resultTree, encoding='UTF-8', pretty_print=True))
00407                 print
00408 
00409             # Process the results
00410             itemFilter = etree.XPath('.//item', namespaces=self.common.namespaces)
00411             # Create special directory elements
00412             for result in resultTree.findall('results'):
00413                 channelTree.xpath('numresults')[0].text = self.common.numresults
00414                 channelTree.xpath('returned')[0].text = self.common.returned
00415                 channelTree.xpath('startindex')[0].text = self.common.startindex
00416                 for item in itemFilter(result):
00417                     channelTree.append(item)
00418 
00419         # Process any mnvsearches
00420         for source in self.userPrefs.xpath('//search//sourceURL[@enabled="true" and @mnvsearch]'):
00421             results = mnvsearch.searchForVideos(title, pagenumber, feedtitle=source.xpath('./@mnvsearch')[0])
00422             if len(results[0].keys()):
00423                 channelTree.xpath('returned')[0].text = u'%s' % (int(channelTree.xpath('returned')[0].text)+results[1])
00424                 channelTree.xpath('startindex')[0].text = u'%s' % (int(channelTree.xpath('startindex')[0].text)+results[2])
00425                 channelTree.xpath('numresults')[0].text = u'%s' % (int(channelTree.xpath('numresults')[0].text)+results[3])
00426                 lastKey = None
00427                 for key in sorted(results[0].keys()):
00428                     if lastKey != key:
00429                         channelTree.append(results[0][key])
00430                         lastKey = key
00431 
00432         # Check that there was at least some items
00433         if len(rssTree.xpath('//item')):
00434             # Output the MNV Mashup results
00435             sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')
00436             sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True))
00437 
00438         sys.exit(0)
00439     # end searchForVideos()
00440 
00441 
00442     def displayTreeView(self):
00443         '''Gather the Mashups Internet sources then get the videos meta data in each of them
00444         Display the results and exit
00445         '''
00446         # Get the user preferences
00447         try:
00448             self.getUserPreferences()
00449         except Exception, e:
00450             sys.stderr.write(u'%s' % e)
00451             sys.exit(1)
00452 
00453         if self.config['debug_enabled']:
00454             print "self.userPrefs:"
00455             sys.stdout.write(etree.tostring(self.userPrefs, encoding='UTF-8', pretty_print=True))
00456             print
00457 
00458         # Massage channel icon
00459         self.channel_icon = self.common.ampReplace(self.channel_icon)
00460 
00461         # Create RSS element tree
00462         rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
00463 
00464         # Add the Channel element tree
00465         self.channel['channel_title'] = self.grabber_title
00466         self.channel_icon = self.setTreeViewIcon(dir_icon=self.mashup_title.replace(u'Mashuptreeview', u''))
00467         channelTree = self.common.mnvChannelElement(self.channel)
00468         rssTree.append(channelTree)
00469         self.common.language = self.config['language']
00470 
00471         # Globally add all the xpath extentions to the "mythtv" namespace allowing access within the
00472         # XSLT stylesheets
00473         self.common.buildFunctionDict()
00474         mnvXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format')
00475         mnvXpath.prefix = 'mnvXpath'
00476         for key in self.common.functionDict.keys():
00477             mnvXpath[key] = common.functionDict[key]
00478 
00479         # Create a structure of feeds that can be concurrently downloaded
00480         xsltFilename = etree.XPath('./@xsltFile', namespaces=self.common.namespaces)
00481         sourceData = etree.XML(u'<xml></xml>')
00482         for source in self.userPrefs.xpath('//directory//sourceURL[@enabled="true"]'):
00483             urlName = source.attrib.get('name')
00484             if urlName:
00485                  uniqueName = u'%s;%s' % (urlName, source.attrib.get('url'))
00486             else:
00487                 uniqueName = u'RSS;%s' % (source.attrib.get('url'))
00488             url = etree.XML(u'<url></url>')
00489             etree.SubElement(url, "name").text = uniqueName
00490             etree.SubElement(url, "href").text = source.attrib.get('url')
00491             if source.attrib.get('parameter') != None:
00492                 etree.SubElement(url, "parameter").text = source.attrib.get('parameter')
00493             if len(xsltFilename(source)):
00494                 for xsltName in xsltFilename(source):
00495                     etree.SubElement(url, "xslt").text = xsltName.strip()
00496             etree.SubElement(url, "parserType").text = source.attrib.get('type')
00497             sourceData.append(url)
00498 
00499         if self.config['debug_enabled']:
00500             print "rssData:"
00501             sys.stdout.write(etree.tostring(sourceData, encoding='UTF-8', pretty_print=True))
00502             print
00503 
00504         # Get the source data
00505         if sourceData.find('url') != None:
00506             # Process each directory of the user preferences that have an enabled rss feed
00507             try:
00508                 resultTree = self.common.getUrlData(sourceData)
00509             except Exception, errormsg:
00510                 raise MashupsUrlDownloadError(self.error_messages['MashupsUrlDownloadError'] % (errormsg))
00511             if self.config['debug_enabled']:
00512                 print "resultTree:"
00513                 sys.stdout.write(etree.tostring(resultTree, encoding='UTF-8', pretty_print=True))
00514                 print
00515             # Process the results
00516             categoryDir = None
00517             categoryElement = None
00518             xsltShowName = etree.XPath('//directory//sourceURL[@url=$url]/../@name', namespaces=self.common.namespaces)
00519             channelThumbnail = etree.XPath('.//directoryThumbnail', namespaces=self.common.namespaces)
00520             directoryFilter = etree.XPath('.//directory', namespaces=self.common.namespaces)
00521             itemFilter = etree.XPath('.//item', namespaces=self.common.namespaces)
00522             feedFilter = etree.XPath('//directory//sourceURL[@url=$url]')
00523             channelThumbnail = etree.XPath('.//directoryThumbnail', namespaces=self.common.namespaces)
00524             specialDirectoriesFilter = etree.XPath('.//specialDirectories')
00525             specialDirectoriesKeyFilter = etree.XPath('.//specialDirectories/*[name()=$name]/@key')
00526             specialDirectoriesDict = {}
00527             # Create special directory elements
00528             for result in resultTree.findall('results'):
00529                 if len(specialDirectoriesFilter(result)):
00530                     for element in specialDirectoriesFilter(result)[0]:
00531                         if not element.tag in specialDirectoriesDict.keys():
00532                             specialDirectoriesElement = etree.XML(u'<directory></directory>')
00533                             specialDirectoriesElement.attrib['name'] = element.attrib['dirname']
00534                             if element.attrib.get('count'):
00535                                 count = int(element.attrib['count'])
00536                             else:
00537                                 count = 1
00538                             if element.attrib.get('thumbnail'):
00539                                 specialDirectoriesElement.attrib['thumbnail'] = element.attrib['thumbnail']
00540                             else:
00541                                 specialDirectoriesElement.attrib['thumbnail'] = self.channel_icon
00542                             specialDirectoriesDict[element.tag] = [specialDirectoriesElement, element.attrib['reverse'], count]
00543             for result in resultTree.findall('results'):
00544                 names = result.find('name').text.split(u';')
00545                 names[0] = self.common.massageText(names[0])
00546                 if len(xsltShowName(self.userPrefs, url=names[1])):
00547                     names[0] =  self.common.massageText(xsltShowName(self.userPrefs, url=names[1])[0].strip())
00548                 if names[0] == 'RSS':
00549                     names[0] = self.common.massageText(rssName(result.find('result'))[0].text)
00550                 count = 0
00551                 urlMax = None
00552                 url = feedFilter(self.userPrefs, url=names[1])
00553                 if len(url):
00554                     if url[0].attrib.get('max'):
00555                         try:
00556                             urlMax = int(url[0].attrib.get('max'))
00557                         except:
00558                             pass
00559                     elif url[0].getparent().getparent().attrib.get('globalmax'):
00560                         try:
00561                             urlMax = int(url[0].getparent().getparent().attrib.get('globalmax'))
00562                         except:
00563                             pass
00564                     if urlMax == 0:
00565                         urlMax = None
00566 
00567                 # Create a new directory and/or subdirectory if required
00568                 if names[0] != categoryDir:
00569                     if categoryDir != None:
00570                         channelTree.append(categoryElement)
00571                     categoryElement = etree.XML(u'<directory></directory>')
00572                     categoryElement.attrib['name'] = names[0]
00573                     if len(channelThumbnail(result)):
00574                         categoryElement.attrib['thumbnail'] = channelThumbnail(result)[0].text
00575                     else:
00576                         categoryElement.attrib['thumbnail'] = self.channel_icon
00577                     categoryDir = names[0]
00578 
00579                 # Add the special directories videos
00580                 for key in specialDirectoriesDict.keys():
00581                     sortDict = {}
00582                     count = 0
00583                     for sortData in specialDirectoriesKeyFilter(result, name=key):
00584                         sortDict[sortData] = count
00585                         count+=1
00586                     if len(sortDict):
00587                         if specialDirectoriesDict[key][1] == 'true':
00588                             sortedKeys = sorted(sortDict.keys(), reverse=True)
00589                         else:
00590                             sortedKeys = sorted(sortDict.keys(), reverse=False)
00591                         if specialDirectoriesDict[key][2] == 0:
00592                             number = len(sortDict)
00593                         else:
00594                             number = specialDirectoriesDict[key][2]
00595                         for count in range(number):
00596                             if count == len(sortDict):
00597                                 break
00598                             specialDirectoriesDict[key][0].append(deepcopy(itemFilter(result)[sortDict[sortedKeys[count]]]))
00599 
00600                 if len(directoryFilter(result)):
00601                     for directory in directoryFilter(result):
00602                         if not len(itemFilter(directory)):
00603                             continue
00604                         tmpDirElement = etree.XML(u'<directory></directory>')
00605                         tmpDirElement.attrib['name'] = directory.attrib['name']
00606                         if directory.attrib.get('thumbnail'):
00607                             tmpDirElement.attrib['thumbnail'] = directory.attrib['thumbnail']
00608                         else:
00609                             tmpDirElement.attrib['thumbnail'] = self.channel_icon
00610                         count = 0
00611                         for item in itemFilter(directory):
00612                             tmpDirElement.append(item)
00613                             if urlMax:
00614                                 count+=1
00615                                 if count == urlMax:
00616                                     break
00617                         categoryElement.append(tmpDirElement)
00618                 else:
00619                     # Process the results through its XSLT stylesheet and append the results to the
00620                     # directory date and time order
00621                     count = 0
00622                     for item in itemFilter(result):
00623                         categoryElement.append(item)
00624                         if urlMax:
00625                             count+=1
00626                             if count == urlMax:
00627                                 break
00628 
00629             # Add the last directory processed and the "Special" directories
00630             if categoryElement != None:
00631                 if len(itemFilter(categoryElement)):
00632                     channelTree.append(categoryElement)
00633                 # Add the special directories videos
00634                 for key in specialDirectoriesDict.keys():
00635                     if len(itemFilter(specialDirectoriesDict[key][0])):
00636                         channelTree.append(specialDirectoriesDict[key][0])
00637 
00638         # Check that there was at least some items
00639         if len(rssTree.xpath('//item')):
00640             # Output the MNV Mashup results
00641             sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')
00642             sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True))
00643 
00644         sys.exit(0)
00645     # end displayTreeView()
00646 # end Videos() class
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends