00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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
00024
00025
00026
00027
00028
00029
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
00075
00076
00077
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
00146
00147 self.config['debug_enabled'] = debug
00148 self.common = common
00149 self.common.debug = debug
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
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
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"]
00176
00177
00178
00179
00180
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
00202
00203
00204 def getMashupsConfig(self):
00205 ''' Read the MNV Mashups grabber "mashups_config.xml" configuration file
00206 return nothing
00207 '''
00208
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
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
00230 self.getMashupsConfig()
00231
00232
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
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
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
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
00262
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
00283 tagName = defaultPrefs.getroot().tag
00284
00285
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
00296 self.userPrefs = defaultPrefs
00297
00298 return
00299
00300
00301
00302
00303
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
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
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
00336 self.common.buildFunctionDict()
00337
00338
00339 self.channel_icon = self.common.ampReplace(self.channel_icon)
00340
00341
00342 rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
00343
00344
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
00351
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
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
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
00398 if sourceData.find('url') != None:
00399
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
00410 itemFilter = etree.XPath('.//item', namespaces=self.common.namespaces)
00411
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
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
00433 if len(rssTree.xpath('//item')):
00434
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
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
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
00459 self.channel_icon = self.common.ampReplace(self.channel_icon)
00460
00461
00462 rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
00463
00464
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
00472
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
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
00505 if sourceData.find('url') != None:
00506
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
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
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
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
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
00620
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
00630 if categoryElement != None:
00631 if len(itemFilter(categoryElement)):
00632 channelTree.append(categoryElement)
00633
00634 for key in specialDirectoriesDict.keys():
00635 if len(itemFilter(specialDirectoriesDict[key][0])):
00636 channelTree.append(specialDirectoriesDict[key][0])
00637
00638
00639 if len(rssTree.xpath('//item')):
00640
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
00646