|
MythTV
0.26-pre
|
00001 #!/usr/bin/env python 00002 # -*- coding: UTF-8 -*- 00003 # ---------------------- 00004 # Name: bbciplayer_api - Simple-to-use Python interface to the BBC iPlayer RSS feeds 00005 # (http://www.bbc.co.uk) 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 BBC iPlayer Web site. 00010 # 00011 # License:Creative Commons GNU GPL v2 00012 # (http://creativecommons.org/licenses/GPL/2.0/) 00013 #------------------------------------- 00014 __title__ ="bbciplayer_api - Simple-to-use Python interface to the BBC iPlayer RSS feeds (http://www.bbc.co.uk)" 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 the BBC iPlayer Web site. These routines process RSS feeds 00019 provided by BBC (http://www.bbc.co.uk). The specific BBC iPlayer RSS feeds that are processed are controled through a user XML preference file usually found at 00020 "~/.mythtv/MythNetvision/userGrabberPrefs/bbciplayer.xml" 00021 ''' 00022 00023 __version__="v0.1.3" 00024 # 0.1.0 Initial development 00025 # 0.1.1 Changed the logger to only output to stderr rather than a file 00026 # 0.1.2 Fixed incorrect URL creation for RSS feed Web pages 00027 # Restricted custom HTML web pages to Video media only. Audio will only play from its Web page. 00028 # Add the "customhtml" tag to search and treeviews 00029 # Removed the need for python MythTV bindings and added "%SHAREDIR%" to icon directory path 00030 # 0.1.3 Fixed search due to BBC Web site changes 00031 00032 import os, struct, sys, re, time, datetime, shutil, urllib, re 00033 import logging 00034 from socket import gethostname, gethostbyname 00035 from threading import Thread 00036 from copy import deepcopy 00037 from operator import itemgetter, attrgetter 00038 from MythTV import MythXML 00039 from bbciplayer_exceptions import (BBCUrlError, BBCHttpError, BBCRssError, BBCVideoNotFound, BBCConfigFileError, BBCUrlDownloadError) 00040 00041 class OutStreamEncoder(object): 00042 """Wraps a stream with an encoder""" 00043 def __init__(self, outstream, encoding=None): 00044 self.out = outstream 00045 if not encoding: 00046 self.encoding = sys.getfilesystemencoding() 00047 else: 00048 self.encoding = encoding 00049 00050 def write(self, obj): 00051 """Wraps the output stream, encoding Unicode strings with the specified encoding""" 00052 if isinstance(obj, unicode): 00053 try: 00054 self.out.write(obj.encode(self.encoding)) 00055 except IOError: 00056 pass 00057 else: 00058 try: 00059 self.out.write(obj) 00060 except IOError: 00061 pass 00062 00063 def __getattr__(self, attr): 00064 """Delegate everything but write to the stream""" 00065 return getattr(self.out, attr) 00066 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8') 00067 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8') 00068 00069 00070 try: 00071 from StringIO import StringIO 00072 from lxml import etree 00073 except Exception, e: 00074 sys.stderr.write(u'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e) 00075 sys.exit(1) 00076 00077 # Check that the lxml library is current enough 00078 # From the lxml documents it states: (http://codespeak.net/lxml/installation.html) 00079 # "If you want to use XPath, do not use libxml2 2.6.27. We recommend libxml2 2.7.2 or later" 00080 # Testing was performed with the Ubuntu 9.10 "python-lxml" version "2.1.5-1ubuntu2" repository package 00081 version = '' 00082 for digit in etree.LIBXML_VERSION: 00083 version+=str(digit)+'.' 00084 version = version[:-1] 00085 if version < '2.7.2': 00086 sys.stderr.write(u''' 00087 ! Error - The installed version of the "lxml" python library "libxml" version is too old. 00088 At least "libxml" version 2.7.2 must be installed. Your version is (%s). 00089 ''' % version) 00090 sys.exit(1) 00091 00092 00093 class Videos(object): 00094 """Main interface to http://www.bbciplayer.com/ 00095 This is done to support a common naming framework for all python Netvision plugins no matter their site 00096 target. 00097 00098 Supports search methods 00099 The apikey is a not required to access http://www.bbciplayer.com/ 00100 """ 00101 def __init__(self, 00102 apikey, 00103 mythtv = True, 00104 interactive = False, 00105 select_first = False, 00106 debug = False, 00107 custom_ui = None, 00108 language = None, 00109 search_all_languages = False, 00110 ): 00111 """apikey (str/unicode): 00112 Specify the target site API key. Applications need their own key in some cases 00113 00114 mythtv (True/False): 00115 When True, the returned meta data is being returned has the key and values massaged to match MythTV 00116 When False, the returned meta data is being returned matches what target site returned 00117 00118 interactive (True/False): (This option is not supported by all target site apis) 00119 When True, uses built-in console UI is used to select the correct show. 00120 When False, the first search result is used. 00121 00122 select_first (True/False): (This option is not supported currently implemented in any grabbers) 00123 Automatically selects the first series search result (rather 00124 than showing the user a list of more than one series). 00125 Is overridden by interactive = False, or specifying a custom_ui 00126 00127 debug (True/False): 00128 shows verbose debugging information 00129 00130 custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers) 00131 A callable subclass of interactive class (overrides interactive option) 00132 00133 language (2 character language abbreviation): (This option is not supported by all target site apis) 00134 The language of the returned data. Is also the language search 00135 uses. Default is "en" (English). For full list, run.. 00136 00137 search_all_languages (True/False): (This option is not supported by all target site apis) 00138 By default, a Netvision grabber will only search in the language specified using 00139 the language option. When this is True, it will search for the 00140 show in any language 00141 00142 """ 00143 self.config = {} 00144 self.mythxml = MythXML() 00145 00146 if apikey is not None: 00147 self.config['apikey'] = apikey 00148 else: 00149 pass # BBC does not require an apikey 00150 00151 self.config['debug_enabled'] = debug # show debugging messages 00152 self.common = common 00153 self.common.debug = debug # Set the common function debug level 00154 00155 self.log_name = u'BBCiPlayer_Grabber' 00156 self.common.logger = self.common.initLogger(path=sys.stderr, log_name=self.log_name) 00157 self.logger = self.common.logger # Setups the logger (self.log.debug() etc) 00158 00159 self.config['custom_ui'] = custom_ui 00160 00161 self.config['interactive'] = interactive 00162 00163 self.config['select_first'] = select_first 00164 00165 self.config['search_all_languages'] = search_all_languages 00166 00167 self.error_messages = {'BBCUrlError': u"! Error: The URL (%s) cause the exception error (%s)\n", 'BBCHttpError': u"! Error: An HTTP communications error with the BBC was raised (%s)\n", 'BBCRssError': u"! Error: Invalid RSS meta data\nwas received from the BBC error (%s). Skipping item.\n", 'BBCVideoNotFound': u"! Error: Video search with the BBC did not return any results (%s)\n", 'BBCConfigFileError': u"! Error: bbc_config.xml file missing\nit should be located in and named as (%s).\n", 'BBCUrlDownloadError': u"! Error: Downloading a RSS feed or Web page (%s).\n", } 00168 00169 # Channel details and search results 00170 self.channel = {'channel_title': u'BBC iPlayer', 'channel_link': u'http://www.bbc.co.uk', 'channel_description': u"BBC iPlayer is our service that lets you catch up with radio and television programmes from the past week.", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0} 00171 00172 # XPath parsers used to detect a video type of item 00173 self.countryCodeParsers = [ 00174 etree.XPath('.//a[@class="episode-title title-link cta-video"]', namespaces=self.common.namespaces), 00175 etree.XPath('.//div[@class="feature video"]', namespaces=self.common.namespaces), 00176 etree.XPath('.//atm:category[@term="TV"]', namespaces=self.common.namespaces), 00177 ] 00178 00179 # Season and Episode detection regex patterns 00180 self.s_e_Patterns = [ 00181 # "Series 7 - Episode 4" or "Series 7 - Episode 4" or "Series 7: On Holiday: Episode 10" 00182 re.compile(u'''^.+?Series\\ (?P<seasno>[0-9]+).*.+?Episode\\ (?P<epno>[0-9]+).*$''', re.UNICODE), 00183 # Series 5 - 1 00184 re.compile(u'''^.+?Series\\ (?P<seasno>[0-9]+)\\ \\-\\ (?P<epno>[0-9]+).*$''', re.UNICODE), 00185 # Series 1 - Warriors of Kudlak - Part 2 00186 re.compile(u'''^.+?Series\\ (?P<seasno>[0-9]+).*.+?Part\\ (?P<epno>[0-9]+).*$''', re.UNICODE), 00187 # Series 3: Programme 3 00188 re.compile(u'''^.+?Series\\ (?P<seasno>[0-9]+)\\:\\ Programme\\ (?P<epno>[0-9]+).*$''', re.UNICODE), 00189 # Series 3: 00190 re.compile(u'''^.+?Series\\ (?P<seasno>[0-9]+).*$''', re.UNICODE), 00191 # Episode 1 00192 re.compile(u'''^.+?Episode\\ (?P<seasno>[0-9]+).*$''', re.UNICODE), 00193 ] 00194 00195 self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/bbciplayer.jpg' 00196 00197 self.config[u'image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions 00198 # end __init__() 00199 00200 ########################################################################################################### 00201 # 00202 # Start - Utility functions 00203 # 00204 ########################################################################################################### 00205 00206 def getBBCConfig(self): 00207 ''' Read the MNV BBC iPlayer grabber "bbc_config.xml" configuration file 00208 return nothing 00209 ''' 00210 # Read the grabber bbciplayer_config.xml configuration file 00211 url = u'file://%s/nv_python_libs/configs/XML/bbc_config.xml' % (baseProcessingDir, ) 00212 if not os.path.isfile(url[7:]): 00213 raise BBCConfigFileError(self.error_messages['BBCConfigFileError'] % (url[7:], )) 00214 00215 if self.config['debug_enabled']: 00216 print url 00217 print 00218 try: 00219 self.bbciplayer_config = etree.parse(url) 00220 except Exception, e: 00221 raise BBCUrlError(self.error_messages['BBCUrlError'] % (url, errormsg)) 00222 return 00223 # end getBBCConfig() 00224 00225 00226 def getUserPreferences(self): 00227 '''Read the bbciplayer_config.xml and user preference bbciplayer.xml file. 00228 If the bbciplayer.xml file does not exist then copy the default. 00229 return nothing 00230 ''' 00231 # Get bbciplayer_config.xml 00232 self.getBBCConfig() 00233 00234 # Check if the bbciplayer.xml file exists 00235 userPreferenceFile = self.bbciplayer_config.find('userPreferenceFile').text 00236 if userPreferenceFile[0] == '~': 00237 self.bbciplayer_config.find('userPreferenceFile').text = u"%s%s" % (os.path.expanduser(u"~"), userPreferenceFile[1:]) 00238 00239 # If the user config file does not exists then copy one from the default 00240 if not os.path.isfile(self.bbciplayer_config.find('userPreferenceFile').text): 00241 # Make the necessary directories if they do not already exist 00242 prefDir = self.bbciplayer_config.find('userPreferenceFile').text.replace(u'/bbciplayer.xml', u'') 00243 if not os.path.isdir(prefDir): 00244 os.makedirs(prefDir) 00245 defaultConfig = u'%s/nv_python_libs/configs/XML/defaultUserPrefs/bbciplayer.xml' % (baseProcessingDir, ) 00246 shutil.copy2(defaultConfig, self.bbciplayer_config.find('userPreferenceFile').text) 00247 00248 # Read the grabber bbciplayer_config.xml configuration file 00249 url = u'file://%s' % (self.bbciplayer_config.find('userPreferenceFile').text, ) 00250 if self.config['debug_enabled']: 00251 print url 00252 print 00253 try: 00254 self.userPrefs = etree.parse(url) 00255 except Exception, e: 00256 raise BBCUrlError(self.error_messages['BBCUrlError'] % (url, errormsg)) 00257 return 00258 # end getUserPreferences() 00259 00260 def setCountry(self, item): 00261 '''Parse the item information (HTML or RSS/XML) to identify if the content is a video or 00262 audio file. Set the contry code if a video is detected as it can only be played in the "UK" 00263 return "uk" if a video type was detected. 00264 return None if a video type was NOT detected. 00265 ''' 00266 countryCode = None 00267 for xpathP in self.countryCodeParsers: 00268 if len(xpathP(item)): 00269 countryCode = u'uk' 00270 break 00271 return countryCode 00272 # end setCountry() 00273 00274 00275 def getSeasonEpisode(self, title): 00276 ''' Check is there is any season or episode number information in an item's title 00277 return array of season and/or episode numbers 00278 return array with None values 00279 ''' 00280 s_e = [None, None] 00281 for index in range(len(self.s_e_Patterns)): 00282 match = self.s_e_Patterns[index].match(title) 00283 if not match: 00284 continue 00285 if index < 4: 00286 s_e[0], s_e[1] = match.groups() 00287 break 00288 elif index == 4: 00289 s_e[0] = match.groups()[0] 00290 break 00291 elif index == 5: 00292 s_e[1] = match.groups()[0] 00293 break 00294 return s_e 00295 # end getSeasonEpisode() 00296 00297 ########################################################################################################### 00298 # 00299 # End of Utility functions 00300 # 00301 ########################################################################################################### 00302 00303 def processVideoUrl(self, url): 00304 playerUrl = self.mythxml.getInternetContentUrl("nv_python_libs/configs/HTML/bbciplayer.html", \ 00305 url) 00306 return playerUrl 00307 00308 def searchTitle(self, title, pagenumber, pagelen): 00309 '''Key word video search of the BBC iPlayer web site 00310 return an array of matching item elements 00311 return 00312 ''' 00313 # Save the origninal URL 00314 orgUrl = self.bbciplayer_config.find('searchURLS').xpath(".//href")[0].text 00315 00316 try: 00317 searchVar = u'/?q=%s&page=%s' % (urllib.quote(title.encode("utf-8")), pagenumber) 00318 except UnicodeDecodeError: 00319 searchVar = u'/?q=%s&page=%s' % (urllib.quote(title), pagenumber) 00320 00321 url = self.bbciplayer_config.find('searchURLS').xpath(".//href")[0].text+searchVar 00322 00323 if self.config['debug_enabled']: 00324 print url 00325 print 00326 00327 self.bbciplayer_config.find('searchURLS').xpath(".//href")[0].text = url 00328 00329 # Perform a search 00330 try: 00331 resultTree = self.common.getUrlData(self.bbciplayer_config.find('searchURLS'), pageFilter=self.bbciplayer_config.find('searchURLS').xpath(".//pageFilter")[0].text) 00332 except Exception, errormsg: 00333 # Restore the origninal URL 00334 self.bbciplayer_config.find('searchURLS').xpath(".//href")[0].text = orgUrl 00335 raise BBCUrlDownloadError(self.error_messages['BBCUrlDownloadError'] % (errormsg)) 00336 00337 # Restore the origninal URL 00338 self.bbciplayer_config.find('searchURLS').xpath(".//href")[0].text = orgUrl 00339 00340 if resultTree is None: 00341 raise BBCVideoNotFound(u"No BBC Video matches found for search value (%s)" % title) 00342 00343 searchResults = resultTree.xpath('//result//li') 00344 if not len(searchResults): 00345 raise BBCVideoNotFound(u"No BBC Video matches found for search value (%s)" % title) 00346 00347 # BBC iPlayer search results fo not have a pubDate so use the current data time 00348 # e.g. "Sun, 06 Jan 2008 21:44:36 GMT" 00349 pubDate = datetime.datetime.now().strftime(self.common.pubDateFormat) 00350 00351 # Set the display type for the link (Fullscreen, Web page, Game Console) 00352 if self.userPrefs.find('displayURL') != None: 00353 urlType = self.userPrefs.find('displayURL').text 00354 else: 00355 urlType = u'fullscreen' 00356 00357 # Translate the search results into MNV RSS item format 00358 audioFilter = etree.XPath('contains(./@class,"audio") or contains(./../../@class,"audio")') 00359 linkFilter = etree.XPath(u".//div[@class='episode-info ']//a") 00360 titleFilter = etree.XPath(u".//div[@class='episode-info ']//a") 00361 descFilter = etree.XPath(u".//div[@class='episode-info ']//p[@class='episode-synopsis']") 00362 thumbnailFilter = etree.XPath(u".//span[@class='episode-image cta-play']//img") 00363 itemDict = {} 00364 for result in searchResults: 00365 tmpLink = linkFilter(result) 00366 if not len(tmpLink): # Make sure that this result actually has a video 00367 continue 00368 bbciplayerItem = etree.XML(self.common.mnvItem) 00369 # Is this an Audio or Video item (true/false) 00370 audioTF = audioFilter(result) 00371 # Extract and massage data 00372 link = tmpLink[0].attrib['href'] 00373 if urlType == 'bigscreen': 00374 link = u'http://www.bbc.co.uk/iplayer/bigscreen%s' % link.replace(u'/iplayer',u'') 00375 elif urlType == 'bbcweb': 00376 link = u'http://www.bbc.co.uk'+ link 00377 else: 00378 if not audioTF: 00379 link = link.replace(u'/iplayer/episode/', u'') 00380 index = link.find(u'/') 00381 link = link[:index] 00382 link = self.processVideoUrl(link); 00383 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}customhtml").text = 'true' 00384 else: # Audio media will not play in the embedded HTML page 00385 link = u'http://www.bbc.co.uk'+ link 00386 link = self.common.ampReplace(link) 00387 00388 title = self.common.massageText(titleFilter(result)[0].attrib['title'].strip()) 00389 description = self.common.massageText(etree.tostring(descFilter(result)[0], method="text", encoding=unicode).strip()) 00390 00391 # Insert data into a new item element 00392 bbciplayerItem.find('title').text = title 00393 bbciplayerItem.find('author').text = u'BBC' 00394 bbciplayerItem.find('pubDate').text = pubDate 00395 bbciplayerItem.find('description').text = description 00396 bbciplayerItem.find('link').text = link 00397 bbciplayerItem.xpath('.//media:thumbnail', namespaces=self.common.namespaces)[0].attrib['url'] = self.common.ampReplace(thumbnailFilter(result)[0].attrib['src']) 00398 bbciplayerItem.xpath('.//media:content', namespaces=self.common.namespaces)[0].attrib['url'] = link 00399 # Videos are only viewable in the UK so add a country indicator if this is a video 00400 if audioTF: 00401 countCode = None 00402 else: 00403 countCode = u'uk' 00404 if countCode: 00405 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}country").text = countCode 00406 s_e = self.getSeasonEpisode(title) 00407 if s_e[0]: 00408 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}season").text = s_e[0] 00409 if s_e[1]: 00410 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}episode").text = s_e[1] 00411 itemDict[title.lower()] = bbciplayerItem 00412 00413 if not len(itemDict.keys()): 00414 raise BBCVideoNotFound(u"No BBC Video matches found for search value (%s)" % title) 00415 00416 # Set the number of search results returned 00417 self.channel['channel_numresults'] = len(itemDict) 00418 00419 return [itemDict, resultTree.xpath('//pageInfo')[0].text] 00420 # end searchTitle() 00421 00422 00423 def searchForVideos(self, title, pagenumber): 00424 """Common name for a video search. Used to interface with MythTV plugin NetVision 00425 """ 00426 # Get the user preferences 00427 try: 00428 self.getUserPreferences() 00429 except Exception, e: 00430 sys.stderr.write(u'%s' % e) 00431 sys.exit(1) 00432 00433 if self.config['debug_enabled']: 00434 print "self.userPrefs:" 00435 sys.stdout.write(etree.tostring(self.userPrefs, encoding='UTF-8', pretty_print=True)) 00436 print 00437 00438 00439 # Easier for debugging 00440 # print self.searchTitle(title, pagenumber, self.page_limit) 00441 # print 00442 # sys.exit() 00443 00444 try: 00445 data = self.searchTitle(title, pagenumber, self.page_limit) 00446 except BBCVideoNotFound, msg: 00447 sys.stderr.write(u"%s\n" % msg) 00448 sys.exit(0) 00449 except BBCUrlError, msg: 00450 sys.stderr.write(u'%s\n' % msg) 00451 sys.exit(1) 00452 except BBCHttpError, msg: 00453 sys.stderr.write(self.error_messages['BBCHttpError'] % msg) 00454 sys.exit(1) 00455 except BBCRssError, msg: 00456 sys.stderr.write(self.error_messages['BBCRssError'] % msg) 00457 sys.exit(1) 00458 except Exception, e: 00459 sys.stderr.write(u"! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e)) 00460 sys.exit(1) 00461 00462 # Create RSS element tree 00463 rssTree = etree.XML(self.common.mnvRSS+u'</rss>') 00464 00465 # Set the paging values 00466 itemCount = len(data[0].keys()) 00467 if data[1] == 'true': 00468 self.channel['channel_returned'] = itemCount 00469 self.channel['channel_startindex'] = itemCount 00470 self.channel['channel_numresults'] = itemCount+(self.page_limit*(int(pagenumber)-1)+1) 00471 else: 00472 self.channel['channel_returned'] = itemCount+(self.page_limit*(int(pagenumber)-1)) 00473 self.channel['channel_startindex'] = self.channel['channel_returned'] 00474 self.channel['channel_numresults'] = self.channel['channel_returned'] 00475 00476 # Add the Channel element tree 00477 channelTree = self.common.mnvChannelElement(self.channel) 00478 rssTree.append(channelTree) 00479 00480 lastKey = None 00481 for key in sorted(data[0].keys()): 00482 if lastKey != key: 00483 channelTree.append(data[0][key]) 00484 lastKey = key 00485 00486 # Output the MNV search results 00487 sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n') 00488 sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True)) 00489 sys.exit(0) 00490 # end searchForVideos() 00491 00492 def displayTreeView(self): 00493 '''Gather the BBC iPlayer feeds then get a max page of videos meta data in each of them 00494 Display the results and exit 00495 ''' 00496 # Get the user preferences 00497 try: 00498 self.getUserPreferences() 00499 except Exception, e: 00500 sys.stderr.write(u'%s' % e) 00501 sys.exit(1) 00502 00503 if self.config['debug_enabled']: 00504 print "self.userPrefs:" 00505 sys.stdout.write(etree.tostring(self.userPrefs, encoding='UTF-8', pretty_print=True)) 00506 print 00507 00508 # Massage channel icon 00509 self.channel_icon = self.common.ampReplace(self.channel_icon) 00510 00511 # Create RSS element tree 00512 rssTree = etree.XML(self.common.mnvRSS+u'</rss>') 00513 00514 # Add the Channel element tree 00515 channelTree = self.common.mnvChannelElement(self.channel) 00516 rssTree.append(channelTree) 00517 00518 # Process any user specified searches 00519 searchResultTree = [] 00520 searchFilter = etree.XPath(u"//item") 00521 userSearchStrings = u'userSearchStrings' 00522 if self.userPrefs.find(userSearchStrings) != None: 00523 userSearch = self.userPrefs.find(userSearchStrings).xpath('./userSearch') 00524 if len(userSearch): 00525 for searchDetails in userSearch: 00526 try: 00527 data = self.searchTitle(searchDetails.find('searchTerm').text, 1, self.page_limit) 00528 except BBCVideoNotFound, msg: 00529 sys.stderr.write(u"%s\n" % msg) 00530 continue 00531 except BBCUrlError, msg: 00532 sys.stderr.write(u'%s\n' % msg) 00533 continue 00534 except BBCHttpError, msg: 00535 sys.stderr.write(self.error_messages['BBCHttpError'] % msg) 00536 continue 00537 except BBCRssError, msg: 00538 sys.stderr.write(self.error_messages['BBCRssError'] % msg) 00539 continue 00540 except Exception, e: 00541 sys.stderr.write(u"! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e)) 00542 continue 00543 dirElement = etree.XML(u'<directory></directory>') 00544 dirElement.attrib['name'] = self.common.massageText(searchDetails.find('dirName').text) 00545 dirElement.attrib['thumbnail'] = self.channel_icon 00546 lastKey = None 00547 for key in sorted(data[0].keys()): 00548 if lastKey != key: 00549 dirElement.append(data[0][key]) 00550 lastKey = key 00551 channelTree.append(dirElement) 00552 continue 00553 00554 # Create a structure of feeds that can be concurrently downloaded 00555 rssData = etree.XML(u'<xml></xml>') 00556 for feedType in [u'treeviewURLS', u'userFeeds']: 00557 if self.userPrefs.find(feedType) == None: 00558 continue 00559 if not len(self.userPrefs.find(feedType).xpath('./url')): 00560 continue 00561 for rssFeed in self.userPrefs.find(feedType).xpath('./url'): 00562 urlEnabled = rssFeed.attrib.get('enabled') 00563 if urlEnabled == 'false': 00564 continue 00565 urlName = rssFeed.attrib.get('name') 00566 if urlName: 00567 uniqueName = u'%s;%s' % (urlName, rssFeed.text) 00568 else: 00569 uniqueName = u'RSS;%s' % (rssFeed.text) 00570 url = etree.XML(u'<url></url>') 00571 etree.SubElement(url, "name").text = uniqueName 00572 etree.SubElement(url, "href").text = rssFeed.text 00573 etree.SubElement(url, "filter").text = u"atm:title" 00574 etree.SubElement(url, "filter").text = u"//atm:entry" 00575 etree.SubElement(url, "parserType").text = u'xml' 00576 rssData.append(url) 00577 00578 if self.config['debug_enabled']: 00579 print "rssData:" 00580 sys.stdout.write(etree.tostring(rssData, encoding='UTF-8', pretty_print=True)) 00581 print 00582 00583 # Get the RSS Feed data 00584 if rssData.find('url') != None: 00585 try: 00586 resultTree = self.common.getUrlData(rssData) 00587 except Exception, errormsg: 00588 raise BBCUrlDownloadError(self.error_messages['BBCUrlDownloadError'] % (errormsg)) 00589 if self.config['debug_enabled']: 00590 print "resultTree:" 00591 sys.stdout.write(etree.tostring(resultTree, encoding='UTF-8', pretty_print=True)) 00592 print 00593 00594 # Set the display type for the link (Fullscreen, Web page, Game Console) 00595 if self.userPrefs.find('displayURL') != None: 00596 urlType = self.userPrefs.find('displayURL').text 00597 else: 00598 urlType = u'fullscreen' 00599 00600 # Process each directory of the user preferences that have an enabled rss feed 00601 feedFilter = etree.XPath('//url[text()=$url]') 00602 itemFilter = etree.XPath('.//atm:entry', namespaces=self.common.namespaces) 00603 titleFilter = etree.XPath('.//atm:title', namespaces=self.common.namespaces) 00604 mediaFilter = etree.XPath('.//atm:category[@term="TV"]', namespaces=self.common.namespaces) 00605 linkFilter = etree.XPath('.//atm:link', namespaces=self.common.namespaces) 00606 descFilter1 = etree.XPath('.//atm:content', namespaces=self.common.namespaces) 00607 descFilter2 = etree.XPath('.//p') 00608 itemThumbNail = etree.XPath('.//media:thumbnail', namespaces=self.common.namespaces) 00609 creationDate = etree.XPath('.//atm:updated', namespaces=self.common.namespaces) 00610 itemDwnLink = etree.XPath('.//media:content', namespaces=self.common.namespaces) 00611 itemLanguage = etree.XPath('.//media:content', namespaces=self.common.namespaces) 00612 rssName = etree.XPath('atm:title', namespaces=self.common.namespaces) 00613 categoryDir = None 00614 categoryElement = None 00615 itemAuthor = u'BBC' 00616 for result in resultTree.findall('results'): 00617 names = result.find('name').text.split(u';') 00618 names[0] = self.common.massageText(names[0]) 00619 if names[0] == 'RSS': 00620 names[0] = self.common.massageText(rssName(result.find('result'))[0].text.replace(u'BBC iPlayer - ', u'')) 00621 count = 0 00622 urlMax = None 00623 url = feedFilter(self.userPrefs, url=names[1]) 00624 if len(url): 00625 if url[0].attrib.get('max'): 00626 try: 00627 urlMax = int(url[0].attrib.get('max')) 00628 except: 00629 pass 00630 elif url[0].getparent().attrib.get('globalmax'): 00631 try: 00632 urlMax = int(url[0].getparent().attrib.get('globalmax')) 00633 except: 00634 pass 00635 if urlMax == 0: 00636 urlMax = None 00637 channelThumbnail = self.channel_icon 00638 channelLanguage = u'en' 00639 # Create a new directory and/or subdirectory if required 00640 if names[0] != categoryDir: 00641 if categoryDir != None: 00642 channelTree.append(categoryElement) 00643 categoryElement = etree.XML(u'<directory></directory>') 00644 categoryElement.attrib['name'] = names[0] 00645 categoryElement.attrib['thumbnail'] = self.channel_icon 00646 categoryDir = names[0] 00647 00648 if self.config['debug_enabled']: 00649 print "Results: #Items(%s) for (%s)" % (len(itemFilter(result)), names) 00650 print 00651 00652 # Create a list of item elements in pubDate order to that the items are processed in 00653 # date and time order 00654 itemDict = [(pd.text, pd.getparent()) for pd in creationDate(result)] 00655 itemList = sorted(itemDict, key=itemgetter(0), reverse=True) 00656 # Convert each RSS item into a MNV item 00657 for tupleDate in itemList: 00658 itemData = tupleDate[1] 00659 bbciplayerItem = etree.XML(self.common.mnvItem) 00660 tmpLink = linkFilter(itemData) 00661 if len(tmpLink): # Make sure that this result actually has a video 00662 link = tmpLink[0].attrib['href'] 00663 if urlType == 'bigscreen': 00664 link = link.replace(u'/iplayer/', u'/iplayer/bigscreen/') 00665 elif urlType == 'bbcweb': 00666 pass # Link does not need adjustments 00667 else: 00668 if len(mediaFilter(itemData)): 00669 link = link.replace(u'http://www.bbc.co.uk/iplayer/episode/', u'') 00670 index = link.find(u'/') 00671 link = link[:index] 00672 link = self.processVideoUrl(link); 00673 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}customhtml").text = 'true' 00674 else: 00675 pass # Radio media cannot be played within the embedded weg page 00676 else: 00677 continue 00678 # Convert the pubDate "2010-03-22T07:56:21Z" to a MNV pubdate format 00679 pubdate = creationDate(itemData) 00680 if len(pubdate): 00681 pubdate = pubdate[0].text 00682 pubdate = time.strptime(pubdate, '%Y-%m-%dT%H:%M:%SZ') 00683 pubdate = time.strftime(self.common.pubDateFormat, pubdate) 00684 else: 00685 pubdate = datetime.datetime.now().strftime(self.common.pubDateFormat) 00686 00687 # Extract and massage data also insert data into a new item element 00688 bbciplayerItem.find('title').text = self.common.massageText(titleFilter(itemData)[0].text.strip()) 00689 bbciplayerItem.find('author').text = itemAuthor 00690 bbciplayerItem.find('pubDate').text = pubdate 00691 description = etree.HTML(etree.tostring(descFilter1(itemData)[0], method="text", encoding=unicode).strip()) 00692 description = etree.tostring(descFilter2(description)[1], method="text", encoding=unicode).strip() 00693 bbciplayerItem.find('description').text = self.common.massageText(description) 00694 bbciplayerItem.find('link').text = link 00695 itemDwnLink(bbciplayerItem)[0].attrib['url'] = link 00696 try: 00697 itemThumbNail(bbciplayerItem)[0].attrib['url'] = self.common.ampReplace(itemThumbNail(itemData)[0].attrib['url']) 00698 except IndexError: 00699 pass 00700 itemLanguage(bbciplayerItem)[0].attrib['lang'] = channelLanguage 00701 # Videos are only viewable in the UK so add a country indicator if this is a video 00702 countCode = self.setCountry(itemData) 00703 if countCode: 00704 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}country").text = countCode 00705 s_e = self.getSeasonEpisode(bbciplayerItem.find('title').text) 00706 if s_e[0]: 00707 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}season").text = s_e[0] 00708 if s_e[1]: 00709 etree.SubElement(bbciplayerItem, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}episode").text = s_e[1] 00710 categoryElement.append(bbciplayerItem) 00711 if urlMax: # Check of the maximum items to processes has been met 00712 count+=1 00713 if count > urlMax: 00714 break 00715 00716 # Add the last directory processed 00717 if categoryElement != None: 00718 if categoryElement.xpath('.//item') != None: 00719 channelTree.append(categoryElement) 00720 00721 # Check that there was at least some items 00722 if len(rssTree.xpath('//item')): 00723 # Output the MNV search results 00724 sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n') 00725 sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True)) 00726 00727 sys.exit(0) 00728 # end displayTreeView() 00729 # end Videos() class
1.7.6.1