MythTV  0.26-pre
bliptv_api.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # -*- coding: UTF-8 -*-
00003 # ----------------------
00004 # Name: bliptv_api - Simple-to-use Python interface to the bliptv API (http://blip.tv/)
00005 # Python Script
00006 # Author:   R.D. Vaughan
00007 # Purpose:  This python script is intended to perform a variety of utility functions to search and access text
00008 #           meta data, video and image URLs from blip.tv.
00009 #           These routines are based on the v2.0 api. Specifications
00010 #           for this api are published at http://blip.tv/about/api/
00011 #
00012 # License:Creative Commons GNU GPL v2
00013 # (http://creativecommons.org/licenses/GPL/2.0/)
00014 #-------------------------------------
00015 __title__ ="bliptv_api - Simple-to-use Python interface to the bliptv API (http://blip.tv/about/api/)"
00016 __author__="R.D. Vaughan"
00017 __purpose__='''
00018 This python script is intended to perform a variety of utility functions to search and access text
00019 meta data, video and image URLs from blip.tv. These routines are based on the v2.0 api. Specifications
00020 for this api are published at  http://blip.tv/about/api/
00021 '''
00022 
00023 __version__="v0.2.5"
00024 # 0.1.0 Initial development
00025 # 0.1.1 Changed to use bliptv's rss data rather than JSON as JSON ad a number of error
00026 # 0.1.2 Changes Search to parse XML and added Tree view
00027 # 0.1.3 Added directory image logic
00028 # 0.1.4 Documentation updates
00029 # 0.2.0 Public release
00030 # 0.2.1 New python bindings conversion
00031 #       Better exception error reporting
00032 #       Better handling of invalid unicode data from source
00033 # 0.2.2 Completed exception message improvements
00034 #       Removed the unused import of the feedparser library
00035 # 0.2.3 Fixed an exception message output code error in two places
00036 # 0.2.4 Removed the need for python MythTV bindings and added "%SHAREDIR%" to icon directory path
00037 # 0.2.5 Changed link URL to full screen when a "blip:embedUrl" exists for an item
00038 #       Removed a subdirectory level as the "Featured" RSS feed has been discontinued
00039 
00040 import os, struct, sys, re, time
00041 import urllib, urllib2
00042 import logging
00043 from MythTV import MythXML
00044 
00045 try:
00046     import xml.etree.cElementTree as ElementTree
00047 except ImportError:
00048     import xml.etree.ElementTree as ElementTree
00049 
00050 from bliptv_exceptions import (BliptvUrlError, BliptvHttpError, BliptvRssError, BliptvVideoNotFound, BliptvXmlError)
00051 
00052 class OutStreamEncoder(object):
00053     """Wraps a stream with an encoder"""
00054     def __init__(self, outstream, encoding=None):
00055         self.out = outstream
00056         if not encoding:
00057             self.encoding = sys.getfilesystemencoding()
00058         else:
00059             self.encoding = encoding
00060 
00061     def write(self, obj):
00062         """Wraps the output stream, encoding Unicode strings with the specified encoding"""
00063         if isinstance(obj, unicode):
00064             try:
00065                 self.out.write(obj.encode(self.encoding))
00066             except IOError:
00067                 pass
00068         else:
00069             try:
00070                 self.out.write(obj)
00071             except IOError:
00072                 pass
00073 
00074     def __getattr__(self, attr):
00075         """Delegate everything but write to the stream"""
00076         return getattr(self.out, attr)
00077 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
00078 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
00079 
00080 
00081 class XmlHandler:
00082     """Deals with retrieval of XML files from API
00083     """
00084     def __init__(self, url):
00085         self.url = url
00086 
00087     def _grabUrl(self, url):
00088         try:
00089             urlhandle = urllib.urlopen(url)
00090         except IOError, errormsg:
00091             raise BliptvHttpError(errormsg)
00092         return urlhandle.read()
00093 
00094     def getEt(self):
00095         xml = self._grabUrl(self.url)
00096         try:
00097             et = ElementTree.fromstring(xml)
00098         except SyntaxError, errormsg:
00099             raise BliptvXmlError(errormsg)
00100         return et
00101 
00102 
00103 class Videos(object):
00104     """Main interface to http://blip.tv/
00105     This is done to support a common naming framework for all python Netvision plugins no matter their site
00106     target.
00107 
00108     Supports search and tree view methods
00109     The apikey is a not required to access http://blip.tv/
00110     """
00111     def __init__(self,
00112                 apikey,
00113                 mythtv = True,
00114                 interactive = False,
00115                 select_first = False,
00116                 debug = False,
00117                 custom_ui = None,
00118                 language = None,
00119                 search_all_languages = False,
00120                 ):
00121         """apikey (str/unicode):
00122             Specify the target site API key. Applications need their own key in some cases
00123 
00124         mythtv (True/False):
00125             When True, the returned meta data is being returned has the key and values massaged to match MythTV
00126             When False, the returned meta data  is being returned matches what target site returned
00127 
00128         interactive (True/False): (This option is not supported by all target site apis)
00129             When True, uses built-in console UI is used to select the correct show.
00130             When False, the first search result is used.
00131 
00132         select_first (True/False): (This option is not supported currently implemented in any grabbers)
00133             Automatically selects the first series search result (rather
00134             than showing the user a list of more than one series).
00135             Is overridden by interactive = False, or specifying a custom_ui
00136 
00137         debug (True/False):
00138              shows verbose debugging information
00139 
00140         custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
00141             A callable subclass of interactive class (overrides interactive option)
00142 
00143         language (2 character language abbreviation): (This option is not supported by all target site apis)
00144             The language of the returned data. Is also the language search
00145             uses. Default is "en" (English). For full list, run..
00146 
00147         search_all_languages (True/False): (This option is not supported by all target site apis)
00148             By default, a Netvision grabber will only search in the language specified using
00149             the language option. When this is True, it will search for the
00150             show in any language
00151 
00152         """
00153         self.config = {}
00154         self.mythxml = MythXML()
00155 
00156         if apikey is not None:
00157             self.config['apikey'] = apikey
00158         else:
00159             pass    # blip.tv does not require an apikey
00160 
00161         self.config['debug_enabled'] = debug # show debugging messages
00162 
00163         self.log_name = "Bliptv"
00164         self.log = self._initLogger() # Setups the logger (self.log.debug() etc)
00165 
00166         self.config['custom_ui'] = custom_ui
00167 
00168         self.config['interactive'] = interactive # prompt for correct series?
00169 
00170         self.config['select_first'] = select_first
00171 
00172         self.config['search_all_languages'] = search_all_languages
00173 
00174         # Defaulting to ENGISH but the blip.tv apis do not support specifying a language
00175         self.config['language'] = "en"
00176 
00177         self.error_messages = {'BliptvUrlError': u"! Error: The URL (%s) cause the exception error (%s)\n", 'BliptvHttpError': u"! Error: An HTTP communicating error with blip.tv was raised (%s)\n", 'BliptvRssError': u"! Error: Invalid RSS meta data\nwas received from blip.tv error (%s). Skipping item.\n", 'BliptvVideoNotFound': u"! Error: Video search with blip.tv did not return any results (%s)\n", }
00178 
00179         # This is an example that must be customized for each target site
00180         self.key_translation = [{'channel_title': 'channel_title', 'channel_link': 'channel_link', 'channel_description': 'channel_description', 'channel_numresults': 'channel_numresults', 'channel_returned': 'channel_returned', 'channel_startindex': 'channel_startindex'}, {'title': 'item_title', 'blip_safeusername': 'item_author', 'updated': 'item_pubdate', 'blip_puredescription': 'item_description', 'link': 'item_link', 'blip_picture': 'item_thumbnail', 'video': 'item_url', 'blip_runtime': 'item_duration', 'blip_rating': 'item_rating', 'width': 'item_width', 'height': 'item_height', 'language': 'item_lang'}]
00181 
00182         # The following url_ configs are based of the
00183         # http://blip.tv/about/api/
00184         self.config['base_url'] = "http://www.blip.tv%s"
00185         self.config['thumb_url'] = u"http://a.images.blip.tv%s"
00186 
00187         self.config[u'urls'] = {}
00188 
00189         # v2 api calls - An example that must be customized for each target site
00190         self.config[u'urls'][u'video.search'] = "http://www.blip.tv/?search=%s;&page=%s;&pagelen=%s;&language_code=%s;&skin=rss"
00191         self.config[u'urls'][u'categories'] = "http://www.blip.tv/?section=categories&cmd=view&skin=api"
00192 
00193         self.config[u'image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions
00194 
00195         # Functions that parse video data from RSS data
00196         self.config['item_parser'] = {}
00197         self.config['item_parser']['main'] = self.getVideosForURL
00198 
00199         # Tree view url and the function that parses that urls meta data
00200         self.config[u'urls'][u'tree.view'] = {
00201             'P_R_R_F': {
00202                 '__all__': ['http://www.blip.tv/%s/?skin=rss', 'main'],
00203                 },
00204             'categories': {
00205                 '__all__': ['http://www.blip.tv/rss/', 'main'],
00206                 },
00207             }
00208 
00209         # Tree view categories are disabled until their results can be made more meaningful
00210         #self.tree_order = ['P_R_R_F', 'categories', ]
00211         self.tree_order = ['P_R_R_F']
00212 
00213         self.tree_org = {
00214 #            'P_R_R_F': [['Popular/Recent/Features/Random ...', ['popular', 'recent', 'random', 'featured',]],
00215             'P_R_R_F': [['', ['popular', 'recent', 'random', 'featured',]],
00216                 ],
00217             # categories are dynamically filled in from a list retrieved from the blip.tv site
00218             'categories': [
00219                 ['Categories', u''],
00220                 ],
00221             }
00222 
00223         self.tree_customize = {
00224             'P_R_R_F': {
00225                 '__default__': { },
00226                 #'cat name': {},
00227             },
00228             'categories': {
00229                 '__default__': {'categories_id': u'', 'sort': u'', },
00230                 #'cat name': {},
00231             },
00232             }
00233 
00234         self.feed_names = {
00235             'P_R_R_F': {'popular': 'Most Comments', 'recent': 'Most Recent', 'random': 'Random selection',
00236             },
00237             'categories': {'featured': 'Featured Videos', 'popular': 'Most Comments', 'recent': 'Most Recent', 'random': 'Random selection',
00238             },
00239             }
00240 
00241         self.feed_icons = {
00242             'P_R_R_F': {'popular': 'directories/topics/most_comments', 'recent': 'directories/topics/most_recent', 'random': 'directories/topics/random',
00243             },
00244             'categories': {'featured': 'directories/topics/featured', 'popular': 'directories/topics/most_comments', 'recent': 'directories/topics/most_recent', 'random': 'directories/topics/random',
00245             },
00246             }
00247 
00248         # Initialize the tree view flag so that the item parsing code can be used for multiple purposes
00249         self.categories = False
00250         self.treeview = False
00251         self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/bliptv.png'
00252     # end __init__()
00253 
00254 
00255 ###########################################################################################################
00256 #
00257 # Start - Utility functions
00258 #
00259 ###########################################################################################################
00260 
00261     def detectUserLocationByIP(self):
00262         '''Get longitude and latitiude to find videos relative to your location. Up to three different
00263         servers will be tried before giving up.
00264         return a dictionary e.g.
00265         {'Latitude': '43.6667', 'Country': 'Canada', 'Longitude': '-79.4167', 'City': 'Toronto'}
00266         return an empty dictionary if there were any errors
00267         Code found at: http://blog.suinova.com/2009/04/from-ip-to-geolocation-country-city.html
00268         '''
00269         def getExternalIP():
00270             '''Find the external IP address of this computer.
00271             '''
00272             url = urllib.URLopener()
00273             try:
00274                 resp = url.open('http://www.whatismyip.com/automation/n09230945.asp')
00275                 return resp.read()
00276             except:
00277                 return None
00278             # end getExternalIP()
00279 
00280         ip = getExternalIP()
00281 
00282         if ip == None:
00283             return {}
00284 
00285         try:
00286             gs = urllib.urlopen('http://blogama.org/ip_query.php?ip=%s&output=xml' % ip)
00287             txt = gs.read()
00288         except:
00289             try:
00290                 gs = urllib.urlopen('http://www.seomoz.org/ip2location/look.php?ip=%s' % ip)
00291                 txt = gs.read()
00292             except:
00293                 try:
00294                     gs = urllib.urlopen('http://api.hostip.info/?ip=%s' % ip)
00295                     txt = gs.read()
00296                 except:
00297                     logging.error('GeoIP servers not available')
00298                     return {}
00299         try:
00300             if txt.find('<Response>') > 0:
00301                 countrys = re.findall(r'<CountryName>([\w ]+)<',txt)[0]
00302                 citys = re.findall(r'<City>([\w ]+)<',txt)[0]
00303                 lats,lons = re.findall(r'<Latitude>([\d\-\.]+)</Latitude>\s*<Longitude>([\d\-\.]+)<',txt)[0]
00304             elif txt.find('GLatLng') > 0:
00305                 citys,countrys = re.findall('<br />\s*([^<]+)<br />\s*([^<]+)<',txt)[0]
00306                 lats,lons = re.findall('LatLng\(([-\d\.]+),([-\d\.]+)',txt)[0]
00307             elif txt.find('<gml:coordinates>') > 0:
00308                 citys = re.findall('<Hostip>\s*<gml:name>(\w+)</gml:name>',txt)[0]
00309                 countrys = re.findall('<countryName>([\w ,\.]+)</countryName>',txt)[0]
00310                 lats,lons = re.findall('gml:coordinates>([-\d\.]+),([-\d\.]+)<',txt)[0]
00311             else:
00312                 logging.error('error parsing IP result %s'%txt)
00313                 return {}
00314             return {'Country':countrys,'City':citys,'Latitude':lats,'Longitude':lons}
00315         except:
00316             logging.error('Error parsing IP result %s'%txt)
00317             return {}
00318     # end detectUserLocationByIP()
00319 
00320     def massageDescription(self, text):
00321         '''Removes HTML markup from a text string.
00322         @param text The HTML source.
00323         @return The plain text.  If the HTML source contains non-ASCII
00324         entities or character references, this is a Unicode string.
00325         '''
00326         def fixup(m):
00327             text = m.group(0)
00328             if text[:1] == "<":
00329                 return "" # ignore tags
00330             if text[:2] == "&#":
00331                 try:
00332                     if text[:3] == "&#x":
00333                         return unichr(int(text[3:-1], 16))
00334                     else:
00335                         return unichr(int(text[2:-1]))
00336                 except ValueError:
00337                     pass
00338             elif text[:1] == "&":
00339                 import htmlentitydefs
00340                 entity = htmlentitydefs.entitydefs.get(text[1:-1])
00341                 if entity:
00342                     if entity[:2] == "&#":
00343                         try:
00344                             return unichr(int(entity[2:-1]))
00345                         except ValueError:
00346                             pass
00347                     else:
00348                         return unicode(entity, "iso-8859-1")
00349             return text # leave as is
00350         return self.ampReplace(re.sub(u"(?s)<[^>]*>|&#?\w+;", fixup, self.textUtf8(text))).replace(u'\n',u' ')
00351     # end massageDescription()
00352 
00353 
00354     def _initLogger(self):
00355         """Setups a logger using the logging module, returns a log object
00356         """
00357         logger = logging.getLogger(self.log_name)
00358         formatter = logging.Formatter('%(asctime)s) %(levelname)s %(message)s')
00359 
00360         hdlr = logging.StreamHandler(sys.stdout)
00361 
00362         hdlr.setFormatter(formatter)
00363         logger.addHandler(hdlr)
00364 
00365         if self.config['debug_enabled']:
00366             logger.setLevel(logging.DEBUG)
00367         else:
00368             logger.setLevel(logging.WARNING)
00369         return logger
00370     #end initLogger
00371 
00372 
00373     def textUtf8(self, text):
00374         if text == None:
00375             return text
00376         try:
00377             return unicode(text, 'utf8')
00378         except UnicodeDecodeError:
00379             return u''
00380         except (UnicodeEncodeError, TypeError):
00381             return text
00382     # end textUtf8()
00383 
00384 
00385     def ampReplace(self, text):
00386         '''Replace all "&" characters with "&amp;"
00387         '''
00388         text = self.textUtf8(text)
00389         return text.replace(u'&amp;',u'~~~~~').replace(u'&',u'&amp;').replace(u'~~~~~', u'&amp;')
00390     # end ampReplace()
00391 
00392     def setTreeViewIcon(self, dir_icon=None):
00393         '''Check if there is a specific generic tree view icon. If not default to the channel icon.
00394         return self.tree_dir_icon
00395         '''
00396         self.tree_dir_icon = self.channel_icon
00397         if not dir_icon:
00398             if not self.feed_icons.has_key(self.tree_key):
00399                 return self.tree_dir_icon
00400             if not self.feed_icons[self.tree_key].has_key(self.feed):
00401                 return self.tree_dir_icon
00402             dir_icon = self.feed_icons[self.tree_key][self.feed]
00403             if not dir_icon:
00404                 return self.tree_dir_icon
00405         self.tree_dir_icon = u'%%SHAREDIR%%/mythnetvision/icons/%s.png' % (dir_icon, )
00406         return self.tree_dir_icon
00407     # end setTreeViewIcon()
00408 
00409 ###########################################################################################################
00410 #
00411 # End of Utility functions
00412 #
00413 ###########################################################################################################
00414 
00415     def processVideoUrl(self, url):
00416         playerUrl = self.mythxml.getInternetContentUrl("nv_python_libs/configs/HTML/bliptv.html", \
00417                                                        url.replace(u'http://blip.tv/play/', ''))
00418         return self.ampReplace(playerUrl)
00419 
00420     def searchTitle(self, title, pagenumber, pagelen):
00421         '''Key word video search of the blip.tv web site
00422         return an array of matching item dictionaries
00423         return
00424         '''
00425         url = self.config[u'urls'][u'video.search'] % (urllib.quote_plus(title.encode("utf-8")), pagenumber, pagelen, self.config['language'])
00426 
00427         if self.config['debug_enabled']:
00428             print "Search URL:"
00429             print url
00430             print
00431 
00432         try:
00433             etree = XmlHandler(url).getEt()
00434         except Exception, errormsg:
00435             raise BliptvUrlError(self.error_messages['BliptvUrlError'] % (url, errormsg))
00436 
00437         if etree is None:
00438             raise BliptvVideoNotFound(u"1-No blip.tv Video matches found for search value (%s)" % title)
00439 
00440         # Massage each field and eliminate any item without a URL
00441         elements_final = []
00442         dictionary_first = False
00443         directory_image = u''
00444         self.next_page = False
00445         language = self.config['language']
00446         for elements in etree.find('channel'):
00447             if elements.tag == 'language':
00448                 if elements.text:
00449                     language = elements.text[:2]
00450                 continue
00451             if not elements.tag == 'item':
00452                 continue
00453             item = {}
00454             item['language'] = language
00455             embedURL = u''
00456             for elem in elements:
00457                 if elem.tag == 'title':
00458                     if elem.text:
00459                         item['title'] = self.massageDescription(elem.text.strip())
00460                     continue
00461                 if elem.tag.endswith('safeusername'):
00462                     if elem.text:
00463                         item['blip_safeusername'] = self.massageDescription(elem.text.strip())
00464                     continue
00465                 if elem.tag.endswith('pubDate'):
00466                     if elem.text:
00467                         item['updated'] = self.massageDescription(elem.text.strip())
00468                     continue
00469                 if elem.tag.endswith('puredescription'):
00470                     if elem.text:
00471                         item['blip_puredescription'] = self.massageDescription(elem.text.strip())
00472                     continue
00473                 if elem.tag.endswith('link'):
00474                     if elem.text:
00475                         item['link'] = self.ampReplace(elem.text.strip())
00476                     continue
00477                 if elem.tag.endswith('embedUrl'):
00478                     if elem.text:
00479                         embedURL = self.ampReplace(elem.text.strip())
00480                     continue
00481                 if elem.tag.endswith('thumbnail'):
00482                     if elem.get('url'):
00483                         item['blip_picture'] = self.ampReplace(elem.get('url').strip())
00484                     continue
00485                 if elem.tag.endswith('group'):
00486                     file_size = 0
00487                     for e in elem:
00488                         if e.tag.endswith('content'):
00489                             if e.get('fileSize'):
00490                                 try:
00491                                     if int(e.get('fileSize')) > file_size:
00492                                         item['video'] = self.ampReplace(e.get('url').strip())
00493                                         file_size = int(e.get('fileSize'))
00494                                 except:
00495                                     pass
00496                             continue
00497                     continue
00498                 if elem.tag.endswith('runtime'):
00499                     if elem.text:
00500                         item['blip_runtime'] = self.massageDescription(elem.text.strip())
00501                     continue
00502                 if elem.tag.endswith('rating'):
00503                     if elem.text:
00504                         item['blip_rating'] = self.massageDescription(elem.text.strip())
00505                     continue
00506             if not item.has_key('video') and not item.has_key('link') and not embedURL:
00507                 continue
00508             if embedURL:
00509                 item['link'] = self.processVideoUrl(embedURL)
00510             if item.has_key('link') and not item.has_key('video'):
00511                 continue
00512             if item.has_key('video') and not item.has_key('link'):
00513                 item['link'] = item['video']
00514             elements_final.append(item)
00515 
00516         if not len(elements_final):
00517             raise BliptvVideoNotFound(u"2-No blip.tv Video matches found for search value (%s)" % title)
00518 
00519         return elements_final
00520         # end searchTitle()
00521 
00522 
00523     def searchForVideos(self, title, pagenumber):
00524         """Common name for a video search. Used to interface with MythTV plugin NetVision
00525         """
00526         try:
00527             data = self.searchTitle(title, pagenumber, self.page_limit)
00528         except BliptvVideoNotFound, msg:
00529             sys.stderr.write(u"%s\n" % msg)
00530             return None
00531         except BliptvUrlError, msg:
00532             sys.stderr.write(u'%s' % msg)
00533             sys.exit(1)
00534         except BliptvHttpError, msg:
00535             sys.stderr.write(self.error_messages['BliptvHttpError'] % msg)
00536             sys.exit(1)
00537         except BliptvRssError, msg:
00538             sys.stderr.write(self.error_messages['BliptvRssError'] % msg)
00539             sys.exit(1)
00540         except Exception, e:
00541             sys.stderr.write(u"! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e))
00542             sys.exit(1)
00543 
00544         if data == None:
00545             return None
00546         if not len(data):
00547             return None
00548 
00549         items = []
00550         for match in data:
00551             item_data = {}
00552             for key in self.key_translation[1].keys():
00553                 if key in match.keys():
00554                     item_data[self.key_translation[1][key]] = match[key]
00555                 else:
00556                     item_data[self.key_translation[1][key]] = u''
00557             items.append(item_data)
00558 
00559         # Channel details and search results
00560         channel = {'channel_title': u'blip.tv', 'channel_link': u'http://blip.tv', 'channel_description': u"We're the next generation television network", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0}
00561 
00562         if len(items) == self.page_limit:
00563             channel['channel_numresults'] = self.page_limit * int(pagenumber) + 1
00564         else:
00565             channel['channel_numresults'] = self.page_limit * int(pagenumber)
00566         channel['channel_startindex'] = self.page_limit * int(pagenumber)
00567         channel['channel_returned'] = len(items)
00568 
00569         if len(items):
00570             return [[channel, items]]
00571         return None
00572     # end searchForVideos()
00573 
00574 
00575     def getCategories(self):
00576         '''Get the list of valid category ids and their name and update the proper dictionaries
00577         return nothing
00578         '''
00579         url = self.config[u'urls'][u'categories']
00580         if self.config['debug_enabled']:
00581             print "Category list URL:"
00582             print url
00583             print
00584 
00585         try:
00586             etree = XmlHandler(url).getEt()
00587         except Exception, errormsg:
00588             sys.stderr.write(self.error_messages['BliptvUrlError'] % (url, errormsg))
00589             self.tree_order.remove('categories')
00590             return
00591 
00592         if etree is None:
00593             sys.stderr.write(u'1-No Categories found at (%s)\n' % url)
00594             self.tree_order.remove('categories')
00595             return
00596 
00597         if not etree.find('payload'):
00598             sys.stderr.write(u'2-No Categories found at (%s)\n' % url)
00599             self.tree_order.remove('categories')
00600             return
00601 
00602         category = False
00603         for element in etree.find('payload'):
00604             if element.tag == 'category':
00605                 tmp_name = u''
00606                 tmp_id = u''
00607                 for e in element:
00608                     if e.tag == 'id':
00609                         if e.text == '-1':
00610                             break
00611                         if e.text:
00612                             tmp_id = self.massageDescription(e.text.strip())
00613                     if e.tag == 'name':
00614                         if e.text:
00615                             tmp_name = self.massageDescription(e.text.strip())
00616                 if tmp_id and tmp_name:
00617                     category = True
00618                     self.tree_org['categories'].append([tmp_name, ['popular', 'recent', 'random', 'featured',]])
00619                     self.feed_names['categories'][tmp_name] = tmp_id
00620 
00621         if not category:
00622             sys.stderr.write(u'3-No Categories found at (%s)\n' % url)
00623             self.tree_order.remove('categories')
00624             return
00625 
00626         self.tree_org['categories'].append([u'', u'']) # Adds a end of the Categories directory indicator
00627 
00628         return
00629     # end getCategories()
00630 
00631     def displayTreeView(self):
00632         '''Gather the categories/feeds/...etc then retrieve a max page of videos meta data in each of them
00633         return array of directories and their video meta data
00634         '''
00635         # Channel details and search results
00636         self.channel = {'channel_title': u'blip.tv', 'channel_link': u'http://blip.tv', 'channel_description': u"We're the next generation television network", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0}
00637 
00638         if self.config['debug_enabled']:
00639             print self.config[u'urls']
00640             print
00641 
00642         # Get category ids
00643         self.getCategories()
00644 
00645         # Process the various video feeds/categories/... etc
00646         self.treeview = True
00647         dictionaries = []
00648         for key in self.tree_order:
00649             if key == 'categories':
00650                 self.categories = True
00651             else:
00652                 self.categories = False
00653             self.tree_key = key
00654             dictionaries = self.getVideos(self.tree_org[key], dictionaries)
00655 
00656         return [[self.channel, dictionaries]]
00657     # end displayTreeView()
00658 
00659     def makeURL(self, URL):
00660         '''Form a URL to search for videos
00661         return a URL
00662         '''
00663         additions = dict(self.tree_customize[self.tree_key]['__default__']) # Set defaults
00664 
00665         # Add customizations
00666         if self.feed in self.tree_customize[self.tree_key].keys():
00667             for element in self.tree_customize[self.tree_key][self.feed].keys():
00668                 additions[element] = self.tree_customize[self.tree_key][self.feed][element]
00669 
00670         # Make the search extension string that is added to the URL
00671         addition = u''
00672         for ky in additions.keys():
00673             if ky.startswith('add_'):
00674                 addition+=u'/%s' %  additions[ky]
00675             else:
00676                 addition+=u'?%s=%s' %  (ky, additions[ky])
00677         index = URL.find('%')
00678         if index == -1:
00679             return (URL+addition)
00680         else:
00681             return (URL+addition) % self.feed
00682     # end makeURL()
00683 
00684 
00685     def getVideos(self, dir_dict, dictionaries):
00686         '''Parse a list made of categories and retrieve video meta data
00687         return a dictionary of directory names and categories video meta data
00688         '''
00689         for sets in dir_dict:
00690             if not isinstance(sets[1], list):
00691                 if sets[0] != '': # Add the nested dictionaries display name
00692                     dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
00693                 else:
00694                     dictionaries.append(['', u'']) # Add the nested dictionary indicator
00695                 continue
00696             temp_dictionary = []
00697             for self.feed in sets[1]:
00698                 if self.categories:
00699                     self.tree_customize[self.tree_key]['__default__']['categories_id'] = self.feed_names['categories'][sets[0]]
00700                     self.tree_customize[self.tree_key]['__default__']['sort'] = self.feed
00701                 if self.config[u'urls'][u'tree.view'][self.tree_key].has_key('__all__'):
00702                     URL = self.config[u'urls'][u'tree.view'][self.tree_key]['__all__']
00703                 else:
00704                     URL = self.config[u'urls'][u'tree.view'][self.tree_key][self.feed]
00705                 temp_dictionary = self.config['item_parser'][URL[1]](self.makeURL(URL[0]), temp_dictionary)
00706             if len(temp_dictionary):
00707                 if len(sets[0]): # Add the nested dictionaries display name
00708                     dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
00709                 for element in temp_dictionary:
00710                     dictionaries.append(element)
00711                 if len(sets[0]):
00712                     dictionaries.append(['', u'']) # Add the nested dictionary indicator
00713         return dictionaries
00714     # end getVideos()
00715 
00716     def getVideosForURL(self, url, dictionaries):
00717         '''Get the video meta data for url search
00718         return the video dictionary of directories and their video mata data
00719         '''
00720         initial_length = len(dictionaries)
00721 
00722         if self.config['debug_enabled']:
00723             print "Video URL:"
00724             print url
00725             print
00726 
00727         try:
00728             etree = XmlHandler(url).getEt()
00729         except Exception, errormsg:
00730             sys.stderr.write(self.error_messages['BliptvUrlError'] % (url, errormsg))
00731             return dictionaries
00732 
00733         if etree is None:
00734             sys.stderr.write(u'1-No Videos for (%s)\n' % self.feed)
00735             return dictionaries
00736 
00737         dictionary_first = False
00738         self.next_page = False
00739         language = self.config['language']
00740         for elements in etree.find('channel'):
00741             if elements.tag.endswith(u'language'):
00742                 if elements.text:
00743                     language = elements.text[:2]
00744                 continue
00745 
00746             if not elements.tag.endswith(u'item'):
00747                 continue
00748 
00749             item = {}
00750             item['language'] = language
00751             embedURL = u''
00752             for elem in elements:
00753                 if elem.tag == 'title':
00754                     if elem.text:
00755                         item['title'] = self.massageDescription(elem.text.strip())
00756                     continue
00757                 if elem.tag.endswith('safeusername'):
00758                     if elem.text:
00759                         item['blip_safeusername'] = self.massageDescription(elem.text.strip())
00760                     continue
00761                 if elem.tag.endswith('pubDate'):
00762                     if elem.text:
00763                         item['updated'] = self.massageDescription(elem.text.strip())
00764                     continue
00765                 if elem.tag.endswith('puredescription'):
00766                     if elem.text:
00767                         item['blip_puredescription'] = self.massageDescription(elem.text.strip())
00768                     continue
00769                 if elem.tag == 'link':
00770                     if elem.text:
00771                         item['link'] = self.ampReplace(elem.text.strip())
00772                     continue
00773                 if elem.tag.endswith('embedUrl'):
00774                     if elem.text:
00775                         embedURL = self.ampReplace(elem.text.strip())
00776                     continue
00777                 if elem.tag.endswith('thumbnail'):
00778                     if elem.get('url'):
00779                         item['blip_picture'] = self.ampReplace(elem.get('url').strip())
00780                     continue
00781                 if elem.tag.endswith('group'):
00782                     file_size = 0
00783                     for e in elem:
00784                         if e.tag.endswith('content'):
00785                             for key in e.keys():
00786                                 if key.endswith('vcodec'):
00787                                     break
00788                             else:
00789                                 continue
00790                             if e.get('fileSize'):
00791                                 try:
00792                                     if int(e.get('fileSize')) > file_size:
00793                                         item['video'] = self.ampReplace(e.get('url').strip())
00794                                         file_size = int(e.get('fileSize'))
00795                                 except:
00796                                     pass
00797                                 if e.get('height'):
00798                                     item['height'] = e.get('height').strip()
00799                                 if e.get('width'):
00800                                     item['width'] = e.get('width').strip()
00801                             continue
00802                     continue
00803                 if elem.tag.endswith('runtime'):
00804                     if elem.text:
00805                         item['blip_runtime'] = self.massageDescription(elem.text.strip())
00806                     continue
00807                 if elem.tag.endswith('rating'):
00808                     if elem.text:
00809                         item['blip_rating'] = self.massageDescription(elem.text.strip())
00810                     continue
00811             if not item.has_key('video') and not item.has_key('link'):
00812                 continue
00813             if embedURL:
00814                 item['link'] = embedURL
00815             if item.has_key('link') and not item.has_key('video'):
00816                 continue
00817             if item.has_key('video') and not item.has_key('link'):
00818                 item['link'] = item['video']
00819 
00820             if self.treeview:
00821                 if not dictionary_first:  # Add the dictionaries display name
00822                     dictionaries.append([self.massageDescription(self.feed_names[self.tree_key][self.feed]), self.setTreeViewIcon()])
00823                     dictionary_first = True
00824 
00825             final_item = {}
00826             for key in self.key_translation[1].keys():
00827                 if not item.has_key(key):
00828                     final_item[self.key_translation[1][key]] = u''
00829                 else:
00830                     final_item[self.key_translation[1][key]] = item[key]
00831             dictionaries.append(final_item)
00832 
00833         if self.treeview:
00834             if initial_length < len(dictionaries): # Need to check if there was any items for this Category
00835                 dictionaries.append(['', u'']) # Add the nested dictionary indicator
00836         return dictionaries
00837     # end getVideosForURL()
00838 # end Videos() class
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends