MythTV  0.26-pre
bbciplayer_api.py
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends