|
MythTV
0.26-pre
|
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
1.7.6.1